1.5.0 release

git-svn-id: http://svn.codehaus.org/groovy/tags/GROOVY_1_5_0@9686 a5544e8c-8a19-0410-ba12-f9af4593a198
diff --git a/groovy/.classpath b/groovy/.classpath
new file mode 100644
index 0000000..389ec0f
--- /dev/null
+++ b/groovy/.classpath
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+	<classpathentry kind="src" path="src/main"/>
+	<classpathentry kind="src" output="target/test-classes" path="src/test"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.4"/>
+	<classpathentry kind="var" path="M2_REPO/org/apache/ant/ant/1.7.0/ant-1.7.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/javax/servlet/jsp-api/2.0/jsp-api-2.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/asm/asm-tree/2.2/asm-tree-2.2.jar"/>
+	<classpathentry kind="var" path="M2_REPO/javax/servlet/servlet-api/2.4/servlet-api-2.4.jar" sourcepath="M2_REPO/javax/servlet/servlet-api/2.4/servlet-api-2.4-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/bsf/bsf/2.4.0/bsf-2.4.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/mx4j/mx4j/3.0.2/mx4j-3.0.2.jar"/>
+	<classpathentry kind="var" path="M2_REPO/antlr/antlr/2.7.6/antlr-2.7.6.jar"/>
+	<classpathentry kind="var" path="M2_REPO/jmock/jmock-cglib/1.2.0/jmock-cglib-1.2.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/asm/asm-attrs/2.2/asm-attrs-2.2.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/apache/ant/ant-junit/1.7.0/ant-junit-1.7.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/junit/junit/3.8.2/junit-3.8.2.jar" sourcepath="M2_REPO/junit/junit/3.8.2/junit-3.8.2-sources.jar"/>
+	<classpathentry kind="var" path="M2_REPO/commons-cli/commons-cli/1.0/commons-cli-1.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/codehaus/castor/castor/1.1.2.1/castor-1.1.2.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/hsqldb/hsqldb/1.8.0.7/hsqldb-1.8.0.7.jar"/>
+	<classpathentry kind="var" path="M2_REPO/xmlunit/xmlunit/1.1/xmlunit-1.1.jar"/>
+	<classpathentry kind="var" path="M2_REPO/com/thoughtworks/xstream/xstream/1.2.2/xstream-1.2.2.jar"/>
+	<classpathentry kind="var" path="M2_REPO/asm/asm-analysis/2.2/asm-analysis-2.2.jar"/>
+	<classpathentry kind="var" path="M2_REPO/cglib/cglib-nodep/2.1_3/cglib-nodep-2.1_3.jar"/>
+	<classpathentry kind="var" path="M2_REPO/jmock/jmock/1.2.0/jmock-1.2.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/mockobjects/mockobjects-core/0.09/mockobjects-core-0.09.jar"/>
+	<classpathentry kind="var" path="M2_REPO/openejb/openejb-loader/1.0/openejb-loader-1.0.jar"/>
+	<classpathentry kind="var" path="M2_REPO/xpp3/xpp3_min/1.1.3.4.O/xpp3_min-1.1.3.4.O.jar"/>
+	<classpathentry kind="var" path="M2_REPO/asm/asm/2.2/asm-2.2.jar"/>
+	<classpathentry kind="var" path="M2_REPO/asm/asm-util/2.2/asm-util-2.2.jar"/>
+	<classpathentry kind="var" path="M2_REPO/org/apache/ant/ant-launcher/1.7.0/ant-launcher-1.7.0.jar"/>
+	<classpathentry kind="lib" path="security/GroovyJarTest.jar"/>
+	<classpathentry kind="var" path="M2_REPO/commons-logging/commons-logging/1.0.4/commons-logging-1.0.4.jar"/>
+	<classpathentry kind="var" path="M2_REPO/log4j/log4j/1.2.8/log4j-1.2.8.jar"/>
+	<classpathentry kind="var" path="M2_REPO/jline/jline/0.9.91/jline-0.9.91.jar"/>
+	<classpathentry kind="output" path="target/classes"/>
+</classpath>
diff --git a/groovy/.externalToolBuilders/Groovy ensureGrammars.launch b/groovy/.externalToolBuilders/Groovy ensureGrammars.launch
new file mode 100644
index 0000000..0c5dae7
--- /dev/null
+++ b/groovy/.externalToolBuilders/Groovy ensureGrammars.launch
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>

+<launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType">

+<stringAttribute key="org.eclipse.ant.ui.ATTR_ANT_AFTER_CLEAN_TARGETS" value="ensureGrammars,"/>

+<stringAttribute key="org.eclipse.ant.ui.ATTR_ANT_MANUAL_TARGETS" value="ensureGrammars,"/>

+<booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED" value="true"/>

+<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/>

+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">

+<listEntry value="/groovy/build.xml"/>

+</listAttribute>

+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">

+<listEntry value="1"/>

+</listAttribute>

+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>

+<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>

+<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>

+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="true"/>

+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="groovy"/>

+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${workspace_loc:/groovy/build.xml}"/>

+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,"/>

+<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>

+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_WORKING_DIRECTORY" value="${project_loc:/groovy}"/>

+</launchConfiguration>

diff --git a/groovy/.project b/groovy/.project
new file mode 100644
index 0000000..4aff9db
--- /dev/null
+++ b/groovy/.project
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>groovy</name>
+	<comment>Groovy: A powerful, dynamic language for the JVM</comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
+			<triggers>full,incremental,</triggers>
+			<arguments>
+				<dictionary>
+					<key>LaunchConfigHandle</key>
+					<value>&lt;project&gt;/.externalToolBuilders/Groovy ensureGrammars.launch</value>
+				</dictionary>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.jdt.core.javabuilder</name>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.jdt.core.javanature</nature>
+	</natures>
+</projectDescription>
diff --git a/groovy/.settings/org.eclipse.core.resources.prefs b/groovy/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..7785db0
--- /dev/null
+++ b/groovy/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Jun 06 17:51:41 BST 2006
+eclipse.preferences.version=1
+encoding//src/test/groovy/Base64Test.groovy=UTF-8
diff --git a/groovy/.settings/org.eclipse.jdt.core.prefs b/groovy/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..1888734
--- /dev/null
+++ b/groovy/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,57 @@
+#Sat Jun 11 03:45:36 CEST 2005
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.4
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.4
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
+org.eclipse.jdt.core.compiler.problem.deprecation=warning
+org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
+org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
+org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
+org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
+org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
+org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
+org.eclipse.jdt.core.compiler.problem.forbiddenReference=error
+org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
+org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
+org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
+org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore
+org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
+org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
+org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore
+org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
+org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
+org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
+org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
+org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore
+org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
+org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=ignore
+org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
+org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
+org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
+org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
+org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
+org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
+org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
+org.eclipse.jdt.core.compiler.problem.unusedImport=warning
+org.eclipse.jdt.core.compiler.problem.unusedLocal=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
+org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
+org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=ignore
+org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
+org.eclipse.jdt.core.compiler.source=1.4
diff --git a/groovy/ASM-LICENSE.txt b/groovy/ASM-LICENSE.txt
new file mode 100644
index 0000000..5f4b794
--- /dev/null
+++ b/groovy/ASM-LICENSE.txt
@@ -0,0 +1,31 @@
+/***
+ * http://asm.objectweb.org/
+ *
+ * ASM: a very small and fast Java bytecode manipulation framework
+ * Copyright (c) 2000,2002,2003 INRIA, France Telecom
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holders nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
diff --git a/groovy/LICENSE.txt b/groovy/LICENSE.txt
new file mode 100644
index 0000000..e0908d4
--- /dev/null
+++ b/groovy/LICENSE.txt
@@ -0,0 +1,15 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+ 
\ No newline at end of file
diff --git a/groovy/NOTICE.txt b/groovy/NOTICE.txt
new file mode 100644
index 0000000..c848b96
--- /dev/null
+++ b/groovy/NOTICE.txt
@@ -0,0 +1,12 @@
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Groovy Language distribution.                 ==
+   =========================================================================
+
+   Groovy Language
+   Copyright 2003-2007 The respective authors and developers
+   Developers and Contributors are listed in the project POM file
+
+   This product includes software developed by
+   The Groovy community (http://groovy.codehaus.org/).
diff --git a/groovy/README.txt b/groovy/README.txt
new file mode 100644
index 0000000..d60f3be
--- /dev/null
+++ b/groovy/README.txt
@@ -0,0 +1,22 @@
+$Id$
+
+Building
+========
+
+To build you will need:
+
+ * JDK 1.4+ (J2SE 1.4.0+) (http://java.sun.com/j2se/1.4.2)
+ * Apache Ant 1.7+ (http://ant.apache.org)
+
+For detailed instructions please see:
+
+    http://groovy.codehaus.org/Building+Groovy+from+Source
+
+To build everything, run tests and create a complete installation:
+
+    ant install
+
+To build without running tests:
+
+    ant install -DskipTests=true
+
diff --git a/groovy/TODO.txt b/groovy/TODO.txt
new file mode 100644
index 0000000..027ef13
--- /dev/null
+++ b/groovy/TODO.txt
@@ -0,0 +1,208 @@
+!!!!!! Obsolete file!! Don't add anything here, use Jira instead.
+
+TODO: remove irrelevant entries, add others to Jira.
+
+Things to Test
+==============
+
+* test synchronized statement
+
+Core Groovy Tasks
+=================
+
+* new GroovyMethods	to be added
+	- File.grep(pattern) -> List
+	- File.grep(pattern) { closure } 
+		allow iteration through a file for each line matching a regex pattern
+		
+	- List.first(), List.last(), pop()
+	- Collection.removeIf { x | x > 0 }
+	- Collection.count(foo) -> returns number of objects that equal foo
+
+	- Map.get(key, defaultValue)
+	- Map.setDefault(key, defaultValue) for things like
+  		 map.setDefault(key, []).add(newValue)
+	
+	- Object.eachProperty
+	- Object.eachPropertyName { object.getProperty(it) }
+	- Object.setProperties(foo:'abc', x:123)
+	
+	- some kind of Regexp iterator of strings like Ruby's scan
+	- maybe support Pythons' zip and reduce functions?
+	
+		maybe add the various readonly List APIs to Object[] and *[] types
+		if we ever support the DTD / Xed style type*, type+ then we can do the same
+		there too
+
+* SQL builder
+
+sql.table("users") {
+ÊÊÊ column('user-id') { autoinc(), primaryKey(), references('foo'), initialValue(1234) }
+ÊÊÊ column('nickname') { varchar(200) }
+	column(name:'foo', type:'varchar(200)', references:['a', 'b'], initialValue:1234)
+  }
+}
+
+* using mixin for adding using style behaviour to any object??
+
+	mixin Using {
+	
+		static using(yield) {
+			object = this.newInstance()
+			try {
+				yield(object)
+				object.close()
+			}
+			catch (Exception e) {
+				try {
+					object.close()
+				}
+				catch (Exception e2)
+					// ignore e2
+					throw e
+				}
+			}
+		}
+		
+		or
+		
+		using(yield) {
+			try {
+				yield(this)
+				close()
+			}
+			catch (Exception e) {
+				try {
+					close()
+				}
+				catch (Exception e2)
+					// ignore e2
+					throw e
+				}
+			}
+		}
+	}
+	
+	then use it as 
+	
+		new FileInputStream().using { in |
+			...
+		}
+
+* looks like a bug on generated methods, should use param name over any field name
+	- also it looks like there's various unnecessary stuff (creation of tuples) when invoking
+	methods
+	
+* test identity -> hashCode + equals methods
+
+* support for property converters, based on type
+
+* to support dynamic mixins we should use dynamic proxies to allow
+	a groovy object to change metaclass at runtime
+
+* groovy dynamic proxy of any Java object in Java-land?
+	NullObject pattern
+	
+* immutable bean
+
+* support static newInstance() method for constructors
+
+* maybe split up ClassGenerator - do code gen & class gen separately
+
+* mixin support...
+
+	SomeClass.addMixin(Foo);
+	
+	MetaClass.addInterceptor( new Interceptor() {
+		filter(method) {
+			return method.isPublic();
+		}
+		invoke(method, args) {
+			// do something
+			method.invoke(args);
+		}
+	});
+
+	* allow meta classes to be added dynamically using closure syntax?
+	e.g. Map?
+	
+
+STUFF TO PONDER
+===============
+
+* Support multiple return values...
+
+	String, Number cheese() {
+		"hello", 7
+	}
+	
+	a, b = cheese()
+	
+	also if we do this we should do assignment / swapping
+	
+		a, b = 1, 2
+		a, b = b, a
+
+* using macros to avoid dependencies on logging stuff (say)
+
+	class GroovyLog {
+		switch (System.getProperty('groovy.log.impl', 'useCommonsLogging')) {
+			case 'useCommonsLogging': {
+				// lets define the new instance method
+				newInstance() {
+					return new CommonsLoggingThingy()
+				}
+			}
+			default {
+				newInstance() {
+					return new SimpleGroovyLog()
+				}
+			}
+		}
+	}
+	
+	doing things like this at compile time means no runtime dependencies. Ditto to do JDK based compiles
+	
+UI WORK
+=======
+
+* tree demo...
+
+* when named method calls are supported with default values, refactor SwingBuilder
+	so that all the creations of widgets occur with SwingFactory which would be 
+	useful in and of itself
+	- plus we should be using normal method call mechanism & for groovy to do the rest to avoid
+	the long laborious Map coding
+	
+* FormModel.addPropertyModel(property)
+	FormModel.addClosureModel(readClosure, writeClosure)
+
+* ListModel is-a List but delegates to an underlying list and has events
+
+* rename tableLayout -> table and table -> grid
+
+* add sortableGrid
+
+* create a GroovyUI
+	-> interactive script + allows scripts to be run & objects explored
+
+
+JUNIT WORK
+==========
+
+* patch GroovyTestCase so that methods which return Object are included in the test. This avoids us having to
+specify void for method return types.This requires a clever static method when we generate the
+	bytecode which can instantiate a special kind of TestSuite
+	unless there's another way?
+
+
+OPTIMISATIONS
+=============
+* method invocations - if foo instanceof GroovyObject
+then generate bytecode
+
+foo.invokeMethod(method, args);
+
+* could code generate the MetaClass with very efficient dynamic dispatch
+	e.g. could switch() on the method name & then use real method invocation
+	on the method instance
diff --git a/groovy/bootstrap/maven-ant-tasks-2.0.7.jar b/groovy/bootstrap/maven-ant-tasks-2.0.7.jar
new file mode 100644
index 0000000..4f0dfdd
--- /dev/null
+++ b/groovy/bootstrap/maven-ant-tasks-2.0.7.jar
Binary files differ
diff --git a/groovy/build.properties b/groovy/build.properties
new file mode 100644
index 0000000..6d69ecb
--- /dev/null
+++ b/groovy/build.properties
@@ -0,0 +1,13 @@
+groovyVersion = 1.5.0
+
+# uncomment the following line to enable java5 elements in the build
+# groovy.build.vm5=true
+
+#  Many people have reported problems testing UberTestCaseGroovySourceSubPackages, others have no difficulties with the default
+#  values ant junit task uses.  The decision has been taken to provide the values to try and cause the least
+#  hassle to the most people.
+
+groovyJUnit_ms = 256m
+groovyJUnit_mx = 256m
+groovyJUnit_permSize = 64m
+groovyJUnit_maxPermSize=128m
diff --git a/groovy/build.xml b/groovy/build.xml
new file mode 100644
index 0000000..cdb0ee6
--- /dev/null
+++ b/groovy/build.xml
@@ -0,0 +1,743 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+Ant build script for Groovy.
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed under the License is
+distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied. See the License for the specific language governing permissions and limitations under the License.
+
+This work is copyright by the author(s) and is part of a greater work collectively copyright by Codehaus on
+behalf of the Groovy community. See the NOTICE.txt file distributed with this work for additional information.
+
+Author : Russel Winder
+Author : Paul King
+$Revision$ ($LastChangedBy$)
+$Date$
+-->
+
+<project name="Groovy" default="createJars" basedir=".">
+
+    <property file="local.build.properties"/>
+    <property file="build.properties"/>
+    <property name="ant.requiredVersion" value="1.6.5"/>
+
+    <!-- config/ant/build-maven.xml imports config/ant/build-setup.xml where all the paths are defined. -->
+    <import file="config/ant/build-maven.xml"/>
+    <import file="config/ant/build-checkstyle.xml"/>
+    <import file="config/ant/build-cobertura.xml"/>
+
+    <condition property="_skipTests_">
+        <or>
+            <equals arg1="${test}" arg2="false"/>
+            <istrue value="${skipTests}"/>
+        </or>
+    </condition>
+
+    <condition property="_skipExamples_">
+        <istrue value="${skipExamples}"/>
+    </condition>
+
+    <condition property="groovy.build.vm5">
+        <not>
+            <contains string="${ant.java.version}" substring="1.4"/>
+        </not>
+    </condition>
+
+    <target name="-initCoverage">
+        <condition property="_forceCoverage_">
+            <and>
+                <not>
+                    <istrue value="${skipTests}"/>
+                </not>
+                <istrue value="${forceCoverage}"/>
+            </and>
+        </condition>
+    </target>
+
+    <target name="ensureGrammars" description="Ensure all the Antlr generated files are up to date.">
+        <mkdir dir="${groovyParserDirectory}"/>
+        <antlr target="${antlrDirectory}/groovy.g" outputdirectory="${groovyParserDirectory}">
+            <classpath refid="compilePath"/>
+        </antlr>
+        <antlr target="${javaParserDirectory}/java.g" outputdirectory="${javaParserDirectory}">
+            <classpath refid="compilePath"/>
+        </antlr>
+    </target>
+
+    <target name="-init" depends="-fetchDependencies">
+        <echo message="Java Runtime Environment version: ${java.version}"/>
+        <echo message="Java Runtime Environment vendor: ${java.vendor}"/>
+        <echo message="Ant version: ${ant.version}"/>
+        <echo message="Operating system name: ${os.name}"/>
+        <echo message="Operating system architecture: ${os.arch}"/>
+        <echo message="Operating system version: ${os.version}"/>
+    </target>
+
+    <target name="-jvm14BuildWarning" unless="groovy.build.vm5">
+        <echo>
+===================================================================
+  WARNING: You are building Groovy with a 1.4 JDK - the resulting
+           jar will be missing some Java 1.5+ specific features.
+===================================================================
+        </echo>
+    </target>
+
+    <!-- will go away after 1.1 final -->
+    <target name="-jvm15BuildWarning" if="groovy.build.vm5">
+        <echo>
+===================================================================
+  WARNING: You are building Groovy with a 1.5+ JDK - please check
+           that you are not using any 1.5+ classes directly in a
+           way that would break the 1.4 build.
+===================================================================
+        </echo>
+    </target>
+
+    <target name="-checkAntVersion" depends="-excludeLegacyAntVersion"
+            description="Check that we are running on the required version of Ant."/>
+
+    <target name="-excludeLegacyAntVersion">
+        <!-- antversion didn't exist in early versions of ant so we have
+             a legacy check to provide a nicer error message in this case -->
+        <fail message="You are using ant ${ant.version}, please build using ant ${ant.requiredVersion}+">
+            <condition>
+                <or>
+                    <contains string="${ant.version}" substring="1.1"></contains>
+                    <contains string="${ant.version}" substring="1.2"></contains>
+                    <contains string="${ant.version}" substring="1.3"></contains>
+                    <contains string="${ant.version}" substring="1.4"></contains>
+                    <contains string="${ant.version}" substring="1.5"></contains>
+                    <and>
+                        <contains string="${ant.version}" substring="1.6"></contains>
+                        <not>
+                            <contains string="${ant.version}" substring="${ant.requiredVersion}"></contains>
+                        </not>
+                    </and>
+                </or>
+            </condition>
+        </fail>
+    </target>
+
+    <!-- add back in if we make 1.7+ minimal required version for build again
+        <target name="-ensureRequiredAntVersion">
+           <fail message="You are using ant ${ant.version}, please install using ant ${ant.requiredVersion}+"/>
+               <condition><not><antversion atleast="${ant.requiredVersion}"/></not></condition>
+           </fail>
+        </target>
+    -->
+
+    <target name="compileMain" depends="ubercompile,stagedcompile"
+            description="Compile the Java and Groovy code in the main source.">
+    </target>
+
+    <target name="stagedcompile" depends="-init,-includeResources,ensureGrammars,-jvm14BuildWarning,-jvm15BuildWarning" unless="uber">
+        <mkdir dir="${mainClassesDirectory}"/>
+        <mkdir dir="${toolsClassesDirectory}"/>
+        <javac srcdir="${mainSourceDirectory}" includeantruntime="false" destdir="${mainClassesDirectory}"
+               deprecation="on" debug="yes" source="1.4" target="1.4" fork="true" classpathref="compilePath">
+               <exclude name="**/vmplugin/v5/**" unless="groovy.build.vm5"/>
+        </javac>
+
+        <echo message="Groovyc of main code."/>
+        <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpath="${mainClassesDirectory}"
+                 classpathref="runtimePath"/>
+        <groovyc srcdir="${mainSourceDirectory}" destdir="${mainClassesDirectory}" classpathref="compilePath"/>
+    </target>
+
+    <target name="ubercompile:bootstrap" depends="-init,-includeResources,ensureGrammars" if="uber">
+        <mkdir dir="${mainClassesDirectory}"/>
+
+        <javac destdir="${mainClassesDirectory}"
+               deprecation="on"
+               debug="yes"
+               source="1.4"
+               target="1.4"
+               fork="true"
+               includeantruntime="false">
+            <classpath refid="compilePath"/>
+            <src location="${mainSourceDirectory}"/>
+
+            <!--
+            NOTE: This list includes *most* of the classes which are required for the ubercompile
+                  bits to work, adding them here to allow for more incremental compilation when
+                  required support classes change.
+            -->
+
+            <include name="groovy/lang/**/*.java"/>
+            <include name="org/codehaus/groovy/ant/**/*.java"/>
+            <include name="org/codehaus/groovy/antlr/**/*.java"/>
+            <include name="org/codehaus/groovy/ast/**/*.java"/>
+            <include name="org/codehaus/groovy/classgen/**/*.java"/>
+            <include name="org/codehaus/groovy/control/**/*.java"/>
+            <include name="org/codehaus/groovy/reflection/**/*.java"/>
+            <include name="org/codehaus/groovy/runtime/**/*.java"/>
+            <include name="org/codehaus/groovy/syntax/**/*.java"/>
+            <include name="org/codehaus/groovy/tools/javac/**/*.java"/>
+        </javac>
+
+        <taskdef name="ubercompile" classname="org.codehaus.groovy.ant.UberCompileTask">
+            <classpath>
+                <pathelement location="${mainClassesDirectory}"/>
+                <path refid="compilePath"/>
+            </classpath>
+        </taskdef>
+    </target>
+
+    <target name="ubercompile" depends="ubercompile:bootstrap" if="uber">
+        <mkdir dir="${mainStubsDirectory}"/>
+
+        <ubercompile destdir="${mainClassesDirectory}">
+            <classpath>
+                <pathelement location="${mainClassesDirectory}"/>
+                <path refid="compilePath"/>
+            </classpath>
+            <src location="${mainSourceDirectory}"/>
+
+            <generatestubs destdir="${mainStubsDirectory}" includes="**/*.groovy">
+                <configuration targetBytecode="1.4"/>
+            </generatestubs>
+
+            <javac includeantruntime="false" deprecation="on" debug="yes" source="1.4" target="1.4" fork="true"/>
+            <groovyc includes="**/*.groovy">
+                <configuration debug="true" verbose="true"/>
+            </groovyc>
+        </ubercompile>
+    </target>
+
+    <target name="compileTest" depends="compileMain" unless="_skipTests_"
+            description="Compile the Java and Groovy code in the test source.">
+        <mkdir dir="${testClassesDirectory}"/>
+        <antforked target="realCompileTest"/>
+    </target>
+
+    <target name="realCompileTest">
+        <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpath="${mainClassesDirectory}"
+                 classpathref="testPath"/>
+        <!-- compile Groovy test files excluding ones which we want to pick up from the file system -->
+        <groovyc srcdir="${testSourceDirectory}"
+                 destdir="${testClassesDirectory}"
+                 excludes="org/codehaus/groovy/ant/**/GroovyTest*.groovy,gls/**/vm5/*Test.groovy" >
+            <classpath>
+                <pathelement path="${mainClassesDirectory}"/>
+                <pathelement path="${testClassesDirectory}"/>
+                <path refid="testPath"/>
+            </classpath>
+            <javac source="1.4" target="1.4" fork="true" memorymaximumsize="64m"/>
+        </groovyc>
+        <antcall target="realCompileTest_vm5"/>
+    </target>
+
+    <target name="realCompileTest_vm5" if="groovy.build.vm5">
+        <groovyc srcdir="${testSourceDirectory}"
+                 destdir="${testClassesDirectory}"
+                 includes="gls/**/vm5/*Test.groovy" >
+            <classpath>
+                <pathelement path="${mainClassesDirectory}"/>
+                <pathelement path="${testClassesDirectory}"/>
+                <path refid="testPath"/>
+            </classpath>
+            <javac fork="true" memorymaximumsize="64m"/>
+        </groovyc>
+
+    </target>
+
+    <target name="compileExamples" depends="compileMain" unless="_skipExamples_"
+            description="Compile the Java and Groovy code in the examples source directory.">
+        <mkdir dir="${examplesClassesDirectory}"/>
+        <echo message="Compiling example code."/>
+        <antforked target="realCompileExamples"/>
+    </target>
+
+    <target name="realCompileExamples">
+        <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc" classpath="${mainClassesDirectory}"/>
+        <groovyc srcdir="${examplesSourceDirectory}/webapps/groovlet-examples/WEB-INF/groovy"
+                 destdir="${examplesClassesDirectory}">
+            <classpath>
+                <pathelement path="${mainClassesDirectory}"/>
+                <path refid="compilePath"/>
+                <path refid="examplesPath"/>
+            </classpath>
+        </groovyc>
+        <groovyc srcdir="${examplesSourceDirectory}"
+                destdir="${examplesClassesDirectory}"
+                excludes="webapps/groovlet-examples/WEB-INF/groovy/**">
+            <classpath>
+                <pathelement path="${mainClassesDirectory}"/>
+                <pathelement path="${examplesClassesDirectory}"/>
+                <path refid="compilePath"/>
+                <path refid="examplesPath"/>
+            </classpath>
+            <javac source="1.4" target="1.4" fork="true" memorymaximumsize="64m"/>
+        </groovyc>
+
+    </target>
+
+    <target name="-initializeReports">
+        <mkdir dir="${reportsDirectory}"/>
+    </target>
+
+    <condition property="_shouldBeHeadless_">
+        <or>
+            <istrue value="${java.awt.headless}"/>
+            <os name="Mac OS X"/>
+        </or>
+    </condition>
+
+    <target name="-testInit" depends="-initHeadless">
+        <property name="headlessArg" value=""/>
+        <property name="junitJvmArgs"
+                  value="-Xms${groovyJUnit_ms} -XX:PermSize=${groovyJUnit_permSize} -XX:MaxPermSize=${groovyJUnit_maxPermSize} ${headlessArg}"/>
+    </target>
+
+    <target name="-initHeadless" if="_shouldBeHeadless_">
+        <property name="headlessArg" value="-Djava.awt.headless=true"/>
+        <echo message="Setting headless mode ..."/>
+    </target>
+
+    <target name="test"
+            depends="-checkAntVersion,-initializeReports,compileTest,-coverageInstrument,-testInit,-testOne,-testAll"
+            description="Compile and test all the classes (or just one class if testCase property is defined)."/>
+
+    <target name="clean-test" depends="clean,test"
+            description="Clean and compile and test all the classes (or just one class if testCase property is defined)."/>
+
+    <condition property="_testOne_">
+        <and>
+            <not>
+                <istrue value="${_skipTests_}"/>
+            </not>
+            <isset property="testCase"/>
+        </and>
+    </condition>
+
+    <target name="-testOne" if="_testOne_">
+        <mkdir dir="${junitRawDirectory}"/>
+        <junit printsummary="true" fork="true" includeantruntime="false" haltonfailure="yes"
+               maxmemory="${groovyJUnit_mx}" dir="${basedir}">
+            <jvmarg line="${junitJvmArgs}"/>
+            <test name="${testCase}" todir="${junitRawDirectory}"/>
+            <formatter type="brief" usefile="false"/>
+            <classpath>
+                <path refid="testPath"/>
+                <pathelement path="${instrumentedClassesDirectory}"/>
+                <pathelement path="${mainClassesDirectory}"/>
+                <pathelement path="${testClassesDirectory}"/>
+                <pathelement path="src/test"/>
+                <path refid="coberturaPath"/>
+            </classpath>
+        </junit>
+    </target>
+
+    <condition property="_testAll_">
+        <and>
+            <not>
+                <istrue value="${_skipTests_}"/>
+            </not>
+            <not>
+                <isset property="_testOne_"/>
+            </not>
+        </and>
+    </condition>
+
+    <target name="-collect14tests" unless="groovy.build.vm5">
+        <fileset id="ubertests.fileset" dir="${testClassesDirectory}" includes="UberTest*.class" excludes="Uber*VM5.class"/>
+    </target>
+
+    <target name="-collect15tests" if="groovy.build.vm5">
+        <fileset id="ubertests.fileset" dir="${testClassesDirectory}" includes="UberTest*.class"/>
+    </target>
+
+    <target name="-testAll" if="_testAll_" depends="-collect14tests,-collect15tests">
+        <mkdir dir="${junitRawDirectory}"/>
+        <junit printsummary="true" fork="true" includeantruntime="false" failureproperty="testFailed"
+               maxmemory="${groovyJUnit_mx}" dir="${basedir}">
+            <jvmarg line="${junitJvmArgs}"/>
+            <formatter type="xml"/>
+            <formatter type="plain" unless="noTextReports"/>
+            <batchtest todir="${junitRawDirectory}">
+                <fileset refid="ubertests.fileset"/>
+            </batchtest>
+            <classpath>
+                <path refid="testPath"/>
+                <pathelement path="${instrumentedClassesDirectory}"/>
+                <pathelement path="${mainClassesDirectory}"/>
+                <pathelement path="${testClassesDirectory}"/>
+                <pathelement path="src/test"/>
+                <path refid="coberturaPath"/>
+            </classpath>
+        </junit>
+        <mkdir dir="${junitReportsDirectory}"/>
+        <junitreport tofile="${junitRawDirectory}/Results.xml">
+            <fileset dir="${junitRawDirectory}" includes="TEST-*.xml"/>
+            <report format="frames" todir="${junitReportsDirectory}"/>
+        </junitreport>
+    </target>
+
+    <target name="-reportTestFailed" depends="test,-coverageReport" if="testFailed">
+        <fail message="Test failed, not processing further targets."/>
+    </target>
+
+    <target name="-coverageInstrument" depends="-initCoverage,-coberturaInit" if="_forceCoverage_">
+        <mkdir dir="${instrumentedClassesDirectory}"/>
+        <coberturaInstrument classesDirectory="${mainClassesDirectory}"/>
+    </target>
+
+    <target name="-coverageReport" depends="-initCoverage" if="_forceCoverage_">
+        <coberturaReport reportDirectory="${reportsDirectory}/cobertura" sourceDirectory="${mainSourceDirectory}"/>
+    </target>
+
+    <target name="-actuallyCreateJars"
+            depends="-makeManifest,-initializeJars,-createBaseJar,-createEmbeddableJar"
+            unless="testFailed"/>
+
+    <target name="-makeManifest">
+        <mkdir dir="${mainClassesDirectory}/META-INF"/>
+        <copy todir="${mainClassesDirectory}/META-INF" file="LICENSE.txt"/>
+        <manifest file="${mainClassesDirectory}/META-INF/MANIFEST.MF">
+            <attribute name="Built-By" value="${user.name}"/>
+            <attribute name="Extension-Name" value="groovy"/>
+            <attribute name="Specification-Title" value="Groovy: a powerful, dynamic language for the JVM"/>
+            <attribute name="Specification-Version" value="${groovyVersion}"/>
+            <attribute name="Specification-Vendor" value="The Codehaus"/>
+            <attribute name="Implementation-Title" value="Groovy: a powerful, dynamic language for the JVM"/>
+            <attribute name="Implementation-Version" value="${groovyVersion}"/>
+            <attribute name="Implementation-Vendor" value="The Codehaus"/>
+        </manifest>
+    </target>
+
+    <target name="-includeResources" depends="-includeGroovyDocTemplates">
+        <copy todir="${mainClassesDirectory}">
+            <fileset dir="${mainSourceDirectory}">
+                <include name="groovy/ui/*.properties"/>
+                <include name="groovy/ui/**/*.png"/>
+                <include name="org/codehaus/groovy/tools/shell/**/*.properties"/>
+                <include name="org/codehaus/groovy/tools/shell/**/*.xml"/>
+            </fileset>
+        </copy>
+    </target>
+
+    <target name="-includeGroovyDocTemplates">
+        <copy todir="${mainClassesDirectory}">
+            <fileset dir="${mainSourceDirectory}">
+                <include name="org/codehaus/groovy/tools/groovydoc/gstring-templates/**/*.*"/>
+            </fileset>
+        </copy>
+    </target>
+
+    <target name="-initializeJars" depends="test">
+        <mkdir dir="${targetDistDirectory}"/>
+    </target>
+
+    <target name="-createBaseJar" unless="testFailed">
+        <jar destfile="${targetDistDirectory}/groovy-${groovyVersion}.jar" basedir="${mainClassesDirectory}"
+             excludes="*.groovy" manifest="${mainClassesDirectory}/META-INF/MANIFEST.MF"/>
+        <jar destfile="${targetDistDirectory}/groovy-${groovyVersion}-sources.jar" basedir="${mainSourceDirectory}"/>
+    </target>
+
+    <target name="-jarjarInit">
+        <taskdef name="jarjar" classname="com.tonicsystems.jarjar.JarJarTask" classpathref="toolsPath"/>
+    </target>
+
+    <target name="-createEmbeddableJar" depends="-jarjarInit" unless="testFailed">
+        <delete dir="${stagingDirectory}" quiet="true"/>
+        <mkdir dir="${stagingDirectory}"/>
+        <unzip dest="${stagingDirectory}">
+            <fileset dir="${runtimeLibDirectory}">
+                <include name="antlr*.jar"/>
+                <include name="asm*.jar"/>
+                <exclude name="asm-attr*.jar"/>
+                <exclude name="asm-util*.jar"/>
+                <exclude name="asm-analysis*.jar"/>
+            </fileset>
+        </unzip>
+        <unzip dest="${stagingDirectory}">
+            <fileset dir="${targetDistDirectory}">
+                <include name="groovy-${groovyVersion}.jar"/>
+            </fileset>
+        </unzip>
+        <copy toDir="${stagingDirectory}/META-INF">
+            <fileset dir="${basedir}">
+                <include name="ASM-LICENSE.txt"/>
+            </fileset>
+        </copy>
+        <mkdir dir="${targetDistDirectory}"/>
+        <jarjar jarfile="${targetDistDirectory}/groovy-all-minimal-${groovyVersion}.jar"
+                manifest="${stagingDirectory}/META-INF/MANIFEST.MF">
+            <fileset dir="${stagingDirectory}"/>
+            <rule pattern="antlr.**" result="groovyjarjarantlr.@1"/>
+            <rule pattern="org.objectweb.**" result="groovyjarjarasm.@1"/>
+        </jarjar>
+        <!-- add commons-cli -->
+        <unzip dest="${stagingDirectory}">
+            <fileset dir="${runtimeLibDirectory}">
+                <include name="commons-cli-*.jar"/>
+            </fileset>
+        </unzip>
+        <jarjar jarfile="${targetDistDirectory}/groovy-all-${groovyVersion}.jar"
+                manifest="${stagingDirectory}/META-INF/MANIFEST.MF">
+            <fileset dir="${stagingDirectory}" excludes="groovy/util/CliBuilder*.class,groovy/util/OptionAccessor*.class"/>
+            <rule pattern="antlr.**" result="groovyjarjarantlr.@1"/>
+            <rule pattern="org.objectweb.**" result="groovyjarjarasm.@1"/>
+            <rule pattern="org.apache.commons.cli.**" result="groovyjarjarcommonscli.@1"/>
+        </jarjar>
+        <jar destfile="${targetDistDirectory}/groovy-all-${groovyVersion}.jar" update="true"
+             basedir="${stagingDirectory}" includes="groovy/util/CliBuilder*.class,groovy/util/OptionAccessor*.class"/>
+        <copy file="${targetDistDirectory}/groovy-${groovyVersion}-sources.jar"
+              tofile="${targetDistDirectory}/groovy-all-${groovyVersion}-sources.jar"/>
+        <copy file="${targetDistDirectory}/groovy-all-${groovyVersion}-sources.jar"
+              tofile="${targetDistDirectory}/groovy-all-minimal-${groovyVersion}-sources.jar"/>
+        <delete dir="${stagingDirectory}" quiet="true"/>
+    </target>
+
+    <target name="createJars" depends="-checkAntVersion,-reportTestFailed,-actuallyCreateJars"
+            description="Build Groovy and create the jarfiles."/>
+
+    <target name="install" depends="createJars" unless="testFailed"
+            description="Create an installation hierarchy in target/install.">
+
+        <!--
+        FIXME: Its not really a good idea to delete stuff, as it tends to negate Ant's (or other tools)
+               ability to run faster incremental builds.
+        -->
+        <delete dir="${installDirectory}" quiet="true"/>
+        <mkdir dir="${installDirectory}"/>
+
+        <!-- Install license files -->
+        <copy todir="${installDirectory}">
+            <fileset dir="${basedir}">
+                <include name="LICENSE.txt"/>
+                <include name="ASM-LICENSE.txt"/>
+                <include name="NOTICE.txt"/>
+            </fileset>
+        </copy>
+        <fixcrlf srcdir="${installDirectory}" eol="crlf" includes="*.txt"/>
+
+        <!-- Install generated artifacts and runtime dependencies -->
+        <mkdir dir="${installDirectory}/lib"/>
+        <copy todir="${installDirectory}/lib">
+            <fileset file="${targetDistDirectory}/groovy-${groovyVersion}.jar"/>
+            <fileset dir="${runtimeLibDirectory}" includes="*.jar"/>
+        </copy>
+
+        <!-- Install the embeddable bits -->
+        <mkdir dir="${installDirectory}/embeddable"/>
+        <copy todir="${installDirectory}/embeddable">
+            <fileset file="${targetDistDirectory}/groovy-all-${groovyVersion}.jar"/>
+        </copy>
+
+        <!-- Install configuration files -->
+        <mkdir dir="${installDirectory}/conf"/>
+        <copy toDir="${installDirectory}/conf">
+            <fileset dir="${sourceDirectory}/conf" includes="*"/>
+        </copy>
+
+        <!-- Install scripts -->
+        <mkdir dir="${installDirectory}/bin"/>
+        <copy toDir="${installDirectory}/bin">
+            <fileset dir="${sourceDirectory}/bin" includes="*"/>
+            <filterset>
+                <filter token="GROOVYJAR" value="groovy-${groovyVersion}.jar"/>
+            </filterset>
+        </copy>
+
+        <!-- Tweak scripts for platform compatibility -->
+        <fixcrlf srcdir="${installDirectory}/bin" eol="lf" excludes="*.bat"/>
+        <fixcrlf srcdir="${installDirectory}/bin" eol="crlf" includes="*.bat"/>
+        <chmod perm="ugo+x">
+            <fileset dir="${installDirectory}/bin" includes="*,*.*"/>
+        </chmod>
+    </target>
+
+    <target name="checkstyle" depends="-init,-initializeReports,-checkstyleInit"
+            description="Create the code style reports.">
+        <checkAndReport
+                reportDirectory="${reportsDirectory}/checkstyle"
+                sourceDirectory="${mainSourceDirectory}"
+                excludes="org/codehaus/groovy/antlr/parser/*,org/codehaus/groovy/antlr/java/*,org/codehaus/groovy/syntax/Types.java">
+            <path>
+                <pathelement path="${mainClassesDirectory}"/>
+                <path refid="testPath"/>
+            </path>
+        </checkAndReport>
+    </target>
+
+    <target name="-compileTools" depends="compileMain">
+        <echo message="Groovyc of tools code."/>
+        <java classname="org.codehaus.groovy.ant.Groovyc" fork="true">
+            <classpath>
+                <path refid="toolsPath"/>
+                <pathelement path="${mainClassesDirectory}"/>
+                <path refid="compilePath"/>
+            </classpath>
+            <arg value="${toolsClassesDirectory}"/>
+            <arg value="${toolsSourceDirectory}"/>
+        </java>
+        <copy todir="${toolsClassesDirectory}">
+            <fileset dir="${toolsSourceDirectory}">
+                <include name="**/*.html"/>
+            </fileset>
+        </copy>
+    </target>
+
+    <target name="buildinfo">
+        <mkdir dir="${cruiseReportRootDirectory}"/>
+        <copy todir="${cruiseReportRootDirectory}">
+            <fileset dir="config/build"/>
+        </copy>
+    </target>
+
+    <target name="cruiseInit">
+        <property name="noTextReports" value="true"/>
+        <property name="forceCoverage" value="true"/>
+        <delete dir="${reportsDirectory}" quiet="true" failonerror="false"/>
+        <delete dir="${junitRawDirectory}" quiet="true" failonerror="false"/>
+    </target>
+
+    <target name="cruise" depends="cruiseInit,distribution,compileExamples,checkstyle,buildinfo"/>
+
+    <target name="-docInit">
+        <mkdir dir="${docsDirectory}"/>
+    </target>
+
+    <target name="doc" depends="javadoc, groovydoc" description="Create the documentation."/>
+
+    <target name="javadoc" depends="-fetchDependencies,-docInit" description="Create the javadoc documentation.">
+        <property name="title" value="Groovy"/>
+        <javadoc destdir="${docsDirectory}/api" author="true" version="true"
+                 windowtitle="${title} (${groovyVersion})" doctitle="${title} (${groovyVersion})"
+                 encoding="ISO-8859-1" useexternalfile="true" source="1.4"
+                 footer="Copyright &amp;copy; 2003-2007 The Codehaus. All rights reserved.">
+            <classpath>
+                <path path="${mainClassesDirectory}"/>
+                <path refid="compilePath"/>
+            </classpath>
+            <fileset dir="${mainSourceDirectory}" includes="**/*.java"/>
+            <link href="http://java.sun.com/j2se/1.4.2/docs/api"/>
+            <link href="http://www.dpml.net/api/ant/1.7.0"/>
+            <link href="http://junit.sourceforge.net/junit3.8.1/javadoc/"/>
+            <link href="http://java.sun.com/j2ee/1.4/docs/api"/>
+            <link href="http://www.antlr2.org/javadoc"/>
+        </javadoc>
+        <jar basedir="${docsDirectory}/api" destfile="${targetDistDirectory}/groovy-${groovyVersion}-javadoc.jar"/>
+        <copy toFile="${targetDistDirectory}/groovy-all-${groovyVersion}-javadoc.jar">
+            <fileset file="${targetDistDirectory}/groovy-${groovyVersion}-javadoc.jar"/>
+        </copy>
+        <copy toFile="${targetDistDirectory}/groovy-all-minimal-${groovyVersion}-javadoc.jar">
+            <fileset file="${targetDistDirectory}/groovy-${groovyVersion}-javadoc.jar"/>
+        </copy>
+    </target>
+
+    <target name="groovydoc" depends="-compileTools,-docInit,-includeGroovyDocTemplates"
+            description="Create the groovydoc documentation.">
+        <java classname="org.codehaus.groovy.tools.DocGenerator" fork="true">
+            <classpath>
+                <pathelement path="${toolsClassesDirectory}"/>
+                <path refid="toolsPath"/>
+                <pathelement path="${mainClassesDirectory}"/>
+            </classpath>
+        </java>
+		<copy todir="target/html/groovy-jdk" file="src/tools/org/codehaus/groovy/tools/groovy.ico"/>
+        <taskdef name="groovydoc" classname="org.codehaus.groovy.ant.Groovydoc">
+            <classpath>
+                <path path="${mainClassesDirectory}"/>
+                <path refid="compilePath"/>
+            </classpath>
+        </taskdef>
+        <groovydoc
+                destdir="${docsDirectory}/gapi"
+                sourcepath="${mainSourceDirectory}"
+                packagenames="**.*"
+                use="true"
+                windowtitle="groovydoc"
+                private="false"/>
+    </target>
+
+	<target name="docGDK" depends="-compileTools" description="Create the GDK documentation">
+        <java classname="org.codehaus.groovy.tools.DocGenerator" fork="yes" failonerror="true">
+            <classpath>
+                <pathelement path="${toolsClassesDirectory}"/>
+                <path refid="toolsPath"/>
+                <pathelement path="${mainClassesDirectory}"/>
+            </classpath>
+        </java>
+		<copy todir="target/html/groovy-jdk" file="src/tools/org/codehaus/groovy/tools/groovy.ico"/>
+	</target>
+
+	<target name="clean" description="Clean out the built materials.">
+        <delete dir="${targetDirectory}" quiet="true"/>
+        <delete quiet="true">
+            <fileset dir="." includes="**/*~"/>
+            <fileset dir="${groovyParserDirectory}" includes="Groovy*.*"/>
+            <fileset dir="${javaParserDirectory}"
+                     includes="JavaLexer.java,JavaRecognizer.java,JavaTokenTypes.java,JavaTokenTypes.txt,*.smap"/>
+        </delete>
+    </target>
+
+    <target name="deploy" depends="-mavenDeployInit,install,doc"
+            description="Deploy jars to maven repository.">
+        <mavenDeploy version="${groovyVersion}" artifact="groovy"/>
+        <mavenDeploy version="${groovyVersion}" artifact="groovy-all"/>
+        <mavenDeploy version="${groovyVersion}" artifact="groovy-all-minimal"/>
+    </target>
+
+    <target name="installRepo" depends="-mavenInit,install,doc"
+            description="Deploy artifacts to local maven repository.">
+        <mavenInstallRepo version="${groovyVersion}" artifact="groovy"/>
+        <mavenInstallRepo version="${groovyVersion}" artifact="groovy-all"/>
+        <mavenInstallRepo version="${groovyVersion}" artifact="groovy-all-minimal"/>
+    </target>
+
+    <target name="distribution" depends="install,doc" description="Create everything needed for a distribution.">
+        <zip destfile="${targetDistDirectory}/groovy-binary-${groovyVersion}.zip"
+             comment="The Groovy ${groovyVersion} binary distribution.">
+
+            <!-- Make unix scripts executable -->
+            <zipfileset dir="${installDirectory}" prefix="groovy-${groovyVersion}" filemode="775">
+                <include name="bin/*"/>
+                <exclude name="bin/*.*"/>
+                <exclude name="bin/startGroovy*"/>
+            </zipfileset>
+
+            <!-- Include the other scripts asis -->
+            <zipfileset dir="${installDirectory}" prefix="groovy-${groovyVersion}">
+                <include name="bin/*.*"/>
+                <include name="bin/startGroovy*"/>
+            </zipfileset>
+
+            <!-- Include everything else asis too -->
+            <zipfileset dir="${installDirectory}" prefix="groovy-${groovyVersion}">
+                <exclude name="bin/**"/>
+                <include name="**"/>
+            </zipfileset>
+        </zip>
+
+        <zip destfile="${targetDistDirectory}/groovy-docs-${groovyVersion}.zip"
+             comment="The Groovy ${groovyVersion} documentation distribution.">
+            <zipfileset dir="${wikiPdfDirectory}" includes="wiki-snapshot.pdf" prefix="groovy-${groovyVersion}/pdf"/>
+            <zipfileset dir="${docsDirectory}" prefix="groovy-${groovyVersion}/html"/>
+        </zip>
+
+        <zip destfile="${targetDistDirectory}/groovy-src-${groovyVersion}.zip"
+             comment="The Groovy ${groovyVersion} source distribution.">
+            <zipfileset dir="${basedir}" prefix="groovy-${groovyVersion}">
+                <!-- Exclude generated bits as well as any other bits that shouldn't make it in -->
+                <exclude name="${targetDirectory}/**"/>
+                <exclude name="classes/**"/>
+                <exclude name="cruise/**"/>
+                <exclude name=".clover/*"/>
+                <exclude name="local.build.properties"/>
+                <exclude name="cobertura.ser"/>
+                <exclude name="junitvmwatcher*.properties"/>
+            </zipfileset>
+        </zip>
+    </target>
+
+    <target name="dist" depends="distribution" description="Alias to distribution for the lazy."/>
+
+</project>
diff --git a/groovy/config/ant/build-checkstyle.xml b/groovy/config/ant/build-checkstyle.xml
new file mode 100644
index 0000000..3d87925
--- /dev/null
+++ b/groovy/config/ant/build-checkstyle.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+
+<!--
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed under the License is
+distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied. See the License for the specific language governing permissions and limitations under the License.
+
+This work is copyright by the author(s) and is part of a greater work collectively copyright by the
+Groovy community. See the NOTICE.txt file distributed with this work for additional information.
+
+Author : Paul King
+$Revision: 5519 $ ($LastChangedBy: paulk $)
+$Date: 2007-03-11 19:14:07 +1000 (Sun, 11 Mar 2007) $
+-->
+
+<project name="build-checkstyle" default="" xmlns:cs="antlib:com.puppycrawl.tools.checkstyle" basedir="../..">
+
+    <import file="build-setup.xml"/>
+
+    <target name="-checkstyleInit">
+        <taskdef resource="checkstyletask.properties" uri="antlib:com.puppycrawl.tools.checkstyle" classpathref="toolsPath"/>
+    </target>
+
+    <macrodef name="checkAndReport">
+        <attribute name="reportDirectory"/>
+        <attribute name="sourceDirectory"/>
+        <attribute name="excludes"/>
+        <element name="path"/>
+        <sequential>
+            <property name="checkstyle.basedir" value="@{sourceDirectory}"/>
+            <property name="checkstyleReportDir" location="@{reportDirectory}"/>
+            <property name="checkstyleResultPrefix" location="@{reportDirectory}/result"/>
+            <mkdir dir="@{reportDirectory}"/>
+            <cs:checkstyle config="config/checkstyle/config.xml" failureProperty="checkstyle.failure"
+                  failOnViolation="false">
+                <classpath>
+                    <path/>
+                </classpath>
+                <formatter type="xml" tofile="${checkstyleResultPrefix}.xml"/>
+                <fileset dir="@{sourceDirectory}" excludes="@{excludes}"/>
+            </cs:checkstyle>
+            <xslt in="${checkstyleResultPrefix}.xml" out="${checkstyleResultPrefix}.html" style="config/checkstyle/checkstyle-frames.xsl">
+                <param name="output.dir" expression="${checkstyleReportDir}"/>
+            </xslt>
+        </sequential>
+    </macrodef>
+
+</project>
diff --git a/groovy/config/ant/build-cobertura.xml b/groovy/config/ant/build-cobertura.xml
new file mode 100644
index 0000000..cf02e3d
--- /dev/null
+++ b/groovy/config/ant/build-cobertura.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+
+<!--
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed under the License is
+distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied. See the License for the specific language governing permissions and limitations under the License.
+
+This work is copyright by the author(s) and is part of a greater work collectively copyright by Codehaus on
+behalf of the Groovy community. See the NOTICE.txt file distributed with this work for additional information.
+
+Author : Paul King
+$Revision: 5519 $ ($LastChangedBy: paulk $)
+$Date: 2007-03-11 19:14:07 +1000 (Sun, 11 Mar 2007) $
+-->
+
+<project name="build-cobertura" default="" basedir="../..">
+
+    <import file="build-setup.xml"/>
+
+    <target name="-coberturaInit">
+        <taskdef resource="tasks.properties" classpathref="toolsPath"/>
+        <path id="coberturaPath">
+            <fileset dir="${toolsLibDirectory}" includes="cobertura-*.jar,asm*.jar"/>
+        </path>
+        <delete dir="${instrumentedClassesDirectory}" quiet="true" failonerror="false"/>
+    </target>
+
+    <macrodef name="coberturaInstrument">
+        <attribute name="classesDirectory"/>
+        <sequential>
+            <mkdir dir="${instrumentedClassesDirectory}"/>
+            <cobertura-instrument todir="${instrumentedClassesDirectory}">
+                <fileset dir="@{classesDirectory}" includes="**/*.class"/>
+            </cobertura-instrument>
+        </sequential>
+    </macrodef>
+
+    <macrodef name="coberturaReport">
+        <attribute name="reportDirectory"/>
+        <attribute name="sourceDirectory"/>
+        <sequential>
+            <cobertura-report destdir="${reportsDirectory}/cobertura">
+                <fileset dir="${mainSourceDirectory}"/>
+            </cobertura-report>
+        </sequential>
+    </macrodef>
+
+</project>
diff --git a/groovy/config/ant/build-maven.xml b/groovy/config/ant/build-maven.xml
new file mode 100644
index 0000000..7f3ba0c
--- /dev/null
+++ b/groovy/config/ant/build-maven.xml
@@ -0,0 +1,191 @@
+<?xml version="1.0"?>
+
+<!--
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed under the License is
+distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied. See the License for the specific language governing permissions and limitations under the License.
+
+This work is copyright by the author(s) and is part of a greater work collectively copyright by the
+Groovy community. See the NOTICE.txt file distributed with this work for additional information.
+
+Author : Paul King
+$Revision$ ($LastChangedBy$)
+$Date$
+-->
+
+<project name="build-maven" default="" basedir="../.." xmlns:artifact="urn:maven-artifact-ant">
+
+    <import file="build-setup.xml"/>
+
+    <macrodef name="maven.dependencies">
+        <attribute name="useScope"/>
+        <attribute name="filesetId"/>
+        <attribute name="pomRefId"/>
+        <sequential>
+            <artifact:dependencies useScope="@{useScope}" filesetId="@{filesetId}">
+                <remoteRepository refid="ibiblio.remote.repository"/>
+                <remoteRepository refid="repo1.remote.repository"/>
+                <pom refid="@{pomRefId}"/>
+            </artifact:dependencies>
+        </sequential>
+    </macrodef>
+
+    <macrodef name="fetch.maven">
+        <attribute name="scope"/>
+        <attribute name="module"/>
+        <sequential>
+            <maven.dependencies useScope="@{scope}" filesetId="fs.@{scope}.@{module}" pomRefId="@{module}.pom"/>
+        </sequential>
+    </macrodef>
+
+    <macrodef name="maven.pom">
+        <attribute name="file"/>
+        <attribute name="id"/>
+        <sequential>
+            <artifact:pom file="@{file}" id="@{id}"/>
+        </sequential>
+    </macrodef>
+
+    <macrodef name="fetch.maven.all.scopes">
+        <attribute name="module"/>
+        <sequential>
+            <fetch.maven scope="compile" module="@{module}"/>
+            <fetch.maven scope="runtime" module="@{module}"/>
+            <fetch.maven scope="test" module="@{module}"/>
+        </sequential>
+    </macrodef>
+
+    <macrodef name="fetch.maven.all.modules">
+        <sequential>
+            <fetch.maven.all.scopes module="groovy"/>
+            <fetch.maven scope="runtime" module="groovy-tools"/>
+            <fetch.maven scope="runtime" module="groovy-examples"/>
+        </sequential>
+    </macrodef>
+
+    <macrodef name="maven.remote.repository">
+        <attribute name="id"/>
+        <attribute name="url"/>
+        <sequential>
+            <artifact:remoteRepository id="@{id}" url="@{url}"/>
+        </sequential>
+    </macrodef>
+
+    <target name="-mavenInit" depends="-mavenTaskdef,-mavenPomDefinitions,-mavenRepositoryDefinitions"/>
+
+    <target name="-mavenTaskdef">
+        <typedef resource="org/apache/maven/artifact/ant/antlib.xml" uri="urn:maven-artifact-ant">
+            <classpath>
+                <fileset dir="${bootstrapDirectory}" includes="maven-ant-tasks-*.jar"/>
+            </classpath>
+        </typedef>
+    </target>
+
+    <target name="-mavenPomDefinitions">
+        <maven.pom file="pom.xml" id="groovy.pom"/>
+        
+        <xslt in="pom.xml" out="${targetDirectory}/groovy-all.pom" style="config/maven/groovy-all.xsl"/>
+        <maven.pom file="${targetDirectory}/groovy-all.pom" id="groovy-all.pom"/>
+        
+        <xslt in="pom.xml" out="${targetDirectory}/groovy-all-minimal.pom" style="config/maven/groovy-all-minimal.xsl"/>
+        <maven.pom file="${targetDirectory}/groovy-all-minimal.pom" id="groovy-all-minimal.pom"/>
+        
+        <maven.pom file="config/maven/groovy-tools.pom" id="groovy-tools.pom"/>
+        <maven.pom file="config/maven/groovy-examples.pom" id="groovy-examples.pom"/>
+    </target>
+
+    <target name="-mavenRepositoryDefinitions">
+        <maven.remote.repository id="repo1.remote.repository" url="http://repo1.maven.org/maven2/"/>
+        <maven.remote.repository id="ibiblio.remote.repository" url="http://ibiblio.org/maven2/"/>
+    </target>
+
+    <target name="-mavenFetchAllModules" depends="-mavenInit">
+        <fetch.maven.all.modules/>
+    </target>
+
+    <target name="-fetchDependencies" depends="-mavenFetchAllModules,-copyLibraries"/>
+
+    <target name="-copyLibraries">
+        <delete dir="${compileLibDirectory}"/>
+        <delete dir="${testLibDirectory}"/>
+        <delete dir="${runtimeLibDirectory}"/>
+        <delete dir="${toolsLibDirectory}"/>
+        <delete dir="${examplesLibDirectory}"/>
+
+        <mkdir dir="${compileLibDirectory}"/>
+        <mkdir dir="${testLibDirectory}"/>
+        <mkdir dir="${runtimeLibDirectory}"/>
+        <mkdir dir="${toolsLibDirectory}"/>
+        <mkdir dir="${examplesLibDirectory}"/>
+
+        <copy todir="${compileLibDirectory}">
+            <mapper type="flatten"/>
+            <fileset refid="fs.compile.groovy"/>
+        </copy>
+        <path id="compilePath">
+            <fileset dir="${compileLibDirectory}" includes="**/*.jar"/>
+        </path>
+        <copy todir="${testLibDirectory}">
+            <mapper type="flatten"/>
+            <fileset refid="fs.test.groovy"/>
+        </copy>
+        <path id="testPath">
+            <fileset dir="${testLibDirectory}" includes="**/*.jar"/>
+        </path>
+        <copy todir="${runtimeLibDirectory}">
+            <mapper type="flatten"/>
+            <fileset refid="fs.runtime.groovy"/>
+        </copy>
+        <path id="runtimePath">
+            <fileset dir="${runtimeLibDirectory}" includes="**/*.jar"/>
+        </path>
+        <copy todir="${toolsLibDirectory}">
+            <mapper type="flatten"/>
+            <fileset refid="fs.runtime.groovy-tools"/>
+        </copy>
+        <path id="toolsPath">
+            <fileset dir="${toolsLibDirectory}" includes="**/*.jar"/>
+        </path>
+        <copy todir="${examplesLibDirectory}">
+            <mapper type="flatten"/>
+            <fileset refid="fs.runtime.groovy-examples"/>
+        </copy>
+        <path id="examplesPath">
+            <fileset dir="${examplesLibDirectory}" includes="**/*.jar"/>
+        </path>
+    </target>
+
+    <target name="-mavenDeployInit" depends="-mavenInit">
+        <artifact:install-provider artifactId="wagon-webdav" version="1.0-beta-2"/>
+    </target>
+
+    <macrodef name="mavenDeploy">
+        <attribute name="version"/>
+        <attribute name="artifact"/>
+        <sequential>
+            <artifact:deploy file="${targetDistDirectory}/@{artifact}-@{version}.jar">
+                <pom refid="@{artifact}.pom"/>
+                <attach file="${targetDistDirectory}/@{artifact}-@{version}-sources.jar" classifier="sources"/>
+                <attach file="${targetDistDirectory}/@{artifact}-@{version}-javadoc.jar" classifier="javadoc"/>
+            </artifact:deploy>
+        </sequential>
+    </macrodef>
+
+    <macrodef name="mavenInstallRepo">
+        <attribute name="version"/>
+        <attribute name="artifact"/>
+        <sequential>
+            <artifact:install file="${targetDistDirectory}/@{artifact}-@{version}.jar">
+                <pom refid="@{artifact}.pom"/>
+                <attach file="${targetDistDirectory}/@{artifact}-@{version}-sources.jar" classifier="sources"/>
+                <attach file="${targetDistDirectory}/@{artifact}-@{version}-javadoc.jar" classifier="javadoc"/>
+            </artifact:install>
+        </sequential>
+    </macrodef>
+
+</project>
diff --git a/groovy/config/ant/build-setup.xml b/groovy/config/ant/build-setup.xml
new file mode 100644
index 0000000..83ee2b2
--- /dev/null
+++ b/groovy/config/ant/build-setup.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+
+<!--
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
+compliance with the License. You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed under the License is
+distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied. See the License for the specific language governing permissions and limitations under the License.
+
+This work is copyright by the author(s) and is part of a greater work collectively copyright by the
+Groovy community. See the NOTICE.txt file distributed with this work for additional information.
+
+Author : Paul King
+$Revision: 5519 $ ($LastChangedBy: paulk $)
+$Date: 2007-03-11 19:14:07 +1000 (Sun, 11 Mar 2007) $
+-->
+
+<project name="build-setup" default="" basedir="../..">
+
+    <property name="bootstrapDirectory" location="bootstrap"/>
+    <property name="sourceDirectory" value="src"/>
+    <property name="wikiPdfDirectory" value="src"/>
+    <property name="mainSourceDirectory" location="${sourceDirectory}/main"/>
+    <property name="testSourceDirectory" value="${sourceDirectory}/test"/>
+    <property name="toolsSourceDirectory" location="${sourceDirectory}/tools"/>
+    <property name="examplesSourceDirectory" location="${sourceDirectory}/examples"/>
+    
+    <property name="targetDirectory" value="target"/>
+    <property name="installDirectory" value="${targetDirectory}/install"/>
+    <property name="cruiseReportRootDirectory" value="${targetDirectory}/root"/>
+    <property name="stagingDirectory" value="${targetDirectory}/staging"/>
+    <property name="docsDirectory" value="${targetDirectory}/html"/>
+    <property name="mainClassesDirectory" value="${targetDirectory}/classes"/>
+    <property name="testClassesDirectory" value="${targetDirectory}/test-classes"/>
+    <property name="toolsClassesDirectory" value="${targetDirectory}/tools-classes"/>
+    <property name="mainStubsDirectory" value="${targetDirectory}/stubs"/>
+    <property name="testStubsDirectory" value="${targetDirectory}/test-stubs"/>
+    
+    <property name="examplesClassesDirectory" value="${targetDirectory}/examples-classes"/>
+    <property name="instrumentedClassesDirectory" value="${targetDirectory}/instrumented-classes"/>
+    <property name="reportsDirectory" value="${targetDirectory}/reports"/>
+    <property name="targetLibDirectory" value="${targetDirectory}/lib"/>
+    <property name="targetDistDirectory" value="${targetDirectory}/dist"/>
+    
+    <property name="antlrDirectory" value="${mainSourceDirectory}/org/codehaus/groovy/antlr"/>
+    <property name="groovyParserDirectory" value="${antlrDirectory}/parser"/>
+    <property name="javaParserDirectory" value="${antlrDirectory}/java"/>
+
+    <property name="compileLibDirectory" value="${targetLibDirectory}/compile"/>
+    <property name="testLibDirectory" value="${targetLibDirectory}/test"/>
+    <property name="runtimeLibDirectory" value="${targetLibDirectory}/runtime"/>
+    <property name="toolsLibDirectory" value="${targetLibDirectory}/tools"/>
+    <property name="examplesLibDirectory" value="${targetLibDirectory}/examples"/>
+    <property name="junitRawDirectory" value="${targetDirectory}/test-reports"/>
+    <property name="junitReportsDirectory" value="${reportsDirectory}/junit"/>
+
+    <macrodef name="antforked">
+        <attribute name="target"/>
+        <sequential>
+            <java classname="org.apache.tools.ant.launch.Launcher" fork="true" maxmemory="256m" failonerror="true">
+                <classpath refid="runtimePath"/>
+                <arg value="@{target}"/>
+            </java>
+        </sequential>
+    </macrodef>
+    
+</project>
diff --git a/groovy/config/build/index.html b/groovy/config/build/index.html
new file mode 100644
index 0000000..c5c8464
--- /dev/null
+++ b/groovy/config/build/index.html
@@ -0,0 +1,16 @@
+<html>
+<body>
+<h1>Build Results</h1>
+<img src="../../images/DukeGroovyChair.jpg" align="right" alt="groovy build picture" />
+<h3>Reports</h3>
+<ul>
+    <li><a href="reports/junit/index.html">JUnit</a></li>
+    <li><a href="reports/checkstyle/index.html">Checkstyle</a></li>
+    <li><a href="reports/cobertura/index.html">Coverage</a></li>
+</ul>
+<h3>Deliverables</h3>
+<ul>
+    <li><a href="dist">Deliverables</a></li>
+</ul>
+</body>
+</html>
\ No newline at end of file
diff --git a/groovy/config/checkstyle/checkstyle-frames.xsl b/groovy/config/checkstyle/checkstyle-frames.xsl
new file mode 100644
index 0000000..2430f08
--- /dev/null
+++ b/groovy/config/checkstyle/checkstyle-frames.xsl
@@ -0,0 +1,394 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="http://xml.apache.org/xalan/redirect"
+    extension-element-prefixes="redirect">
+
+<!--
+ The Apache Software License, Version 1.1
+
+ Copyright (c) 2002 The Apache Software Foundation.  All rights
+ reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in
+    the documentation and/or other materials provided with the
+    distribution.
+
+ 3. The end-user documentation included with the redistribution, if
+    any, must include the following acknowlegement:
+       "This product includes software developed by the
+        Apache Software Foundation (http://www.apache.org/)."
+    Alternately, this acknowlegement may appear in the software itself,
+    if and wherever such third-party acknowlegements normally appear.
+
+ 4. The names "The Jakarta Project", "Ant", and "Apache Software
+    Foundation" must not be used to endorse or promote products derived
+    from this software without prior written permission. For written
+    permission, please contact apache@apache.org.
+
+ 5. Products derived from this software may not be called "Apache"
+    nor may "Apache" appear in their names without prior written
+    permission of the Apache Group.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+ ====================================================================
+
+ This software consists of voluntary contributions made by many
+ individuals on behalf of the Apache Software Foundation.  For more
+ information on the Apache Software Foundation, please see
+ <http://www.apache.org/>.
+ -->
+
+    <xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+    <xsl:decimal-format decimal-separator="." grouping-separator="," />
+
+    <xsl:key name="files" match="file" use="@name"/>
+    <xsl:key name="errorfiles" match="file[count(error)>0]" use="@name"/>
+    <xsl:key name="violations" match="file/error" use="@source"/>
+
+    <xsl:param name="output.dir" select="'.'"/>
+
+	<xsl:variable name="allFiles" select="/checkstyle/file[@name and generate-id(.) = generate-id(key('files', @name))]"/>
+	<xsl:variable name="allFilesWithError" select="/checkstyle/file[@name and generate-id(.) = generate-id(key('errorfiles', @name))]"/>
+
+    <xsl:template match="checkstyle">
+        <!-- create the index.html -->
+        <redirect:write file="{$output.dir}/index.html">
+            <xsl:call-template name="index.html"/>
+        </redirect:write>
+
+        <!-- create the stylesheet.css -->
+        <redirect:write file="{$output.dir}/stylesheet.css">
+            <xsl:call-template name="stylesheet.css"/>
+        </redirect:write>
+
+        <!-- create the overview-summary.html at the root -->
+        <redirect:write file="{$output.dir}/overview-frame.html">
+            <xsl:apply-templates select="." mode="overview"/>
+        </redirect:write>
+
+        <!-- create the all-classes.html at the root -->
+        <redirect:write file="{$output.dir}/allclasses-frame.html">
+            <xsl:apply-templates select="." mode="all.classes"/>
+        </redirect:write>
+
+        <!-- process all files -->
+        <xsl:apply-templates select="$allFilesWithError"/>
+    </xsl:template>
+
+    <xsl:template name="index.html">
+        <html>
+            <head>
+                <title>CheckStyle Audit</title>
+            </head>
+            <frameset cols="20%,80%">
+                <frame src="allclasses-frame.html" name="fileListFrame"/>
+                <frame src="overview-frame.html" name="fileFrame"/>
+            </frameset>
+            <noframes>
+                <h2>Frame Alert</h2>
+                <p>
+                    This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+                </p>
+            </noframes>
+        </html>
+    </xsl:template>
+
+    <xsl:template name="pageHeader">
+        <table border="0" cellpadding="0" cellspacing="0" width="100%">
+            <tr>
+                <td class="text-align:right"><h2>CheckStyle Audit</h2></td>
+            </tr>
+            <tr>
+                <td class="text-align:right">Designed for use with <a href='http://checkstyle.sourceforge.net/'>CheckStyle</a> and <a href='http://jakarta.apache.org'>Ant</a>.</td>
+            </tr>
+        </table>
+        <hr size="1"/>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="overview">
+        <html>
+            <head>
+                <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+            </head>
+            <body>
+                <!-- page header -->
+                <xsl:call-template name="pageHeader"/>
+
+                <!-- Summary part -->
+                <xsl:apply-templates select="." mode="summary"/>
+                <hr size="1" width="100%" align="left"/>
+
+                <!-- File list part -->
+                <xsl:apply-templates select="." mode="filelist"/>
+            </body>
+        </html>
+    </xsl:template>
+
+    <xsl:template name="stylesheet.css">
+        .bannercell {
+        border: 0px;
+        padding: 0px;
+        }
+        body {
+        margin-left: 10;
+        margin-right: 10;
+        font:normal 80% arial,helvetica,sanserif;
+        background-color:#FFFFFF;
+        color:#000000;
+        }
+        .a td {
+        background: #efefef;
+        }
+        .b td {
+        background: #fff;
+        }
+        th, td {
+        text-align: left;
+        vertical-align: top;
+        }
+        th {
+        font-weight:bold;
+        background: #ccc;
+        color: black;
+        }
+        table, th, td {
+        font-size:100%;
+        border: none
+        }
+        table.log tr td, tr th {
+
+        }
+        h2 {
+        font-weight:bold;
+        font-size:140%;
+        margin-bottom: 5;
+        }
+        h3 {
+        font-size:100%;
+        font-weight:bold;
+        background: #525D76;
+        color: white;
+        text-decoration: none;
+        padding: 5px;
+        margin-right: 2px;
+        margin-left: 2px;
+        margin-bottom: 0;
+        }
+    </xsl:template>
+
+    <!--
+    Replace DOS characters in a path.
+    Replace '\' with '/', ':' with '_'.
+    -->
+    <xsl:template name="from-dos">
+        <xsl:param name="path"/>
+        <xsl:value-of select="translate($path, '\:', '/_')"/>
+    </xsl:template>
+
+    <!--
+    Creates an all-classes.html file that contains a link to all files.
+    -->
+    <xsl:template match="checkstyle" mode="all.classes">
+        <html>
+            <head>
+                <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+            </head>
+            <body>
+                <h2>Files</h2>
+                <p><a href="overview-frame.html" target="fileFrame">Summary</a></p>
+                <p>
+                    <table width="100%">
+                        <!-- For each file create its part -->
+                        <xsl:apply-templates select="$allFilesWithError" mode="all.classes">
+                            <xsl:sort select="@name"/>
+                        </xsl:apply-templates>
+                    </table>
+                </p>
+            </body>
+        </html>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="filelist">
+        <h3>Files</h3>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+            <tr>
+                <th>Name</th>
+                <th>Errors</th>
+            </tr>
+            <xsl:apply-templates select="$allFilesWithError" mode="filelist">
+                <xsl:sort select="@name"/>
+            </xsl:apply-templates>
+        </table>
+    </xsl:template>
+
+    <xsl:template match="file" mode="filelist">
+        <xsl:variable name="name" select="@name" />
+
+        <!--<xsl:if test="$first = 'true'">-->
+            <xsl:variable name="new-name">
+                <xsl:call-template name="from-dos">
+                    <xsl:with-param name="path" select="@name"/>
+                </xsl:call-template>
+            </xsl:variable>
+            <tr>
+                <xsl:call-template name="alternated-row" />
+                <td nowrap="nowrap">
+                    <a>
+                        <xsl:attribute name="href">
+                            <xsl:text>files/</xsl:text><xsl:value-of select="$new-name"/><xsl:text>.html</xsl:text>
+                        </xsl:attribute>
+                        <xsl:value-of select="@name"/>
+                    </a>
+                </td>
+                <td><xsl:value-of select="count(../file[@name = $name]/error)"/></td>
+            </tr>
+        <!--</xsl:if>-->
+    </xsl:template>
+
+    <xsl:template match="file" mode="all.classes">
+
+        <xsl:variable name="new-name">
+            <xsl:call-template name="from-dos">
+                <xsl:with-param name="path" select="@name"/>
+            </xsl:call-template>
+        </xsl:variable>
+        <tr>
+            <td nowrap="nowrap">
+                <a target="fileFrame">
+                    <xsl:attribute name="href">
+                        <xsl:text>files/</xsl:text>
+                        <xsl:value-of select="$new-name"/>
+                        <xsl:text>.html</xsl:text>
+                    </xsl:attribute>
+                    <xsl:value-of select="@name"/>
+                </a>
+            </td>
+        </tr>
+    </xsl:template>
+
+    <!--
+    transform string like a/b/c to ../../../
+    @param path the path to transform into a descending directory path
+    -->
+    <xsl:template name="path">
+        <xsl:param name="path"/>
+        <xsl:if test="contains($path,'/')">
+            <xsl:text>../</xsl:text>
+            <xsl:call-template name="path">
+                <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'/')"/></xsl:with-param>
+            </xsl:call-template>
+        </xsl:if>
+        <xsl:if test="not(contains($path,'/')) and not($path = '')">
+            <xsl:text>../</xsl:text>
+        </xsl:if>
+    </xsl:template>
+
+    <xsl:template match="file">
+        <xsl:variable name="name" select="@name"/>
+
+        <xsl:variable name="new-name">
+            <xsl:call-template name="from-dos">
+                <xsl:with-param name="path" select="@name"/>
+            </xsl:call-template>
+        </xsl:variable>
+        <redirect:write file="{$output.dir}/files/{$new-name}.html">
+            <html>
+                <head>
+                    <link rel="stylesheet" type="text/css">
+                        <xsl:attribute name="href">
+                            <xsl:call-template name="path">
+                                <xsl:with-param name="path" select="$new-name"/>
+                            </xsl:call-template>
+                            <xsl:text>stylesheet.css</xsl:text>
+                        </xsl:attribute>
+                    </link>
+                </head>
+                <body>
+                    <xsl:call-template name="pageHeader"/>
+                    <h3>File <xsl:value-of select="@name"/> </h3>
+                    <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+                        <tr>
+                            <th>Error Description</th>
+                            <th>Line</th>
+                        </tr>
+                        <xsl:for-each select="../file[@name = $name]/error">
+                            <tr>
+                                <xsl:call-template name="alternated-row"/>
+                                <td>
+                                    <xsl:value-of select="@message"/>
+                                </td>
+                                <td>
+                                    <xsl:value-of select="@line"/>
+                                </td>
+                            </tr>
+                        </xsl:for-each>
+                    </table>
+                </body>
+            </html>
+        </redirect:write>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="summary">
+        <h3>Summary</h3>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+            <tr>
+                <th>Files</th>
+                <th>Files with Errors</th>
+                <th>Errors</th>
+            </tr>
+            <tr>
+                <xsl:call-template name="alternated-row"/>
+                <td><xsl:value-of select="count($allFiles)"/></td>
+                <td><xsl:value-of select="count($allFilesWithError)"/></td>
+                <td><xsl:value-of select="count(file/error)"/></td>
+            </tr>
+        </table>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+            <tr>
+                <th>Check</th>
+                <th>Number of Violations</th>
+            </tr>
+            <xsl:for-each select="file/error[@source and generate-id(.) = generate-id(key('violations', @source))]">
+                <xsl:sort select="@source"/>
+                <xsl:variable name="source" select="@source"/>
+                <tr>
+                    <xsl:call-template name="alternated-row"/>
+                    <td>
+                        <xsl:value-of select="$source"/>
+                    </td>
+                    <td>
+                        <xsl:value-of select="count(/checkstyle/file/error[@source=$source])"/>
+                    </td>
+                </tr>
+            </xsl:for-each>
+        </table>
+    </xsl:template>
+
+    <xsl:template name="alternated-row">
+        <xsl:attribute name="class">
+            <xsl:if test="position() mod 2 = 1">a</xsl:if>
+            <xsl:if test="position() mod 2 = 0">b</xsl:if>
+        </xsl:attribute>
+    </xsl:template>
+
+</xsl:stylesheet>
+
diff --git a/groovy/config/checkstyle/config.xml b/groovy/config/checkstyle/config.xml
new file mode 100644
index 0000000..df6b19d
--- /dev/null
+++ b/groovy/config/checkstyle/config.xml
@@ -0,0 +1,161 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+        "-//Puppy Crawl//DTD Check Configuration 1.1//EN"
+        "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+<!--
+
+  A Checkstyle configuration that checks against the recommendations
+  in Joshua Bloch, Effective Java (highly recommended read!)
+
+  This file does NOT duplicate the checks for whitespace settings,
+  placement of curly braces, etc.  Only the rules that are explicitly
+  mentioned in the book are enforced.
+
+  Currently the amount of rules that can be automatically checked by
+  Checkstyle is not very large, but adding more checks of this quality
+  is a high priority goal for the development team.
+
+-->
+
+<module name="Checker">
+    <!-- checkstyle.basedir is the absolute path to sources, set in ant's build.xml -->
+    <property name="basedir" value="${checkstyle.basedir}"/>
+    <property name="localeCountry" value="en"/>
+    <property name="localeLanguage" value="en"/>
+
+    <module name="TreeWalker">
+
+        <!-- Item 4 - Avoid creating duplicate objects -->
+        <module name="IllegalInstantiation">
+            <property name="classes" value="java.lang.Boolean, java.lang.String"/>
+        </module>
+
+        <!-- Item 6 - Avoid finalizers -->
+        <!-- this will not find violations that contain linebreaks -->
+        <module name="GenericIllegalRegexp">
+            <property name="format"
+                      value="((public)|(protected))\s+void\s+finalize\(\s*\)"/>
+        </module>
+
+        <!-- Item 8 - Always override hashCode when you override equals -->
+        <module name="EqualsHashCode"/>
+
+        <!-- Item 12 - Make all fields private -->
+        <module name="VisibilityModifier">
+            <!-- TODO: add one or both of these back in? -->
+            <property name="protectedAllowed" value="true"/>
+            <property name="packageAllowed" value="true"/>
+        </module>
+
+        <!-- Item 15 - Design and document for inheritance or else prohibit it -->
+        <!-- the module actually implements a very strict rule, it would be
+             interesting to know whether Joshua meant what checkstyle implements.
+             We feel this implementation is well suited as a warning,
+             i.e. if you get error messages from this check you should be
+             able to name a good reason to implement your code the way you do it,
+             especially if you are designing a library and not an application.
+
+        <module name="DesignForExtension">
+          <property name="severity" value="warning"/>
+        </module>
+          -->
+
+        <!-- Item 17 - Use interfaces only to define types -->
+        <module name="InterfaceIsType"/>
+
+        <!-- Item 25 - Design method signatures carefully -->
+        <!-- Avoid long parameter lists -->
+        <module name="ParameterNumber">
+            <property name="max" value="6"/>
+        </module>
+
+        <!-- Item 28 - Write doc comments for all exposed API elements
+
+        <module name="JavadocType">
+          <property name="scope" value="protected"/>
+        </module>
+        <module name="JavadocMethod">
+          <property name="scope" value="protected"/>
+        </module>
+        <module name="JavadocVariable">
+          <property name="scope" value="protected"/>
+        </module>
+        -->
+
+
+        <!-- Item 38 - Adhere to generally accepted naming conventions -->
+        <module name="PackageName">
+            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]{1,22})*$"/>
+        </module>
+        <module name="TypeName"/>
+        <module name="ConstantName"/>
+        <module name="LocalFinalVariableName"/>
+        <module name="LocalVariableName"/>
+        <module name="MemberName"/>
+        <module name="MethodName"/>
+        <module name="ParameterName"/>
+        <module name="StaticVariableName"/>
+
+        <!-- Item 47 - Don't ignore exceptions -->
+        <module name="EmptyBlock">
+            <property name="tokens" value="LITERAL_CATCH"/>
+            <!-- require a comment, change to stmt to require a statement -->
+            <property name="option" value="text"/>
+        </module>
+
+        <!-- other sun checks -->
+        <module name="RedundantImport"/>
+        <module name="UnusedImports"/>
+        <module name="ModifierOrder"/>
+        <module name="RedundantModifier"/>
+        <!-- current style has lots of occurrences of this, off for now -->
+        <!--<module name="HiddenField"/>-->
+        <module name="MissingSwitchDefault"/>
+        <module name="DefaultComesLast"/>
+        <!-- very liberal values for metrics, perhaps tighten some later -->
+        <module name="JavaNCSS">
+            <property name="methodMaximum" value="80"/>
+            <property name="classMaximum" value="300"/>
+        </module>
+        <module name="ClassFanOutComplexity">
+            <property name="max" value="22"/>
+        </module>
+        <module name="ClassDataAbstractionCoupling">
+            <property name="max" value="14"/>
+        </module>
+        <module name="CyclomaticComplexity">
+            <property name="max" value="14"/>
+        </module>
+        <!-- TODO: turn this back on? -->
+        <!--<module name="ExplicitInitialization"/>-->
+        <!-- turned off - instead, keep methods short and make
+             parameters final in cases where confusion is possible -->
+        <!--<module name="ParameterAssignment"/>-->
+        <module name="IllegalType">
+            <property name="format" value="^$"/>
+            <!-- otherwise default of '*Abstract' is illegal -->
+            <property name="illegalClassNames" value="java.util.GregorianCalendar, java.util.Hashtable, java.util.HashSet, java.util.HashMap, java.util.ArrayList, java.util.LinkedHashMap, java.util.LinkedHashSet, java.util.TreeSet, java.util.TreeMap, java.util.Vector"/>
+        </module>
+        <module name="UpperEll"/>
+        <!-- off for now - to allow parenteses which add clarity -->
+        <!--<module name="UnnecessaryParentheses"/>-->
+        <module name="JUnitTestCase"/>
+        <module name="FinalClass"/>
+        <!-- good to have but pollutes coverage -->
+        <!--<module name="HideUtilityClassConstructor"/>-->
+        <module name="MutableException"/>
+        <!-- add below in eventually? -->
+        <!--<module name="LeftCurly">-->
+        <!--<property name="option" value="nl"/>-->
+        <!--<property name="tokens" value="CLASS_DEF,INTERFACE_DEF"/>-->
+        <!--</module>-->
+        <!-- add below in eventually for consistency -->
+        <!--<module name="ArrayTypeStyle"/>-->
+    </module>
+    <module name="PackageHtml"/>
+    <module name="au.com.redhillconsulting.simian.SimianCheck">
+        <property name="threshold" value="8"/>
+        <!-- remove language once Simian understands Groovy -->
+        <property name="language" value="java"/>
+    </module>
+</module>
diff --git a/groovy/config/maven/groovy-all-minimal.xsl b/groovy/config/maven/groovy-all-minimal.xsl
new file mode 100644
index 0000000..7ea7336
--- /dev/null
+++ b/groovy/config/maven/groovy-all-minimal.xsl
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns="http://maven.apache.org/POM/4.0.0"
+    exclude-result-prefixes="#default" version="1.0">
+
+    <!-- rename artifactId -->
+    <xsl:template match="/*[local-name() = 'project']/*[local-name() = 'artifactId']">
+        <artifactId>groovy-all-minimal</artifactId>
+    </xsl:template>
+
+    <!-- remove dependencies  -->
+    <xsl:template match="/*[local-name() = 'project']/*[local-name() = 'dependencies']"/>
+
+    <xsl:template match="@*|node()">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|node()"/>
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/config/maven/groovy-all.xsl b/groovy/config/maven/groovy-all.xsl
new file mode 100644
index 0000000..055b8f9
--- /dev/null
+++ b/groovy/config/maven/groovy-all.xsl
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns="http://maven.apache.org/POM/4.0.0"
+    exclude-result-prefixes="#default" version="1.0">
+
+    <!-- rename artifactId -->
+    <xsl:template match="/*[local-name() = 'project']/*[local-name() = 'artifactId']">
+        <artifactId>groovy-all</artifactId>
+    </xsl:template>
+
+    <!-- remove embedded antlr and asm dependencies  -->
+    <xsl:template match="/*[local-name() = 'project']/*[local-name() = 'dependencies']/*[local-name() = 'dependency'][*/text() = 'antlr']"/>
+    <xsl:template match="/*[local-name() = 'project']/*[local-name() = 'dependencies']/*[local-name() = 'dependency'][*/text() = 'asm']"/>
+
+    <xsl:template match="@*|node()">
+        <xsl:copy>
+            <xsl:apply-templates select="@*|node()"/>
+        </xsl:copy>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/config/maven/groovy-examples.pom b/groovy/config/maven/groovy-examples.pom
new file mode 100644
index 0000000..c4a9387
--- /dev/null
+++ b/groovy/config/maven/groovy-examples.pom
@@ -0,0 +1,73 @@
+<?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>groovy-examples</artifactId>

+    <version>internal</version>

+    <packaging>jar</packaging>

+    <!-- intentionally leaving off scope here as scope is probably overkill for examples -->

+    <dependencies>

+        <dependency>

+            <groupId>lucene</groupId>

+            <artifactId>lucene</artifactId>

+            <version>1.4.3</version>

+        </dependency>

+        <dependency>

+            <groupId>commons-httpclient</groupId>

+            <artifactId>commons-httpclient</artifactId>

+            <version>3.0.1</version>

+            <scope>runtime</scope>

+            <!-- exclude non-compile scoped dependencies manually for now -->

+            <exclusions>

+                <exclusion>

+                    <groupId>junit</groupId>

+                    <artifactId>junit</artifactId>

+                </exclusion>

+                <exclusion>

+                    <groupId>commons-logging</groupId>

+                    <artifactId>commons-logging</artifactId>

+                </exclusion>

+                <exclusion>

+                    <groupId>commons-codec</groupId>

+                    <artifactId>commons-codec</artifactId>

+                </exclusion>

+            </exclusions>

+        </dependency>

+        <dependency>

+            <groupId>openejb</groupId>

+            <artifactId>openejb-loader</artifactId>

+            <version>1.0</version>

+            <scope>runtime</scope>

+            <!-- exclude unneeded libraries and specs -->

+            <exclusions>

+                <exclusion>

+                    <groupId>log4j</groupId>

+                    <artifactId>log4j</artifactId>

+                </exclusion>

+                <exclusion>

+                    <groupId>openejb</groupId>

+                    <artifactId>openejb-core</artifactId>

+                </exclusion>

+                <exclusion>

+                    <groupId>org.apache.geronimo.specs</groupId>

+                    <artifactId>geronimo-jta_1.0.1B_spec</artifactId>

+                </exclusion>

+                <exclusion>

+                    <groupId>org.apache.geronimo.specs</groupId>

+                    <artifactId>geronimo-servlet_2.4_spec</artifactId>

+                </exclusion>

+                <exclusion>

+                    <groupId>org.apache.geronimo.specs</groupId>

+                    <artifactId>geronimo-ejb_2.1_spec</artifactId>

+                </exclusion>

+                <exclusion>

+                    <groupId>org.apache.geronimo.specs</groupId>

+                    <artifactId>geronimo-j2ee-connector_1.5_spec</artifactId>

+                </exclusion>

+            </exclusions>

+        </dependency>

+

+    </dependencies>

+</project>

diff --git a/groovy/config/maven/groovy-tools.pom b/groovy/config/maven/groovy-tools.pom
new file mode 100644
index 0000000..ccbdc22
--- /dev/null
+++ b/groovy/config/maven/groovy-tools.pom
@@ -0,0 +1,46 @@
+<?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>groovy-tools</artifactId>

+    <version>internal</version>

+    <packaging>jar</packaging>

+    <!-- intentionally leaving off scope here as scope is probably overkill for tools -->

+    <dependencies>

+        <dependency>

+            <groupId>com.tonicsystems</groupId>

+            <artifactId>jarjar</artifactId>

+            <version>0.6</version>

+        </dependency>

+        <dependency>

+            <groupId>checkstyle</groupId>

+            <artifactId>checkstyle</artifactId>

+            <version>4.3</version>

+            <exclusions>

+                <exclusion>

+                  <groupId>junit</groupId>

+                  <artifactId>junit</artifactId>

+                </exclusion>

+            </exclusions>

+        </dependency>

+        <dependency>

+            <groupId>redhill</groupId>

+            <artifactId>simian</artifactId>

+            <version>2.2.4</version>

+        </dependency>

+        <dependency>

+            <groupId>cobertura</groupId>

+            <artifactId>cobertura</artifactId>

+            <version>1.8</version>

+        </dependency>

+        <!-- used for the JavaDoc generator script -->

+        <dependency>

+            <groupId>qdox</groupId>

+            <artifactId>qdox</artifactId>

+            <version>1.5</version>

+            <scope>compile</scope>

+        </dependency>

+    </dependencies>

+</project>

diff --git a/groovy/cruise/build.xml b/groovy/cruise/build.xml
new file mode 100644
index 0000000..c8e1d2b
--- /dev/null
+++ b/groovy/cruise/build.xml
@@ -0,0 +1,57 @@
+<project name="groovy-cruisecontrol-starter" default="cruise"> 
+  
+  <property name="svn.tag" value="https://svn.codehaus.org/groovy/tags/cc"/>
+  <property name="dir.checkout" value=".."/>
+  <property name="svn.user" value="dierk"/>
+  
+  <!--target name="cruise" depends="update, copy-reporting-app, delegate"/-->
+  <target name="cruise" depends="update, copy-reporting-app, delegate, tagLastBuild"/>
+  
+  <!-- label is given by CruiseControl, provides a default value here for the case where the admin starts
+  this script manually. -->
+
+  <property name="label" value="manualBuild"/>
+  
+  <target name="update">
+    <echo message="*** getting the detected modifications ***"/>
+    <exec executable="svn" failonerror="true">
+      <arg line="--username ${svn.user} --non-interactive"/>
+      <arg line="update ${dir.checkout}"/>
+    </exec>
+  </target>
+  
+  <target name="delegate" description="Groovy-specific build parts">
+  <!-- disabled dk: no maven for Groovy build
+    <echo message="*** Starting the groovy-specific Maven 1 build parts ***"/>
+    <exec dir=".." executable="maven" failonerror="true"/>
+    <echo message="*** groovy build Maven 1 successfully ended          ***"/>
+    -->
+    <ant dir=".." antfile="build.xml" target="clean"/>
+	<ant dir=".." antfile="build.xml" target="cruise">
+		<property name="buildnumber" value="${label}"/>
+	</ant>
+    <echo message="*** groovy (${label}) Ant build successfully ended ***"/>
+  </target>
+  
+  <target name="tagLastBuild">
+    <exec executable="svn" failonerror="true">
+      <arg line="--username ${svn.user} --non-interactive"/>
+      <arg line="copy -m '' ${dir.checkout} ${svn.tag}/${label}"/>
+    </exec>
+    <exec executable="svn" failonerror="true">
+      <arg line="--username ${svn.user} --non-interactive"/>
+      <arg line="rm -m '' ${svn.tag}/LAST_BUILD"/>
+    </exec>
+    <exec executable="svn" failonerror="true">
+      <arg line="--username ${svn.user} --non-interactive"/>
+      <arg line="copy -m '' ${svn.tag}/${label} ${svn.tag}/LAST_BUILD"/>
+    </exec>
+  </target>
+  
+  <target name="copy-reporting-app">
+    <fail unless="reporting-app-dir" message="The property reporting-app-dir must be set from outside!" />
+    <copy todir="${reporting-app-dir}" >  <!-- overwrite="true" can be needed occasionally -->
+      <fileset dir="reporting-app" />   <!-- only changes to web.xml need context reload -->
+    </copy>
+  </target>
+</project>
diff --git a/groovy/cruise/buildstatus.xsl b/groovy/cruise/buildstatus.xsl
new file mode 100644
index 0000000..747483c
--- /dev/null
+++ b/groovy/cruise/buildstatus.xsl
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="us-ascii" ?>
+<!--
+  Excerpted from the book, "Pragmatic Project Automation"
+  ISBN 0-9745140-3-9
+  Copyright 2004 The Pragmatic Programmers, LLC.  All Rights Reserved.
+  Visit www.PragmaticProgrammer.com
+ -->
+
+
+<xsl:stylesheet
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+	version="1.0">
+
+	<xsl:output method="xml" indent="yes"/>
+
+	<xsl:template match="/">
+
+		<rss version="2.0">
+			<channel>
+
+				<title>groovy Build Status</title>
+
+				<link>http://build.canooo.com/groovy/</link>
+
+				<description>CruiseControl build status feed for the groovy project.</description>
+
+				<item>
+
+					<xsl:variable name="project.name"
+						select="cruisecontrol/info/property[@name='projectname']/@value"/>
+					<xsl:variable name="build.date"
+						select="cruisecontrol/info/property[@name='builddate']/@value"/>
+					<xsl:variable name="build.time"
+						select="cruisecontrol/build/@time"/>
+					<xsl:variable name="modification.list"
+						select="cruisecontrol/modifications/modification"/>
+
+					<xsl:variable name="testsuite.list" select="//testsuites/testsuite"/>
+					<xsl:variable name="testcase.list" select="$testsuite.list/testcase"/>
+					<title>
+						<xsl:choose>
+							<xsl:when test="cruisecontrol/build/@error">BUILD FAILED</xsl:when>
+							<xsl:otherwise>Build Succeeded</xsl:otherwise>
+						</xsl:choose>
+					</title>
+					<link>http://build.canoo.com/groovy/</link>
+					<description>
+						<xsl:choose>
+							<xsl:when test="cruisecontrol/build/@error">
+								<xsl:text>&lt;b&gt;Ant Error Message:&lt;/b&gt;&lt;br/&gt;</xsl:text>
+								<xsl:value-of select="cruisecontrol/build/@error"/>
+							</xsl:when>
+							<xsl:otherwise>
+								<xsl:text>&lt;b&gt;Build:&lt;/b&gt;</xsl:text>
+								<xsl:value-of select="cruisecontrol/info/property[@name='label']/@value"/>
+							</xsl:otherwise>
+						</xsl:choose>
+
+						&lt;br/&gt;
+						&lt;br/&gt;
+
+						&lt;b&gt;Date of build:&lt;/b&gt;
+						<xsl:value-of select="$build.date"/>&lt;br/&gt;
+
+						&lt;b&gt;Time to build:&lt;/b&gt;
+						<xsl:value-of select="$build.time"/>&lt;br/&gt;
+
+						<xsl:apply-templates select="$modification.list">
+							<xsl:sort select="date" order="descending" data-type="text"/>
+						</xsl:apply-templates>
+
+						&lt;br/&gt;
+
+						&lt;b&gt;Unit Tests:&lt;/b&gt;
+						<xsl:value-of select="count($testcase.list)"/>
+
+					</description>
+				</item>
+			</channel>
+		</rss>
+	</xsl:template>
+
+	<xsl:template match="modification">
+		<xsl:if test="position() = 1">
+
+			&lt;br/&gt;
+
+			&lt;b&gt;Last changed:&lt;/b&gt;
+			<xsl:value-of select="date"/>&lt;br/&gt;
+
+			&lt;b&gt;Last changed by:&lt;/b&gt;
+			<xsl:value-of select="user"/>&lt;br/&gt;
+
+			&lt;b&gt;Last log entry:&lt;/b&gt;
+			<xsl:value-of select="comment"/>&lt;br/&gt;
+
+		</xsl:if>
+	</xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/cruise/cc.xsl b/groovy/cruise/cc.xsl
new file mode 100644
index 0000000..399d402
--- /dev/null
+++ b/groovy/cruise/cc.xsl
@@ -0,0 +1,380 @@
+<?xml version="1.0"?>
+<!DOCTYPE xsl:stylesheet [
+	<!ENTITY space "&#32;">
+	<!ENTITY nbsp "&#160;">
+	<!ENTITY bullet "&#8226;">]>
+
+<xsl:stylesheet
+	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+   xmlns:xslt="http://xml.apache.org/xslt"
+	>
+	<xsl:output method="html"/>
+
+	<!-- collects all task nodes with parent target into the variable $tasklist -->
+	<xsl:variable name="tasklist" select="//task"/>
+
+	<!-- collects all task nodes from $tasklist with attribute name="Javac" etc. -->
+	<xsl:variable name="javac.tasklist" select="$tasklist[@name='javac']"/>
+	<xsl:variable name="ejbjar.tasklist" select="$tasklist[@name='ejbjar']"/>
+	<xsl:variable name="jar.tasklist" select="$tasklist[@name='jar']"/>
+	<xsl:variable name="war.tasklist" select="$tasklist[@name='war']"/>
+
+	<!-- count elements in sublists -->
+	<xsl:variable name="dist.count" select="count($jar.tasklist) + count($war.tasklist)"/>
+
+	<!-- collect all testsuite nodes in build, regardless of depth -->
+	<xsl:variable name="testsuite.list" select="//testsuites/testsuite"/>
+	<!-- count error nodes directly under testsuite -->
+	<xsl:variable name="testsuite.error.count" select="count($testsuite.list/error)"/>
+
+	<xsl:variable name="testcase.list" select="$testsuite.list/testcase"/>
+	<xsl:variable name="testcase.error.list" select="$testcase.list/error"/>
+	<xsl:variable name="testcase.failure.list" select="$testcase.list/failure"/>
+	<xsl:variable name="totalErrorsAndFailures" select="count($testcase.error.list) + count($testcase.failure.list)"/>
+
+	<!-- 1: /build/modifications, 2: /cruisecontrol/modifications -->
+	<xsl:variable name="modification.list" select="//modifications/modification"/>
+	<!-- collects all the modification sets -->
+	<xsl:key name="modificationSet" match="//modifications/modification" use="concat(user,':',comment)"/>
+
+	<!-- long template for the whole page, this ensures sequence -->
+	<xsl:template match="/">
+		<!-- Header Part -->
+
+		<xsl:choose>
+			<xsl:when test="//build/@error"> <!-- build tag contains attribute error -->
+				<h1>BUILD FAILED</h1>
+			</xsl:when>
+			<xsl:otherwise>
+				<h1>
+					<xsl:text>BUILD COMPLETE -&space;</xsl:text>
+					<!-- support CruiseControl 1.0 and 2.0 -->
+					<xsl:value-of select="build/label | //info/property[@name='label']/@value"/>
+				</h1>
+			</xsl:otherwise>
+		</xsl:choose>
+
+		<table>
+			<tr>
+				<th>Date of build</th>
+				<td>
+					<!-- support CruiseControl 1.0 and 2.0 -->
+					<xsl:value-of select="build/today | //info/property[@name='builddate']/@value"/>
+				</td>
+			</tr>
+			<tr>
+				<th>Time to build</th>
+				<td>
+					<xsl:value-of select="//build/@time"/>
+				</td>
+			</tr>
+			<tr>
+				<th>Last changed</th>
+				<td>
+					<xsl:value-of select="//modifications/modification/date"/>
+				</td>
+			</tr>
+			<tr>
+				<th>Last log entry</th>
+				<td>
+					<xsl:value-of select="//modifications/modification/comment"/>
+				</td>
+			</tr>
+		</table>
+
+		<xsl:if test="//build/@error">
+			<h2>Ant Error Message</h2>
+			<pre>
+				<xsl:value-of select="//build/@error"/>
+			</pre>
+			<pre>
+				<xsl:value-of select="//stacktrace"/>
+			</pre>
+		</xsl:if>
+
+		<!-- Compilation Messages -->
+
+		<xsl:variable name="javac.warn.messages" select="$javac.tasklist/message[@priority='warn']"/>
+		<xsl:variable name="ejbjar.warn.messages" select="$ejbjar.tasklist/message[@priority='warn']"/>
+		<xsl:variable name="total.errorMessage.count"
+		              select="count($javac.warn.messages) + count($ejbjar.warn.messages)"/>
+
+		<!-- NOTE: total.errorMessage.count is actually the number of lines of error
+									  messages. This accurately represents the number of errors ONLY if the Ant property
+									  build.compiler.emacs is set to "true" -->
+		<xsl:if test="$total.errorMessage.count > 0">
+			<h2>
+				<xsl:text>Error-/Warning- Lines:&space;</xsl:text>
+				<xsl:value-of select="$total.errorMessage.count"/>
+			</h2>
+
+			<pre>
+				<xsl:apply-templates select="$javac.warn.messages"/>
+			</pre>
+		</xsl:if>
+
+		<!-- Unit Tests -->
+		<xsl:variable name="unit.passed" select="count($testcase.list)-$totalErrorsAndFailures"/>
+
+		<h2>Unit Tests</h2>
+		<p>
+			<xsl:text>Test cases:&space;</xsl:text>
+			<b>
+				<xsl:value-of select="count($testcase.list)"/>
+			</b>
+			<xsl:text>, passed:&space;</xsl:text>
+			<b>
+				<xsl:value-of select="$unit.passed"/>
+			</b>
+			<xsl:text>, failures:&space;</xsl:text>
+			<b>
+				<xsl:value-of select="count($testcase.failure.list)"/>
+			</b>
+			<xsl:text>, errors:&space;</xsl:text>
+			<b>
+				<xsl:value-of select="count($testcase.error.list)"/>
+			</b>
+			<xsl:text>.</xsl:text>
+		</p>
+
+		<xsl:call-template name="colorBar">
+			<xsl:with-param name="success.count" select="$unit.passed"/>
+			<xsl:with-param name="failed.count" select="$totalErrorsAndFailures"/>
+			<xsl:with-param name="total.count" select="count($testcase.list)"/>
+			<xsl:with-param name="tableID">utests</xsl:with-param>
+		</xsl:call-template>
+
+		<xsl:if test="count($testcase.error.list) > 0">
+			<h3>Errors</h3>
+			<ul>
+				<xsl:apply-templates select="$testcase.error.list"/>
+			</ul>
+		</xsl:if>
+
+		<xsl:if test="count($testcase.failure.list) > 0">
+			<h3>Failures</h3>
+			<ul>
+				<xsl:apply-templates select="$testcase.failure.list"/>
+			</ul>
+		</xsl:if>
+
+		<xsl:if test="$totalErrorsAndFailures > 0">
+			<h3>
+				<xsl:text>Unit Test Error Details:&space;(</xsl:text>
+				<xsl:value-of select="$totalErrorsAndFailures"/>
+				<xsl:text>)</xsl:text>
+			</h3>
+			<xsl:apply-templates select="//testsuite/testcase[.//error]"/>
+			<xsl:apply-templates select="//testsuite/testcase[.//failure]"/>
+		</xsl:if>
+
+		<!-- Functional Tests -->
+
+		<!-- functional testing vars -->
+		<xsl:variable name="cases" select="//summary/testresult"/>
+		<xsl:variable name="steps" select="//summary/testresult/results/step"/>
+		<xsl:variable name="passed" select="$cases[@successful='yes']"/>
+		<xsl:variable name="failed" select="$cases[@successful='no']"/>
+
+		<h2>Functional Tests</h2>
+		<p>
+			<xsl:text>Szenarios:&space;</xsl:text>
+			<b>
+				<xsl:value-of select="count($cases)"/>
+			</b>
+			<xsl:text>, passed:&space;</xsl:text>
+			<b>
+				<xsl:value-of select="count($passed)"/>
+			</b>
+			<xsl:text>, failed:&space;</xsl:text>
+			<b>
+				<xsl:value-of select="count($failed)"/>
+			</b>
+			<xsl:text>, including&space;</xsl:text>
+			<b>
+				<xsl:value-of select="count($steps)"/>
+			</b>
+			<xsl:text>&space;single steps.</xsl:text>
+		</p>
+
+		<xsl:call-template name="colorBar">
+			<xsl:with-param name="success.count" select="count($passed)"/>
+			<xsl:with-param name="failed.count" select="count($failed)"/>
+			<xsl:with-param name="total.count" select="count($cases)"/>
+			<xsl:with-param name="tableID">ftests</xsl:with-param>
+		</xsl:call-template>
+
+		<h2>Modifications</h2>
+		<p>
+			<xsl:value-of select="count($modification.list)"/>
+			<xsl:text>&space;modifications since last build.</xsl:text>
+		</p>
+
+		<xsl:for-each select="$modification.list[count(.|key('modificationSet',concat(user,':',comment))[1])=1]">
+			<h3>
+				<xsl:value-of select="user"/>
+				<xsl:text>:&space;</xsl:text>
+				<xsl:value-of select="comment"/>
+				<xsl:text>&space;(</xsl:text>
+				<xsl:value-of select="date"/>
+				<xsl:text>)</xsl:text>
+			</h3>
+			<ul>
+				<xsl:apply-templates select="key('modificationSet',concat(user,':',comment))"/>
+			</ul>
+		</xsl:for-each>
+
+
+		<xsl:if test="$dist.count > 0">
+			<h2>Deployments</h2>
+			<p>
+				<xsl:value-of select="$dist.count"/>
+				<xsl:text>&space;files deployed by this build.</xsl:text>
+			</p>
+
+			<ul>
+				<xsl:apply-templates select="$jar.tasklist | $war.tasklist"/>
+			</ul>
+		</xsl:if>
+
+	</xsl:template>
+
+	<xsl:template name="colorBar">
+		<xsl:param name="success.count"/>
+		<xsl:param name="failed.count"/>
+		<xsl:param name="total.count"/>
+		<xsl:param name="tableID"/>
+
+		<table bgcolor="white" cellspacing="0" cellpadding="0">
+			<xsl:attribute name="id">
+				<xsl:value-of select="$tableID"/>
+			</xsl:attribute>
+			<tr>
+				<xsl:if test="$success.count > 0">
+					<td bgcolor="green">
+						<xsl:attribute name="width">
+							<xsl:value-of select="concat($success.count * 100 div $total.count, '%')"/>
+						</xsl:attribute>
+						&nbsp;
+					</td>
+				</xsl:if>
+
+				<xsl:if test="$failed.count > 0">
+					<td bgcolor="#FFFFA0">
+						<xsl:attribute name="width">
+							<xsl:value-of select="concat($failed.count * 100 div $total.count, '%')"/>
+						</xsl:attribute>
+						&nbsp;
+					</td>
+				</xsl:if>
+
+				<xsl:if test="($total.count = 0) or ($total.count > ($success.count + $failed.count))">
+					<td>&nbsp;</td>
+				</xsl:if>
+			</tr>
+		</table>
+	</xsl:template>
+
+
+	<!-- UnitTest Errors/Failures -->
+	<xsl:template match="error|failure">
+		<li>
+			<xsl:call-template name="colorOddEvenRow"/>
+			<xsl:value-of select="../@name"/>
+		</li>
+	</xsl:template>
+
+	<!-- UnitTest Errors And Failures Detail Template -->
+	<xsl:template match="//testsuite/testcase">
+		<h4>
+			<xsl:text>Test:&space;</xsl:text>
+			<xsl:value-of select="@name"/>
+		</h4>
+
+		<p>
+			<xsl:text>Type:</xsl:text>
+			<xsl:value-of select="node()/@type"/>
+		</p>
+		<p>
+			<xsl:text>Message:</xsl:text>
+			<xsl:value-of select="node()/@message"/>
+		</p>
+
+		<PRE>
+			<xsl:value-of select="node()"/>
+		</PRE>
+	</xsl:template>
+
+	<!-- Compilation Error Details -->
+	<xsl:template match="message[@priority='warn']">
+		<xsl:value-of select="text()"/>
+		<br/>
+	</xsl:template>
+	<!-- Test Execution Stacktrace -->
+	<xsl:template match="stacktrace">
+		<pre>
+			<xsl:value-of select="text()"/>
+		</pre>
+	</xsl:template>
+
+	<!-- Modifications template -->
+	<xsl:template match="modification[file/filename]">
+		<!-- CC 2.0 -->
+		<li>
+			<xsl:call-template name="colorOddEvenRow"/>
+			<xsl:call-template name="modificationType">
+				<xsl:with-param name="type" select="file/@action"/>
+			</xsl:call-template>
+
+			<xsl:value-of select="file/project"/>
+			<xsl:if test="string-length(normalize-space(file/project))">
+				<xsl:text>/</xsl:text>
+			</xsl:if>
+			<xsl:value-of select="file/filename"/>
+		</li>
+	</xsl:template>
+
+	<xsl:template match="modification">
+		<!-- CC 1.0 -->
+		<li>
+			<xsl:call-template name="colorOddEvenRow"/>
+			<xsl:call-template name="modificationType">
+				<xsl:with-param name="type" select="@type"/>
+			</xsl:call-template>
+
+			<xsl:value-of select="filename"/>
+		</li>
+	</xsl:template>
+
+	<xsl:template name="modificationType">
+		<xsl:param name="type"/>
+
+		<span class="modifificationType">
+			<xsl:choose>
+				<xsl:when test="$type='modified'">&gt;</xsl:when>
+				<xsl:when test="$type='added'">+</xsl:when>
+				<xsl:when test="$type='deleted'">-</xsl:when>
+				<xsl:otherwise><xsl:value-of select="$type"/></xsl:otherwise>
+			</xsl:choose>
+		</span>
+
+		&nbsp;
+	</xsl:template>
+
+	<!-- jar and war template -->
+	<xsl:template match="task[translate(string(@name),'jJwW','')='ar']">
+		<li>
+			<xsl:call-template name="colorOddEvenRow"/>
+			<xsl:value-of select="message"/>
+		</li>
+	</xsl:template>
+
+	<xsl:template name="colorOddEvenRow">
+		<xsl:attribute name="class">
+			<xsl:choose>
+				<xsl:when test="position() mod 2 = 0">evenrow</xsl:when>
+				<xsl:otherwise>oddrow</xsl:otherwise>
+			</xsl:choose>
+		</xsl:attribute>
+	</xsl:template>
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/groovy/cruise/emailmap.properties b/groovy/cruise/emailmap.properties
new file mode 100644
index 0000000..cc04ab6
--- /dev/null
+++ b/groovy/cruise/emailmap.properties
@@ -0,0 +1,12 @@
+#
+# Maps SVN user id to e-mail adress.
+#
+# these team members get notified on builds
+# of what they have commited to the SVN
+
+
+dierk=dierk.koenig@canoo.com
+graeme=graemerocher@yahoo.co.uk
+mguillem=mguillemot@yahoo.fr
+paulk=paulk@asert.com.au
+jbaumann=joachim.baumann@xinaris.de
\ No newline at end of file
diff --git a/groovy/cruise/htmlmail.xsl b/groovy/cruise/htmlmail.xsl
new file mode 100644
index 0000000..b24fb27
--- /dev/null
+++ b/groovy/cruise/htmlmail.xsl
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="us-ascii" ?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+	<xsl:import href="cc.xsl"/>
+	<xsl:output method="html"/>
+
+	<xsl:template match="/">
+		<html>
+			<head>
+				<title>CruiseControl Build Result</title>
+			</head>
+			<body>
+				<xsl:apply-imports/>
+			</body>
+		</html>
+	</xsl:template>
+</xsl:stylesheet>
diff --git a/groovy/cruise/project.xml b/groovy/cruise/project.xml
new file mode 100644
index 0000000..0b1e105
--- /dev/null
+++ b/groovy/cruise/project.xml
@@ -0,0 +1,61 @@
+<!-- 
+This file is referenced from cruisecontrol's config.xml and contains the project definition.
+
+Available entities are:
+basedir
+hostname
+defaultmailhost
+-->
+
+<project name="groovy" buildafterfailed="false">
+		<modificationset quietperiod="120">
+			<svn localworkingcopy="&basedir;/groovy/cruisecontrol/checkout"/>
+		</modificationset>
+		<schedule>
+			<!-- buildfile is relative to checkout dir -->
+			<ant    antscript="/usr/local/java/apache-ant-1.7.0/bin/ant"
+				buildfile="&basedir;/groovy/cruisecontrol/checkout/cruise/build.xml"
+				antWorkingDir="&basedir;/groovy/cruisecontrol/checkout"
+				usedebug="false"
+				uselogger="true"
+				>
+                            <property name="reporting-app-dir" value="&basedir;/groovy/&hostname;/groovy/"/>
+                        </ant>
+		</schedule>
+		<listeners>
+			<currentbuildstatuslistener file="&basedir;/groovy/cruisecontrol/logs/status.txt"/>
+		</listeners>
+		<log dir="&basedir;/groovy/cruisecontrol/logs">
+			<merge dir="&basedir;/groovy/cruisecontrol/checkout/target/test-reports/"/>
+		</log>
+
+		<publishers>
+            <artifactspublisher dir="&basedir;/groovy/cruisecontrol/checkout/target/reports"
+            dest="&basedir;/groovy/cruisecontrol/artifacts" subdirectory="reports"/>
+            <artifactspublisher dir="&basedir;/groovy/cruisecontrol/checkout/target/dist"
+            dest="&basedir;/groovy/cruisecontrol/artifacts" subdirectory="dist"/>
+            <artifactspublisher dir="&basedir;/groovy/cruisecontrol/checkout/target/root"
+            dest="&basedir;/groovy/cruisecontrol/artifacts"/>
+            <!-- htmlemail is used only for explicit subscribers -->
+            <htmlemail
+				buildresultsurl="http://&hostname;/groovy/"
+				mailhost="&defaultmailhost;"
+				defaultsuffix="@codehaus.org"
+				returnaddress="build-support@canoo.com"
+				spamwhilebroken="false"
+				skipusers="true"
+				logdir="&basedir;/groovy/cruisecontrol/logs"
+				xslfile="&basedir;/groovy/cruisecontrol/checkout/cruise/htmlmail.xsl"
+				>
+				<always address="scm@groovy.codehaus.org"/>
+				<always address="dierk.koenig@canoo.com"/>
+				<always address="mguillemot@yahoo.fr"/>
+                                <always address="build-support@canoo.com"/>
+			</htmlemail>
+			<XSLTLogPublisher
+				directory="&basedir;/groovy/&hostname;/groovy/"
+				outfilename="buildstatus.rss"
+				xsltfile="&basedir;/groovy/cruisecontrol/checkout/cruise/buildstatus.xsl"
+				/>
+		</publishers>
+</project>
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/BuildInfo.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/BuildInfo.class
new file mode 100644
index 0000000..ada309a
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/BuildInfo.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/BuildInfoSummary.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/BuildInfoSummary.class
new file mode 100644
index 0000000..9f8d53b
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/BuildInfoSummary.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/CruiseControlWebAppException.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/CruiseControlWebAppException.class
new file mode 100644
index 0000000..36f565d
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/CruiseControlWebAppException.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/StatusHelper.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/StatusHelper.class
new file mode 100644
index 0000000..3fc96c5
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/StatusHelper.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/chart/AbstractCruiseControlChartData.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/chart/AbstractCruiseControlChartData.class
new file mode 100644
index 0000000..ef6a3a0
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/chart/AbstractCruiseControlChartData.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/chart/PieChartData.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/chart/PieChartData.class
new file mode 100644
index 0000000..b10ba0e
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/chart/PieChartData.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/chart/TimeChartData.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/chart/TimeChartData.class
new file mode 100644
index 0000000..27329ab
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/chart/TimeChartData.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/servlet/FileServlet.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/servlet/FileServlet.class
new file mode 100644
index 0000000..6ba43da
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/servlet/FileServlet.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/servlet/WebFile.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/servlet/WebFile.class
new file mode 100644
index 0000000..49a8c86
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/servlet/WebFile.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/ArtifactsLinkTag.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/ArtifactsLinkTag.class
new file mode 100644
index 0000000..e34df35
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/ArtifactsLinkTag.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/ArtifactsLinkTagExtraInfo.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/ArtifactsLinkTagExtraInfo.class
new file mode 100644
index 0000000..414dda7
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/ArtifactsLinkTagExtraInfo.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/BuildInfoTag.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/BuildInfoTag.class
new file mode 100644
index 0000000..d44b402
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/BuildInfoTag.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/BuildInfoTagExtraInfo.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/BuildInfoTagExtraInfo.class
new file mode 100644
index 0000000..8f495ad
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/BuildInfoTagExtraInfo.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlBodyTagSupport.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlBodyTagSupport.class
new file mode 100644
index 0000000..2fb7861
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlBodyTagSupport.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlLogFileFilter.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlLogFileFilter.class
new file mode 100644
index 0000000..8925392
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlLogFileFilter.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlTagSupport$1.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlTagSupport$1.class
new file mode 100644
index 0000000..6c38ff5
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlTagSupport$1.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlTagSupport.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlTagSupport.class
new file mode 100644
index 0000000..ec3110f
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CruiseControlTagSupport.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CurrentBuildStatusTag.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CurrentBuildStatusTag.class
new file mode 100644
index 0000000..a6eb815
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/CurrentBuildStatusTag.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/LinkTag.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/LinkTag.class
new file mode 100644
index 0000000..0974be9
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/LinkTag.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/LinkTagExtraInfo.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/LinkTagExtraInfo.class
new file mode 100644
index 0000000..cd51562
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/LinkTagExtraInfo.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/NavigationTag.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/NavigationTag.class
new file mode 100644
index 0000000..43dac85
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/NavigationTag.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/NavigationTagExtraInfo.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/NavigationTagExtraInfo.class
new file mode 100644
index 0000000..81a62ef
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/NavigationTagExtraInfo.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/ReversedComparator.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/ReversedComparator.class
new file mode 100644
index 0000000..d7bcc2d
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/ReversedComparator.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/Tab.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/Tab.class
new file mode 100644
index 0000000..8d70a1a
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/Tab.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/TabSheetTag.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/TabSheetTag.class
new file mode 100644
index 0000000..14dd51e
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/TabSheetTag.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/TabTag.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/TabTag.class
new file mode 100644
index 0000000..3dd906f
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/TabTag.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/XSLTag$1.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/XSLTag$1.class
new file mode 100644
index 0000000..951562e
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/XSLTag$1.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/XSLTag.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/XSLTag.class
new file mode 100644
index 0000000..e78d42a
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/taglib/XSLTag.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/util/DateUtil.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/util/DateUtil.class
new file mode 100644
index 0000000..ed4a4e2
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/util/DateUtil.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/util/TimeNumberFormat.class b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/util/TimeNumberFormat.class
new file mode 100644
index 0000000..72ea257
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/classes/net/sourceforge/cruisecontrol/util/TimeNumberFormat.class
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/cruisecontrol-jsp11.tld b/groovy/cruise/reporting-app/WEB-INF/cruisecontrol-jsp11.tld
new file mode 100644
index 0000000..cea2d11
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/cruisecontrol-jsp11.tld
@@ -0,0 +1,150 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>

+<!DOCTYPE taglib

+        PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN"

+	"http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">

+

+<!--********************************************************************************

+ * CruiseControl, a Continuous Integration Toolkit

+ * Copyright (c) 2001, ThoughtWorks, Inc.

+ * 651 W Washington Ave. Suite 600

+ * Chicago, IL 60661 USA

+ * All rights reserved.

+ *

+ * Redistribution and use in source and binary forms, with or without

+ * modification, are permitted provided that the following conditions

+ * are met:

+ *

+ *     + Redistributions of source code must retain the above copyright

+ *       notice, this list of conditions and the following disclaimer.

+ *

+ *     + Redistributions in binary form must reproduce the above

+ *       copyright notice, this list of conditions and the following

+ *       disclaimer in the documentation and/or other materials provided

+ *       with the distribution.

+ *

+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the

+ *       names of its contributors may be used to endorse or promote

+ *       products derived from this software without specific prior

+ *       written permission.

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR

+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,

+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,

+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR

+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF

+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING

+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ ********************************************************************************-->

+<taglib>

+    <tlibversion>1.0</tlibversion>

+    <jspversion>1.1</jspversion>

+    <shortname>simple</shortname>

+    <uri></uri>

+    <info>

+        A simple tab library for the examples

+    </info>

+    <tag>

+        <name>xsl</name>

+        <tagclass>net.sourceforge.cruisecontrol.taglib.XSLTag</tagclass>

+        <bodycontent>JSP</bodycontent>

+        <info>

+            Transforms the xml build log using XSL.

+        </info>

+        <attribute>

+            <name>xslFile</name>

+            <required>true</required>

+        </attribute>

+        <attribute>

+            <name>xslRootContext</name>

+            <required>false</required>

+        </attribute>

+    </tag>

+    <tag>

+        <name>currentbuildstatus</name>

+        <tagclass>net.sourceforge.cruisecontrol.taglib.CurrentBuildStatusTag</tagclass>

+        <bodycontent>empty</bodycontent>

+        <info>

+            Report when the build started, or when it will start again.

+        </info>

+    </tag>

+    <tag>

+        <name>nav</name>

+        <tagclass>net.sourceforge.cruisecontrol.taglib.NavigationTag</tagclass>

+        <teiclass>net.sourceforge.cruisecontrol.taglib.NavigationTagExtraInfo</teiclass>

+        <bodycontent>JSP</bodycontent>

+        <info>

+            Builds the navigation

+        </info>

+        <attribute>

+            <name>dateFormat</name>

+            <required>false</required>

+        </attribute>

+        <attribute>

+            <name>startingBuildNumber</name>

+            <required>false</required>

+        </attribute>

+        <attribute>

+            <name>finalBuildNumber</name>

+            <required>false</required>

+        </attribute>

+    </tag>

+    <tag>

+        <name>artifactsLink</name>

+        <tagclass>net.sourceforge.cruisecontrol.taglib.ArtifactsLinkTag</tagclass>

+        <teiclass>net.sourceforge.cruisecontrol.taglib.ArtifactsLinkTagExtraInfo</teiclass>

+        <bodycontent>JSP</bodycontent>

+        <info>

+            Inserts link to the artifacts FileServlet

+        </info>

+    </tag>

+    <tag>

+        <name>buildInfo</name>

+        <tagclass>net.sourceforge.cruisecontrol.taglib.BuildInfoTag</tagclass>

+        <teiclass>net.sourceforge.cruisecontrol.taglib.BuildInfoTagExtraInfo</teiclass>

+        <bodycontent>empty</bodycontent>

+        <info>

+            Inserts information about the various builds into the page scope.

+        </info>

+    </tag>

+    <tag>

+        <name>tabsheet</name>

+        <tagclass>net.sourceforge.cruisecontrol.taglib.TabSheetTag</tagclass>

+        <bodycontent>JSP</bodycontent>

+        <info>

+            Creates a tab sheet that tabs can be placed into.

+        </info>

+    </tag>

+    <tag>

+        <name>tab</name>

+        <tagclass>net.sourceforge.cruisecontrol.taglib.TabTag</tagclass>

+        <bodycontent>JSP</bodycontent>

+        <info>Creates a tab that selected content gets put into.</info>

+        <attribute>

+            <name>name</name>

+            <required>true</required>

+        </attribute>

+        <attribute>

+            <name>label</name>

+            <required>true</required>

+        </attribute>

+    </tag>

+    <tag>

+        <name>link</name>

+        <tagclass>net.sourceforge.cruisecontrol.taglib.LinkTag</tagclass>

+        <teiclass>net.sourceforge.cruisecontrol.taglib.LinkTagExtraInfo</teiclass>

+        <bodycontent>empty</bodycontent>

+        <info>Builds up a link URL</info>

+        <attribute>

+            <name>id</name>

+            <required>true</required>

+        </attribute>

+        <attribute>

+            <name>exclude</name>

+            <required>false</required>

+        </attribute>

+    </tag>

+</taglib>

diff --git a/groovy/cruise/reporting-app/WEB-INF/cruisecontrol-jsp12.tld b/groovy/cruise/reporting-app/WEB-INF/cruisecontrol-jsp12.tld
new file mode 100644
index 0000000..3ca20d6
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/cruisecontrol-jsp12.tld
@@ -0,0 +1,145 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>

+<!DOCTYPE taglib

+  PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"

+  "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">

+

+<!--********************************************************************************

+ * CruiseControl, a Continuous Integration Toolkit

+ * Copyright (c) 2001, ThoughtWorks, Inc.

+ * 651 W Washington Ave. Suite 600

+ * Chicago, IL 60661 USA

+ * All rights reserved.

+ *

+ * Redistribution and use in source and binary forms, with or without

+ * modification, are permitted provided that the following conditions

+ * are met:

+ *

+ *     + Redistributions of source code must retain the above copyright

+ *       notice, this list of conditions and the following disclaimer.

+ *

+ *     + Redistributions in binary form must reproduce the above

+ *       copyright notice, this list of conditions and the following

+ *       disclaimer in the documentation and/or other materials provided

+ *       with the distribution.

+ *

+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the

+ *       names of its contributors may be used to endorse or promote

+ *       products derived from this software without specific prior

+ *       written permission.

+ *

+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS

+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT

+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR

+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR

+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,

+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,

+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR

+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF

+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING

+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ ********************************************************************************-->

+<taglib>

+    <tlib-version>1.0</tlib-version>

+    <jsp-version>1.2</jsp-version>

+    <short-name>cruisecontrol</short-name>

+    <uri>http://cruisecontrol.sourceforge.net/taglibs/cruisecontrol-1.0</uri>

+    <tag>

+        <name>xsl</name>

+        <tag-class>net.sourceforge.cruisecontrol.taglib.XSLTag</tag-class>

+        <body-content>JSP</body-content>

+        <description>

+            Transforms the xml build log using XSL.

+        </description>

+        <attribute>

+            <name>xslFile</name>

+            <required>true</required>

+        </attribute>

+        <attribute>

+            <name>xslRootContext</name>

+            <required>false</required>

+        </attribute>

+    </tag>

+    <tag>

+        <name>currentbuildstatus</name>

+        <tag-class>net.sourceforge.cruisecontrol.taglib.CurrentBuildStatusTag</tag-class>

+        <body-content>empty</body-content>

+        <description>

+            Report when the build started, or when it will start again.

+        </description>

+    </tag>

+    <tag>

+        <name>nav</name>

+        <tag-class>net.sourceforge.cruisecontrol.taglib.NavigationTag</tag-class>

+        <tei-class>net.sourceforge.cruisecontrol.taglib.NavigationTagExtraInfo</tei-class>

+        <body-content>JSP</body-content>

+        <description>

+            Builds the navigation

+        </description>

+        <attribute>

+            <name>dateFormat</name>

+            <required>false</required>

+        </attribute>

+        <attribute>

+            <name>startingBuildNumber</name>

+            <required>false</required>

+        </attribute>

+        <attribute>

+            <name>finalBuildNumber</name>

+            <required>false</required>

+        </attribute>

+    </tag>

+    <tag>

+        <name>artifactsLink</name>

+        <tag-class>net.sourceforge.cruisecontrol.taglib.ArtifactsLinkTag</tag-class>

+        <tei-class>net.sourceforge.cruisecontrol.taglib.ArtifactsLinkTagExtraInfo</tei-class>

+        <body-content>JSP</body-content>

+        <description>

+            Inserts link to the artifacts FileServlet

+        </description>

+    </tag>

+     <tag>

+        <name>buildInfo</name>

+        <tag-class>net.sourceforge.cruisecontrol.taglib.BuildInfoTag</tag-class>

+        <tei-class>net.sourceforge.cruisecontrol.taglib.BuildInfoTagExtraInfo</tei-class>

+        <body-content>empty</body-content>

+        <description>

+            Inserts information about the various builds into the page scope.

+        </description>

+    </tag>

+    <tag>

+        <name>tabsheet</name>

+        <tag-class>net.sourceforge.cruisecontrol.taglib.TabSheetTag</tag-class>

+        <body-content>JSP</body-content>

+        <description>Creates a tab sheet that tabs can be placed into.</description>

+    </tag>

+    <tag>

+        <name>tab</name>

+        <tag-class>net.sourceforge.cruisecontrol.taglib.TabTag</tag-class>

+        <body-content>JSP</body-content>

+        <description>Creates a tab that selected content gets put into.</description>

+        <attribute>

+            <name>name</name>

+            <required>true</required>

+        </attribute>

+        <attribute>

+            <name>label</name>

+            <required>true</required>

+        </attribute>

+    </tag>

+    <tag>

+        <name>link</name>

+        <tag-class>net.sourceforge.cruisecontrol.taglib.LinkTag</tag-class>

+        <tei-class>net.sourceforge.cruisecontrol.taglib.LinkTagExtraInfo</tei-class>

+        <body-content>empty</body-content>

+        <description>Builds up a link URL</description>

+        <attribute>

+            <name>id</name>

+            <required>true</required>

+        </attribute>

+        <attribute>

+            <name>exclude</name>

+            <required>false</required>

+        </attribute>

+    </tag>

+</taglib>

diff --git a/groovy/cruise/reporting-app/WEB-INF/lib/batik-awt-util.jar b/groovy/cruise/reporting-app/WEB-INF/lib/batik-awt-util.jar
new file mode 100644
index 0000000..943a536
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/lib/batik-awt-util.jar
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/lib/batik-svggen.jar b/groovy/cruise/reporting-app/WEB-INF/lib/batik-svggen.jar
new file mode 100644
index 0000000..e11e86c
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/lib/batik-svggen.jar
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/lib/batik-util.jar b/groovy/cruise/reporting-app/WEB-INF/lib/batik-util.jar
new file mode 100644
index 0000000..af3d81b
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/lib/batik-util.jar
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/lib/cewolf.jar b/groovy/cruise/reporting-app/WEB-INF/lib/cewolf.jar
new file mode 100644
index 0000000..dda5a9e
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/lib/cewolf.jar
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/lib/commons-logging.jar b/groovy/cruise/reporting-app/WEB-INF/lib/commons-logging.jar
new file mode 100644
index 0000000..a4d5563
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/lib/commons-logging.jar
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/lib/jcommon-0.8.0.jar b/groovy/cruise/reporting-app/WEB-INF/lib/jcommon-0.8.0.jar
new file mode 100644
index 0000000..90bbb30
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/lib/jcommon-0.8.0.jar
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/lib/jfreechart-0.9.8.jar b/groovy/cruise/reporting-app/WEB-INF/lib/jfreechart-0.9.8.jar
new file mode 100644
index 0000000..a9e6081
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/lib/jfreechart-0.9.8.jar
Binary files differ
diff --git a/groovy/cruise/reporting-app/WEB-INF/web.xml b/groovy/cruise/reporting-app/WEB-INF/web.xml
new file mode 100644
index 0000000..91aa38b
--- /dev/null
+++ b/groovy/cruise/reporting-app/WEB-INF/web.xml
@@ -0,0 +1,139 @@
+<!DOCTYPE web-app
+    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
+    "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
+
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<web-app>
+    <display-name>groovy CruiseControl Reporting App</display-name>
+    <description>The reporting application for CruiseControl. Provides a simple but rich
+        interface for viewing CruiseControl build reports.
+    </description>
+
+    <context-param>
+        <param-name>singleProject</param-name>
+        <param-value>true</param-value>
+        <description>Indicates if the CruiseControl instance is to report on only one project.
+            If it is, then you should set this to true.
+        </description>
+    </context-param>
+
+    <context-param>
+      <!-- You can set this value via the user.log.dir property in Ant, when building the WAR file. -->
+      <param-name>logDir</param-name>
+      <param-value>/opt/groovy/cruisecontrol/logs/</param-value>
+      <description> This should be the full path to your CruiseControl log directory. If you
+          are in single project mode, this will contain only the logs for your project. If you
+          are in multi-project mode, it is expected that you will have multiple sub-directories
+          inside this log directory, one for each project.
+      </description>
+    </context-param>
+
+    <context-param>
+      <!-- You can set this value via the user.build.status.file property in Ant, when building the WAR file. -->
+      <param-name>currentBuildStatusFile</param-name>
+      <param-value>status.txt</param-value>
+      <description>This should be the path to your current build status file, which is relative
+          to the log directory (or, in single-project mode, relative to the project's log
+          directory)
+      </description>
+    </context-param>
+
+    <servlet>
+      <servlet-name>buildresults</servlet-name>
+      <display-name>Build Result Reporter</display-name>
+      <description>Presents build results in a human-readable and intuitive format.</description>
+      <jsp-file>/main.jsp</jsp-file>
+    </servlet>
+
+    <servlet>
+        <servlet-name>index</servlet-name>
+        <display-name>Index/Summary page</display-name>
+        <description>Presents a summary of all projects, allowing easy navigation to each.</description>
+        <jsp-file>/index.jsp</jsp-file>
+    </servlet>
+
+    <servlet>
+        <servlet-name>ArtifactServlet</servlet-name>
+        <servlet-class>net.sourceforge.cruisecontrol.servlet.FileServlet</servlet-class>
+        <init-param>
+            <param-name>rootDir</param-name>
+            <param-value>/opt/groovy/cruisecontrol/artifacts/</param-value>
+        </init-param>
+    </servlet>
+
+    <servlet>
+        <servlet-name>LogServlet</servlet-name>
+        <servlet-class>net.sourceforge.cruisecontrol.servlet.FileServlet</servlet-class>
+    </servlet>
+
+    <!-- Used for charting... -->
+    <servlet>
+        <servlet-name>CewolfServlet</servlet-name>
+        <servlet-class>de.laures.cewolf.CewolfRenderer</servlet-class>
+    </servlet>
+
+    <servlet-mapping>
+        <servlet-name>CewolfServlet</servlet-name>
+        <url-pattern>/cewolf/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>LogServlet</servlet-name>
+        <url-pattern>/logs/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>ArtifactServlet</servlet-name>
+        <url-pattern>/artifacts/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+      <servlet-name>buildresults</servlet-name>
+      <!-- Strictly speaking, this should be /buildresults for single-project mode. But it works anyway. -->
+      <url-pattern>/buildresults/*</url-pattern>
+    </servlet-mapping>
+
+    <servlet-mapping>
+        <servlet-name>index</servlet-name>
+        <url-pattern>/index</url-pattern>
+    </servlet-mapping>
+
+    <welcome-file-list>
+    	<welcome-file>index.jsp</welcome-file>
+    </welcome-file-list>
+</web-app>
diff --git a/groovy/cruise/reporting-app/buildresults.jsp b/groovy/cruise/reporting-app/buildresults.jsp
new file mode 100644
index 0000000..2d414d0
--- /dev/null
+++ b/groovy/cruise/reporting-app/buildresults.jsp
@@ -0,0 +1,60 @@
+<%--********************************************************************************
+* CruiseControl, a Continuous Integration Toolkit
+* Copyright (c) 2001, ThoughtWorks, Inc.
+* 651 W Washington Ave. Suite 600
+* Chicago, IL 60661 USA
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+*
+*     + Redistributions of source code must retain the above copyright
+*       notice, this list of conditions and the following disclaimer.
+*
+*     + Redistributions in binary form must reproduce the above
+*       copyright notice, this list of conditions and the following
+*       disclaimer in the documentation and/or other materials provided
+*       with the distribution.
+*
+*     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+*       names of its contributors may be used to endorse or promote
+*       products derived from this software without specific prior
+*       written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+********************************************************************************--%>
+<%@ taglib uri="/WEB-INF/cruisecontrol-jsp11.tld" prefix="cruisecontrol"%>
+<cruisecontrol:xsl xslFile="/xsl/header.xsl"/>
+
+<cruisecontrol:artifactsLink>
+  <table width="98%" border="0" cellspacing="0" cellpadding="2" align="center">
+    <tr><td class="header-label"><a href="<%= artifacts_url %>/index.html">Build Artifacts</a></td></tr>
+  </table>
+</cruisecontrol:artifactsLink>
+
+<cruisecontrol:xsl xslFile="/xsl/maven.xsl"/>
+<p>
+<cruisecontrol:xsl xslFile="/xsl/checkstyle.xsl"/>
+<p>
+<cruisecontrol:xsl xslFile="/xsl/compile.xsl"/>
+<p>
+<cruisecontrol:xsl xslFile="/xsl/javadoc.xsl"/>
+<p>
+<cruisecontrol:xsl xslFile="/xsl/unittests.xsl"/>
+<p>
+<cruisecontrol:xsl xslFile="/xsl/modifications.xsl"/>
+<p>
+<cruisecontrol:xsl xslFile="/xsl/distributables.xsl"/>
+<p>
+<cruisecontrol:xsl xslFile="/xsl/breakdown.xsl"/>
diff --git a/groovy/cruise/reporting-app/checkstyle.xml b/groovy/cruise/reporting-app/checkstyle.xml
new file mode 100644
index 0000000..2d3bc69
--- /dev/null
+++ b/groovy/cruise/reporting-app/checkstyle.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC
+    "-//Puppy Crawl//DTD Check Configuration 1.1//EN"
+    "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+
+<module name="Checker">
+    <module name="Translation"/>   
+    <module name="TreeWalker">
+        <property name="cacheFile" value="target/checkstyle.cache"/>
+        <property name="tabWidth" value="4"/>
+
+        <!-- Naming conventions -->
+        <module name="ConstantName"/>
+        <module name="LocalFinalVariableName"/>
+        <module name="LocalVariableName"/>
+        <module name="MemberName"/>
+        <module name="MethodName"/>
+        <module name="PackageName">
+            <property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
+        </module>
+        <module name="ParameterName"/>
+        <module name="StaticVariableName"/>
+        <module name="TypeName"/>
+
+        <!-- Imports -->
+        <module name="AvoidStarImport"/>
+        <module name="IllegalImport"/>
+        <module name="RedundantImport"/>
+        <module name="UnusedImports"/>
+
+        <!-- Size violations -->
+        <module name="FileLength"/>
+        <module name="LineLength">
+            <property name="max" value="120"/>
+        </module>
+        <module name="MethodLength"/>
+        <module name="ParameterNumber"/>
+
+        <!-- Whitespace -->
+        <module name="NoWhitespaceAfter">
+            <property name="tokens"
+                value="BNOT, DEC, DOT, INC, LNOT, UNARY_MINUS, UNARY_PLUS"/>
+        </module>
+        <module name="NoWhitespaceBefore"/>
+        <module name="OperatorWrap"/>
+        <module name="ParenPad"/>
+        <module name="TabCharacter"/>
+        <module name="WhitespaceAfter"/>
+        <module name="WhitespaceAround"/>
+
+        <!-- Modifiers -->
+        <module name="ModifierOrder"/>
+
+        <!-- Blocks -->
+        <module name="EmptyBlock">
+            <property name="tokens"
+                value="LITERAL_DO, LITERAL_ELSE, LITERAL_FINALLY, LITERAL_FOR, LITERAL_TRY, LITERAL_WHILE, STATIC_INIT"/>
+        </module>
+        <module name="LeftCurly"/>
+        <module name="NeedBraces"/>
+        <module name="RightCurly"/>
+        <module name="AvoidNestedBlocks"/>
+
+        <!-- Checks for coding problems -->
+        <module name="DoubleCheckedLocking"/>
+        <module name="HiddenField">
+            <property name="tokens" value="VARIABLE_DEF"/>
+        </module>
+        <module name="InnerAssignment"/>
+        <module name="MissingSwitchDefault"/>
+        <module name="SimplifyBooleanExpression"/>
+        <module name="SimplifyBooleanReturn"/>
+        
+        <!-- Design checks -->
+        <module name="VisibilityModifier">
+            <property name="publicMemberPattern" value="^[a-zA-Z0-9]*$"/>
+        </module>
+        <module name="FinalClass"/>
+        <module name="InterfaceIsType"/>
+        <module name="HideUtilityClassConstructor"/>
+
+        <!-- Miscellaneous -->
+        <module name="UpperEll"/>
+        <module name="ArrayTypeStyle"/>        
+    </module>
+</module>
diff --git a/groovy/cruise/reporting-app/controlpanel.jsp b/groovy/cruise/reporting-app/controlpanel.jsp
new file mode 100644
index 0000000..04c7836
--- /dev/null
+++ b/groovy/cruise/reporting-app/controlpanel.jsp
@@ -0,0 +1,63 @@
+<%--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************--%>
+<%@ taglib uri="/WEB-INF/cruisecontrol-jsp11.tld" prefix="cruisecontrol"%>
+<%@page import="java.net.*,java.io.*"%>
+<%
+    String hostname = "";
+    try
+    {
+        hostname = InetAddress.getLocalHost().getHostName();
+    }
+    catch(IOException e)
+    {
+        hostname = "localhost";
+    }
+%>
+<p>
+<table width="600" align="center" cellpadding="0" cellspacing="0">
+    <tr>
+        <td align="center">
+            <h2>JMX Control Panel</h2>
+        </td>
+    </tr>
+    <tr>
+        <td>
+            <iframe name="controlPanelFrame" id="controlPanelFrame" height="520" marginheight="0" frameborder="1" marginwidth="0" src="http://build.canoo.com:37980" width="605"></iframe>
+        </td>
+    </tr>
+</table>
+</p>
diff --git a/groovy/cruise/reporting-app/cruisecontrol.header b/groovy/cruise/reporting-app/cruisecontrol.header
new file mode 100644
index 0000000..88205c9
--- /dev/null
+++ b/groovy/cruise/reporting-app/cruisecontrol.header
@@ -0,0 +1,36 @@
+/********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2003, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************/
diff --git a/groovy/cruise/reporting-app/css/cruisecontrol.css b/groovy/cruise/reporting-app/css/cruisecontrol.css
new file mode 100644
index 0000000..a07a9f5
--- /dev/null
+++ b/groovy/cruise/reporting-app/css/cruisecontrol.css
@@ -0,0 +1,153 @@
+.host {
+	BACKGROUND-POSITION: 0px -25px; FONT-SIZE: 80%; BACKGROUND-IMAGE: url(/groovy/images/yellowGreenBack.png); BACKGROUND-REPEAT: repeat-x; FONT-FAMILY: verdana, helvetica, arial, sans-serif; HEIGHT: 75px; BACKGROUND-COLOR: #ffffaa; TEXT-ALIGN: right
+}
+.canoo {
+	RIGHT: 20px; POSITION: absolute; TOP: 4px
+}
+.logo {
+    LEFT: 20px; POSITION: relative; TOP: -84px
+}
+
+.white {
+	COLOR: #ffffff
+}
+.navigation {
+    padding-top: 20px;
+    padding-left: 10px;
+	BACKGROUND-COLOR: #ffffaa
+}
+.index {
+	BACKGROUND-COLOR: #ffffff
+}
+.index-passed {
+	COLOR: #004400
+}
+.index-failed {
+	FONT-WEIGHT: bold; COLOR: #ff0000
+}
+.index-header {
+	FONT-WEIGHT: bold
+}
+.link {
+	FONT-SIZE: 10pt; COLOR: #336699; FONT-FAMILY: arial,helvetica,sans-serif; TEXT-DECORATION: none
+}
+.tab-table {
+	MARGIN: 0em 0em 0.5em
+}
+.tabs {
+	PADDING-RIGHT: 2em; PADDING-LEFT: 2em; FONT-WEIGHT: bold; FONT-SIZE: 8pt; PADDING-BOTTOM: 0em; COLOR: #000000; PADDING-TOP: 0em; FONT-FAMILY: arial,helvetica,sans-serif; BACKGROUND-COLOR: #ccccff
+}
+.tabs-link {
+	COLOR: #000000; TEXT-DECORATION: none
+}
+.tabs-link:visited {
+	COLOR: #000000; TEXT-DECORATION: none
+}
+.tabs-selected {
+	PADDING-RIGHT: 2em; PADDING-LEFT: 2em; FONT-WEIGHT: bold; FONT-SIZE: 8pt; PADDING-BOTTOM: 0em; COLOR: #000000; PADDING-TOP: 0em; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.tabs-selected {
+	BORDER-RIGHT: inset; BORDER-TOP: inset; BORDER-LEFT: inset; BORDER-BOTTOM: inset
+}
+.header-title {
+	FONT-WEIGHT: bold; FONT-SIZE: 12pt; COLOR: #336699; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.header-label {
+	FONT-WEIGHT: bold
+}
+.header-data {
+	FONT-SIZE: 10pt; COLOR: #336699; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.modifications-data {
+	FONT-SIZE: 8pt; COLOR: #000000; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.modifications-sectionheader {
+	FONT-SIZE: 10pt; COLOR: #336699; FONT-FAMILY: arial,helvetica,sans-serif; BACKGROUND-COLOR: #ffffaa
+}
+.modifications-oddrow {
+	BACKGROUND-COLOR: #ccccff
+}
+.modifications-evenrow {
+	BACKGROUND-COLOR: #ffffcc
+}
+.breakdown-sectionheader {
+	FONT-SIZE: 10pt; COLOR: #336699; FONT-FAMILY: arial,helvetica,sans-serif; BACKGROUND-COLOR: #ffffaa
+}
+.breakdown-data {
+	FONT-SIZE: 8pt; COLOR: #000000; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.breakdown-oddrow {
+	BACKGROUND-COLOR: #ccccff
+}
+.breakdown-evenrow {
+	BACKGROUND-COLOR: #ffffcc
+}
+.changelists-oddrow {
+	BACKGROUND-COLOR: #ccccff
+}
+.changelists-evenrow {
+	BACKGROUND-COLOR: #ffffcc
+}
+.changelists-file-spacer {
+	BACKGROUND-COLOR: #ffffff
+}
+.changelists-file-evenrow {
+	BACKGROUND-COLOR: #eeeeee
+}
+.changelists-file-oddrow {
+	BACKGROUND-COLOR: #ffffee
+}
+.changelists-file-header {
+	FONT-SIZE: 8pt; COLOR: #ffffff; FONT-FAMILY: arial,helvetica,sans-serif; BACKGROUND-COLOR: #666666
+}
+.compile-data {
+	FONT-SIZE: 8pt; COLOR: #000000; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.compile-error-data {
+	FONT-SIZE: 8pt; COLOR: #ff0000; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.compile-warn-data {
+	FONT-SIZE: 8pt; COLOR: #cc9900; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.compile-sectionheader {
+	FONT-SIZE: 10pt; COLOR: #336699; FONT-FAMILY: arial,helvetica,sans-serif; BACKGROUND-COLOR: #ffffaa
+}
+.distributables-data {
+	FONT-SIZE: 8pt; COLOR: #000000; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.distributables-sectionheader {
+	FONT-SIZE: 10pt; COLOR: #336699; FONT-FAMILY: arial,helvetica,sans-serif; BACKGROUND-COLOR: #ffffaa
+}
+.distributables-oddrow {
+	BACKGROUND-COLOR: #ccccff
+}
+.distributables-evenrow {
+	BACKGROUND-COLOR: #ffffcc
+}
+.unittests-sectionheader {
+	FONT-SIZE: 10pt; COLOR: #336699; FONT-FAMILY: arial,helvetica,sans-serif; BACKGROUND-COLOR: #ffffaa
+}
+.unittests-oddrow {
+	BACKGROUND-COLOR: #ccccff
+}
+.unittests-evenrow {
+	BACKGROUND-COLOR: #ffffcc
+}
+.unittests-data {
+	FONT-SIZE: 8pt; COLOR: #000000; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.unittests-error {
+	FONT-SIZE: 8pt; COLOR: #901090; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.unittests-failure {
+	FONT-SIZE: 8pt; COLOR: #ff0000; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.checkstyle-oddrow {
+	BACKGROUND-COLOR: #ccccff
+}
+.checkstyle-data {
+	FONT-SIZE: 8pt; COLOR: #000000; FONT-FAMILY: arial,helvetica,sans-serif
+}
+.checkstyle-sectionheader {
+	FONT-SIZE: 10pt; COLOR: #336699; FONT-FAMILY: arial,helvetica,sans-serif; BACKGROUND-COLOR: #ffffaa
+}
diff --git a/groovy/cruise/reporting-app/images/DukeGroovyChair.jpg b/groovy/cruise/reporting-app/images/DukeGroovyChair.jpg
new file mode 100644
index 0000000..7e0c2c5
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/DukeGroovyChair.jpg
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/backhead2.png b/groovy/cruise/reporting-app/images/backhead2.png
new file mode 100644
index 0000000..d8e06eb
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/backhead2.png
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/blank35.gif b/groovy/cruise/reporting-app/images/blank35.gif
new file mode 100644
index 0000000..d60dd2e
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/blank35.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/blank8.gif b/groovy/cruise/reporting-app/images/blank8.gif
new file mode 100644
index 0000000..9cbf371
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/blank8.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/bluebg.gif b/groovy/cruise/reporting-app/images/bluebg.gif
new file mode 100644
index 0000000..02711ce
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/bluebg.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/bluestripesbottom.gif b/groovy/cruise/reporting-app/images/bluestripesbottom.gif
new file mode 100644
index 0000000..6bbea1c
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/bluestripesbottom.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/bluestripestop.gif b/groovy/cruise/reporting-app/images/bluestripestop.gif
new file mode 100644
index 0000000..6bbea1c
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/bluestripestop.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/buildResultsTab-off.gif b/groovy/cruise/reporting-app/images/buildResultsTab-off.gif
new file mode 100644
index 0000000..77aa777
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/buildResultsTab-off.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/buildResultsTab-on.gif b/groovy/cruise/reporting-app/images/buildResultsTab-on.gif
new file mode 100644
index 0000000..6322f78
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/buildResultsTab-on.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/canoo_rgb_pos.gif b/groovy/cruise/reporting-app/images/canoo_rgb_pos.gif
new file mode 100644
index 0000000..2504f28
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/canoo_rgb_pos.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/continuousintegration.gif b/groovy/cruise/reporting-app/images/continuousintegration.gif
new file mode 100644
index 0000000..2040e5e
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/continuousintegration.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/controlPanelTab-off.gif b/groovy/cruise/reporting-app/images/controlPanelTab-off.gif
new file mode 100644
index 0000000..8a01d18
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/controlPanelTab-off.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/controlPanelTab-on.gif b/groovy/cruise/reporting-app/images/controlPanelTab-on.gif
new file mode 100644
index 0000000..25acd52
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/controlPanelTab-on.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/logo.gif b/groovy/cruise/reporting-app/images/logo.gif
new file mode 100644
index 0000000..79abe20
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/logo.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/testResultsTab-off.gif b/groovy/cruise/reporting-app/images/testResultsTab-off.gif
new file mode 100644
index 0000000..2c7b0da
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/testResultsTab-off.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/testResultsTab-on.gif b/groovy/cruise/reporting-app/images/testResultsTab-on.gif
new file mode 100644
index 0000000..563140e
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/testResultsTab-on.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/xmlLogFileTab-off.gif b/groovy/cruise/reporting-app/images/xmlLogFileTab-off.gif
new file mode 100644
index 0000000..0a4c8ed
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/xmlLogFileTab-off.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/xmlLogFileTab-on.gif b/groovy/cruise/reporting-app/images/xmlLogFileTab-on.gif
new file mode 100644
index 0000000..fec9104
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/xmlLogFileTab-on.gif
Binary files differ
diff --git a/groovy/cruise/reporting-app/images/yellowGreenBack.png b/groovy/cruise/reporting-app/images/yellowGreenBack.png
new file mode 100644
index 0000000..6b2395b
--- /dev/null
+++ b/groovy/cruise/reporting-app/images/yellowGreenBack.png
Binary files differ
diff --git a/groovy/cruise/reporting-app/index.jsp b/groovy/cruise/reporting-app/index.jsp
new file mode 100644
index 0000000..d2caeab
--- /dev/null
+++ b/groovy/cruise/reporting-app/index.jsp
@@ -0,0 +1,155 @@
+<%@ page import="java.io.File,
+                 java.util.Arrays,
+                 java.util.Calendar"%>
+ <%--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************--%>
+<jsp:useBean id="statusHelper" scope="page" class="net.sourceforge.cruisecontrol.StatusHelper" />
+<%
+    String singleProjectMode = application.getInitParameter("singleProject");
+    if (Boolean.valueOf(singleProjectMode).booleanValue()) {
+       %><jsp:forward page="buildresults" /><%
+        return;
+    }
+
+    StringBuffer reportTime = new StringBuffer();
+    Calendar now = Calendar.getInstance();
+    reportTime.append(now.get(Calendar.HOUR_OF_DAY));
+    reportTime.append(":");
+    String minutes = String.valueOf(now.get(Calendar.MINUTE));
+    if (minutes.length() == 1) {
+        minutes = 0 + minutes;
+    }
+    reportTime.append(minutes);
+
+    boolean autoRefresh = "true".equals(request.getParameter("auto_refresh"));
+%>
+<html>
+<head>
+  <title>CruiseControl Status Page</title>
+  <base href="<%=request.getScheme()%>://<%=request.getServerName()%>:<%=request.getServerPort()%><%=request.getContextPath()%>/" />
+  <link type="text/css" rel="stylesheet" href="css/cruisecontrol.css"/>
+  <%
+     if (autoRefresh) { 
+  %>
+  <META HTTP-EQUIV="Refresh" CONTENT="10">
+  <%
+     }
+  %>
+</head>
+<body background="images/bluebg.gif" topmargin="0" leftmargin="0" marginheight="0" marginwidth="0">
+<p>&nbsp;</p>
+
+<h1 class="white" align="center">CruiseControl Status Page</h1>
+
+<table align="center" border="0" cellpadding="0" cellspacing="0" width="70%">
+<tfoot>
+  <tr><td class="link">listing generated at <%=reportTime.toString()%></td></tr>
+</tfoot>
+<tbody>
+<tr><td align="right">
+  <%
+     if (autoRefresh) {
+  %>
+    <a class="white" href="?auto_refresh=false">Turn autorefresh off</a>
+  <%
+     } else {
+  %>
+    <a class="white" href="?auto_refresh=true">Turn autorefresh on</a>
+  <%
+     }
+  %>
+  </td></tr>
+  <tr><td>&nbsp;</td></tr>
+  <tr><td bgcolor="#FFFFFF"><img border="0" src="images/bluestripestop.gif"></td></tr>
+  <tr><td><table class="index" width="100%">
+<%
+   String logDirPath = application.getInitParameter("logDir");
+   if (logDirPath == null) {
+       %><tr><td>You need to provide a value for the context parameter <code>&quot;logDir&quot;</code></td></tr><%
+   } else {
+       java.io.File logDir = new java.io.File(logDirPath);
+       if (logDir.isDirectory() == false) {
+           %><tr><td>Context parameter logDir needs to be set to a directory. Currently set to &quot;<%=logDirPath%>&quot;</td></tr><%
+       } else {
+           String[] projectDirs = logDir.list(new java.io.FilenameFilter() {
+               public boolean accept(File dir, String name) {
+                   return (new File(dir, name).isDirectory());
+               }
+           });
+
+           if (projectDirs.length == 0) {
+               %><tr><td>no project directories found under <%=logDirPath%></td></tr><%
+           }
+           else {
+%>    <thead class="index-header">
+      <tr>
+        <td>Project</td>
+        <td align="center">Last build result</td>
+        <td align="center">Last build time</td>
+        <td align="center">Last successful build time</td>
+        <td align="center">Last label</td>
+    </tr>
+    </thead>
+    <tbody>
+ <%
+               Arrays.sort(projectDirs);
+             for (int i = 0; i < projectDirs.length; i++) {
+                   String project = projectDirs[i];
+                   File projectDir = new File(logDir, project);
+                   statusHelper.setProjectDirectory(projectDir);
+                 final String result = statusHelper.getLastBuildResult();
+         %>        <tr><td><a href="buildresults/<%=project%>"><%=project%></a></td><%
+                 %><td class="index-<%=result%>" align="center"><%=result%></td><%
+                 %><td align="center"><%=statusHelper.getLastBuildTimeString(request.getLocale())%></td><%
+                 %><td align="center"><%=statusHelper.getLastSuccessfulBuildTimeString(request.getLocale())%></td><%
+                 %><td><%=statusHelper.getLastSuccessfulBuildLabel()%></td>
+                   </tr>
+ <%
+               }
+         %>    </tbody>
+<%
+           }
+       }
+   }
+%></table></td></tr>
+  <tr><td bgcolor="#FFFFFF"><img border="0" src="images/bluestripesbottom.gif"></td></tr>
+  <tr><td>&nbsp;</td></tr>
+</tbody>
+</table>
+</body>
+</html>
+
diff --git a/groovy/cruise/reporting-app/main.jsp b/groovy/cruise/reporting-app/main.jsp
new file mode 100644
index 0000000..6cdbdde
--- /dev/null
+++ b/groovy/cruise/reporting-app/main.jsp
@@ -0,0 +1,85 @@
+<%--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************--%>
+<%@page contentType="text/html"%>
+<%@ taglib uri="/WEB-INF/cruisecontrol-jsp11.tld" prefix="cruisecontrol"%>
+<html>
+<head>
+  <title>groovy: CruiseControl Build Results</title>
+  <base href="<%=request.getScheme()%>://<%=request.getServerName()%>:<%=request.getServerPort()%><%=request.getContextPath()%>/" />
+  <link type="text/css" rel="stylesheet" href="css/cruisecontrol.css"/>
+</head>
+<body topmargin="0" leftmargin="0" marginheight="0" marginwidth="0">
+  <table border="0" align="center" cellpadding="0" cellspacing="0" width="100%">
+    <tr><td colspan="2" class="host">
+        <div class="canoo">hosted by<br><a href="http://www.canoo.com"><img border="0" height="20" width="112" src="images/canoo_rgb_pos.gif"></a>
+        </div></td></tr>
+    <tr>
+      <td valign="top" class="navigation">
+        <%@ include file="navigation.jsp" %>
+      </td>
+      <td valign="top">
+        &nbsp;<br>
+        <cruisecontrol:tabsheet>
+          <tr>
+            <td bgcolor="white" >
+              <cruisecontrol:tab name="buildResults" label="Build Results" >
+                <%@ include file="buildresults.jsp" %>
+              </cruisecontrol:tab>
+
+              <cruisecontrol:tab name="testResults" label="Test Results" >
+                <%@ include file="testdetails.jsp" %>
+              </cruisecontrol:tab>
+
+              <cruisecontrol:tab name="xmlLogFile" label="XML Log File" >
+                <%@ include file="xmllog.jsp" %>
+              </cruisecontrol:tab>
+
+              <cruisecontrol:tab name="metrics" label="Metrics" >
+                <%@ include file="metrics.jsp" %>
+              </cruisecontrol:tab>
+
+              <cruisecontrol:tab name="controlPanel" label="Control Panel" >
+                <%@ include file="controlpanel.jsp" %>
+              </cruisecontrol:tab>
+            </td>
+          </tr>
+        </cruisecontrol:tabsheet>
+      </td>
+    </tr>
+  </table>
+</body>
+</html>
diff --git a/groovy/cruise/reporting-app/metrics.jsp b/groovy/cruise/reporting-app/metrics.jsp
new file mode 100644
index 0000000..d1f531f
--- /dev/null
+++ b/groovy/cruise/reporting-app/metrics.jsp
@@ -0,0 +1,35 @@
+<%@page import="net.sourceforge.cruisecontrol.*, net.sourceforge.cruisecontrol.chart.*"%>
+<%@taglib uri='WEB-INF/lib/cewolf.jar' prefix='cewolf' %>
+<%@ taglib uri="/WEB-INF/cruisecontrol-jsp11.tld" prefix="cruisecontrol"%>
+
+<cruisecontrol:buildInfo />
+
+<table>
+  <tr><td>Number of Build Attempts</td><td><%=build_info.size() %></td></tr>
+  <tr><td>Number of Broken Builds</td><td><%=build_info.getNumBrokenBuilds() %></td></tr>
+  <tr><td>Number of Successful Builds</td><td><%=build_info.getNumSuccessfulBuilds() %></td></tr>
+</table>
+
+<hr />
+<jsp:useBean id="pieData" class="net.sourceforge.cruisecontrol.chart.PieChartData" />
+<cewolf:chart id="pie" title="Breakdown of build types" type="pie" >
+    <cewolf:data>
+        <cewolf:producer id="pieData">
+          <cewolf:param name="buildInfo" value="<%=build_info%>" />
+        </cewolf:producer>
+    </cewolf:data>
+</cewolf:chart>
+<cewolf:img chartid="pie" renderer="cewolf" width="400" height="300"/>
+
+<hr />
+<jsp:useBean id="chartData" class="net.sourceforge.cruisecontrol.chart.TimeChartData" />
+<cewolf:chart id="chart" title="Breakdown of build types" type="timeseries"  xaxislabel="date" yaxislabel="time">
+    <cewolf:data>
+        <cewolf:producer id="chartData">
+          <cewolf:param name="buildInfo" value="<%=build_info%>" />
+        </cewolf:producer>
+    </cewolf:data>
+    <cewolf:chartpostprocessor id="chartData" />
+</cewolf:chart>
+<cewolf:img chartid="chart" renderer="cewolf" width="400" height="300"/>
+
diff --git a/groovy/cruise/reporting-app/navigation.jsp b/groovy/cruise/reporting-app/navigation.jsp
new file mode 100644
index 0000000..75817a1
--- /dev/null
+++ b/groovy/cruise/reporting-app/navigation.jsp
@@ -0,0 +1,57 @@
+<%--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************--%>
+<%@ taglib uri="/WEB-INF/cruisecontrol-jsp11.tld" prefix="cruisecontrol"%>
+        
+        <DIV class=logo><h1><a href="http://build.canoo.com/groovy" border="0">groovy</a></h1><div><p>
+        <table border="0" align="center" width="98%">
+            <tr><td><cruisecontrol:currentbuildstatus/></td></tr>
+            <tr><td>&nbsp;</td></tr>
+            <cruisecontrol:link id="baseUrl" />
+            <tr><td><a class="link" href="<%=baseUrl%>">Latest Build</a></td></tr>
+            <cruisecontrol:nav startingBuildNumber="0" finalBuildNumber="10" >
+                <tr><td><a class="link" href="<%= url %>"><%= linktext %></a></td></tr>
+            </cruisecontrol:nav>
+            <tr><td>
+              <form method="GET" action="<%=baseUrl%>" >
+                <select name="log" onchange="form.submit()">
+                  <cruisecontrol:nav startingBuildNumber="10">
+                    <option value="<%=logfile%>"><%= linktext %></option>
+                  </cruisecontrol:nav>
+                </select>
+              </form>
+            </td></tr>
+        </table>
diff --git a/groovy/cruise/reporting-app/testdetails.jsp b/groovy/cruise/reporting-app/testdetails.jsp
new file mode 100644
index 0000000..9e68af6
--- /dev/null
+++ b/groovy/cruise/reporting-app/testdetails.jsp
@@ -0,0 +1,39 @@
+<%--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************--%>
+<%@ taglib uri="/WEB-INF/cruisecontrol-jsp11.tld" prefix="cruisecontrol"%>
+
+<cruisecontrol:xsl xslFile="/xsl/testdetails.xsl"/>
diff --git a/groovy/cruise/reporting-app/xmllog.jsp b/groovy/cruise/reporting-app/xmllog.jsp
new file mode 100644
index 0000000..626fe91
--- /dev/null
+++ b/groovy/cruise/reporting-app/xmllog.jsp
@@ -0,0 +1,38 @@
+<%--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************--%>
+<%@ taglib uri="/WEB-INF/cruisecontrol-jsp11.tld" prefix="cruisecontrol"%>
+<pre class="modifications-data"><cruisecontrol:xsl xslFile="/xsl/logfile.xsl"/></pre>
diff --git a/groovy/cruise/reporting-app/xsl/breakdown.xsl b/groovy/cruise/reporting-app/xsl/breakdown.xsl
new file mode 100644
index 0000000..d1daaf4
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/breakdown.xsl
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<!--
+ * Copyright (c) 2007, ASERT Consulting Pty Ltd
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ASERT nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *-->
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+        xmlns="http://www.w3.org/TR/html4/strict.dtd">
+
+    <xsl:output method="html"/>
+
+    <xsl:variable name="longtasks" select="/cruisecontrol/build//target[contains(@time,'minute') or number(substring-before(@time,' ')) > 30]"/>
+
+    <xsl:template match="/">
+        <table align="center" cellpadding="2" cellspacing="0" border="0" width="98%">
+            <tr>
+                <td class="breakdown-sectionheader">
+                    &#160;Target Time Breakdown
+                </td>
+            </tr>
+            <tr><td class="breakdown-data">
+                <ul>
+                    <xsl:for-each select="$longtasks">
+                        <li style="padding-left:{count(ancestor::target)*20}px;">
+                            <xsl:value-of select="concat(@name, ' (', @time, ')')"/>
+                        </li>
+                    </xsl:for-each>
+                </ul>
+            </td></tr>
+        </table>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/cruise/reporting-app/xsl/changelists/header.xsl b/groovy/cruise/reporting-app/xsl/changelists/header.xsl
new file mode 100644
index 0000000..8fb61b5
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/changelists/header.xsl
@@ -0,0 +1,84 @@
+<?xml version="1.0"?>
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt">
+
+    <xsl:output method="html"/>
+
+    <xsl:template match="/">
+        <table align="center" cellpadding="2" cellspacing="0" border="0" width="98%">
+
+            <xsl:if test="cruisecontrol/build/@error">
+                <tr><td class="header-title">BUILD FAILED</td></tr>
+                <tr><td class="header-data">
+                    <span class="header-label">Error Message:&#160;</span>
+                    <xsl:value-of select="cruisecontrol/build/@error"/>
+                </td></tr>
+            </xsl:if>
+
+            <xsl:if test="not (cruisecontrol/build/@error)">
+                <tr><td class="header-title">BUILD COMPLETE&#160;-&#160;
+                    <xsl:value-of select="cruisecontrol/info/property[@name='label']/@value"/>
+                </td></tr>
+            </xsl:if>
+
+            <tr><td class="header-data">
+                <span class="header-label">Date of build:&#160;</span>
+                <xsl:value-of select="cruisecontrol/info/property[@name='lastbuild']/@value"/>
+            </td></tr>
+            <tr><td class="header-data">
+                <span class="header-label">Time to build:&#160;</span>
+                <xsl:value-of select="cruisecontrol/build/@time"/>
+            </td></tr>
+            <tr>
+                <td class="header-data">
+                    <span class="header-label">Last changed:&#160;</span>
+                    <xsl:value-of select="cruisecontrol/modifications/changelist/@dateOfSubmission"/>
+                </td>
+            </tr>
+            <tr>
+                <td class="header-data">
+                    <span class="header-label">Last log entry:&#160;</span>
+                    <xsl:value-of select="cruisecontrol/modifications/changelist/description"/>
+                </td>
+            </tr>
+        </table>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/cruise/reporting-app/xsl/checkstyle.xsl b/groovy/cruise/reporting-app/xsl/checkstyle.xsl
new file mode 100644
index 0000000..f0dac4b
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/checkstyle.xsl
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+    <xsl:output method="html"/>
+
+    <xsl:template match="/">
+        <xsl:apply-templates select="cruisecontrol/checkstyle"/>
+    </xsl:template>
+
+    <xsl:template match="checkstyle[file/error]">
+        <xsl:variable name="file.error.count" select="count(file[error])" />
+        <xsl:variable name="total.error.count" select="count(file/error)" />
+        <table align="center" cellpadding="2" cellspacing="0" border="0" width="98%">
+          <tr>
+            <td class="checkstyle-sectionheader" colspan="3">
+                Checkstyle errors (<xsl:value-of select="$total.error.count" />)
+            </td>
+          </tr>
+          <xsl:for-each select="file/error" >
+            <tr>
+              <xsl:if test="position() mod 2 = 1">
+                <xsl:attribute name="class">checkstyle-oddrow</xsl:attribute>
+              </xsl:if>
+              <td class="checkstyle-data"><xsl:value-of select="../@name" /></td>
+              <td class="checkstyle-data"><xsl:value-of select="@line" /></td>
+              <td class="checkstyle-data"><xsl:value-of select="@message" /></td>
+            </tr>
+          </xsl:for-each>
+        </table>
+    </xsl:template>
+
+<!--    <xsl:template match="*|@*|text()" />-->
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/groovy/cruise/reporting-app/xsl/compile.xsl b/groovy/cruise/reporting-app/xsl/compile.xsl
new file mode 100644
index 0000000..a402a0e
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/compile.xsl
@@ -0,0 +1,119 @@
+<?xml version="1.0"?>
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<xsl:stylesheet
+    version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns="http://www.w3.org/TR/html4/strict.dtd" >
+
+    <xsl:output method="html"/>
+
+    <xsl:variable name="tasklist" select="/cruisecontrol/build//target/task"/>
+    <xsl:variable name="javac.tasklist" select="$tasklist[@name='Javac'] | $tasklist[@name='javac'] | $tasklist[@name='compilewithwalls']"/>
+    <xsl:variable name="ejbjar.tasklist" select="$tasklist[@name='EjbJar'] | $tasklist[@name='ejbjar']"/>
+
+    <xsl:template match="/">
+
+        <xsl:variable name="javac.error.messages" select="$javac.tasklist/message[@priority='error'][text() != '']"/>
+        <xsl:variable name="javac.warn.messages" select="$javac.tasklist/message[@priority='warn'][text() != '']"/>
+        <xsl:variable name="ejbjar.error.messages" select="$ejbjar.tasklist/message[@priority='error'][text() != '']"/>
+        <xsl:variable name="ejbjar.warn.messages" select="$ejbjar.tasklist/message[@priority='warn'][text() != '']"/>
+        <xsl:variable name="total.errorMessage.count" select="count($javac.warn.messages) + count($ejbjar.warn.messages) + count($javac.error.messages) + count($ejbjar.error.messages)"/>
+
+        <xsl:if test="$total.errorMessage.count > 0">
+            <table align="center" cellpadding="2" cellspacing="0" border="0" width="98%">
+                <tr>
+                    <!-- NOTE: total.errorMessage.count is actually the number of lines of error
+                     messages. This accurately represents the number of errors ONLY if the Ant property
+                     build.compiler.emacs is set to "true" -->
+                    <td class="compile-sectionheader">
+                        &#160;Errors/Warnings: (<xsl:value-of select="$total.errorMessage.count"/>)
+                    </td>
+                </tr>
+                <xsl:if test="count($javac.error.messages) > 0">
+                    <tr>
+                        <td>
+                           <pre class="compile-error-data">
+                            <xsl:apply-templates select="$javac.error.messages"/>
+                           </pre>
+                        </td>
+                    </tr>
+                </xsl:if>
+                <xsl:if test="count($javac.warn.messages) > 0">
+                    <tr>
+                        <td>
+                           <pre class="compile-data">
+                            <xsl:apply-templates select="$javac.warn.messages"/>
+                           </pre>
+                        </td>
+                    </tr>
+                </xsl:if>
+                <xsl:if test="count($ejbjar.error.messages) > 0">
+                    <tr>
+                        <td>
+                           <pre class="compile-error-data">
+                            <xsl:apply-templates select="$ejbjar.error.messages"/>
+                           </pre>
+                        </td>
+                    </tr>
+                </xsl:if>
+                <xsl:if test="count($ejbjar.warn.messages) > 0">
+                    <tr>
+                        <td>
+                           <pre class="compile-warn-data">
+                            <xsl:apply-templates select="$ejbjar.warn.messages"/>
+                           </pre>
+                        </td>
+                    </tr>
+                </xsl:if>
+            </table>
+        </xsl:if>
+
+    </xsl:template>
+
+    <xsl:template match="message[@priority='error']">
+        <xsl:value-of select="text()"/>
+        <xsl:if test="count(./../message[@priority='error']) != position()">
+            <br class="none"/>
+        </xsl:if>
+    </xsl:template>
+
+    <xsl:template match="message[@priority='warn']">
+        <xsl:value-of select="text()"/><br class="none"/>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/cruise/reporting-app/xsl/distributables.xsl b/groovy/cruise/reporting-app/xsl/distributables.xsl
new file mode 100644
index 0000000..abc998c
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/distributables.xsl
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt">
+
+    <xsl:output method="html"/>
+    <xsl:variable name="tasklist" select="/cruisecontrol/build//target/task"/>
+    <xsl:variable name="jar.tasklist" select="$tasklist[@name='Jar']/message[@priority='info'] | $tasklist[@name='jar']/message[@priority='info'] | $tasklist[@name='zip']/message[@priority='info'] | $tasklist[@name='jarjar']/message[@priority='info']"/>
+    <xsl:variable name="war.tasklist" select="$tasklist[@name='War']/message[@priority='info'] | $tasklist[@name='war']/message[@priority='info']"/>
+    <xsl:variable name="ejbjar.tasklist" select="$tasklist[@name='ejbjar']/message[@priority='info']"/>
+    <xsl:variable name="ear.tasklist" select="$tasklist[@name='ear']/message[@priority='info']"/>
+    <xsl:variable name="dist.count" select="count($jar.tasklist) + count($war.tasklist) + count($ejbjar.tasklist) + count($ear.tasklist)"/>
+
+    <xsl:template match="/">
+        <table align="center" cellpadding="2" cellspacing="0" border="0" width="98%">
+
+            <xsl:if test="$dist.count > 0">
+                <tr>
+                    <td class="distributables-sectionheader">
+                        &#160;Deployments by this build:&#160;(<xsl:value-of select="$dist.count"/>)
+                    </td>
+                </tr>
+                <xsl:apply-templates select="$jar.tasklist | $war.tasklist | $ejbjar.tasklist | $ear.tasklist" />
+            </xsl:if>
+
+        </table>
+    </xsl:template>
+
+    <xsl:template match="task[@name='Jar']/message[@priority='info'] | task[@name='War']/message[@priority='info'] | task[@name='jar']/message[@priority='info'] | task[@name='zip']/message[@priority='info'] | task[@name='jarjar']/message[@priority='info'] | task[@name='war']/message[@priority='info'] | task[@name='ejbjar']/message[@priority='info'] | task[@name='ear']/message[@priority='info']">
+        <tr>
+            <xsl:if test="position() mod 2 = 0">
+                <xsl:attribute name="class">distributables-evenrow</xsl:attribute>
+            </xsl:if>
+            <xsl:if test="position() mod 2 != 0">
+                <xsl:attribute name="class">distributables-oddrow</xsl:attribute>
+            </xsl:if>
+            <td class="distributables-data">
+                <xsl:value-of select="text()"/>
+            </td>
+        </tr>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/cruise/reporting-app/xsl/header.xsl b/groovy/cruise/reporting-app/xsl/header.xsl
new file mode 100644
index 0000000..585a0b8
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/header.xsl
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt">
+
+    <xsl:output method="html"/>
+
+    <xsl:template match="/">
+        <xsl:variable name="modification.list" select="cruisecontrol/modifications/modification"/>
+
+        <table align="center" cellpadding="2" cellspacing="0" border="0" width="98%">
+
+            <xsl:if test="cruisecontrol/build/@error">
+                <tr><td class="header-title">BUILD FAILED</td></tr>
+                <tr><td class="header-data">
+                    <span class="header-label">Ant Error Message:&#160;</span>
+                    <xsl:value-of select="cruisecontrol/build/@error"/>
+                </td></tr>
+            </xsl:if>
+
+            <xsl:if test="not (cruisecontrol/build/@error)">
+                <tr><td class="header-title">BUILD COMPLETE&#160;-&#160;
+                    <xsl:value-of select="cruisecontrol/info/property[@name='label']/@value"/>
+                </td></tr>
+            </xsl:if>
+
+            <tr><td class="header-data">
+                <span class="header-label">Date of build:&#160;</span>
+                <xsl:value-of select="cruisecontrol/info/property[@name='builddate']/@value"/>
+            </td></tr>
+            <tr><td class="header-data">
+                <span class="header-label">Time to build:&#160;</span>
+                <xsl:value-of select="cruisecontrol/build/@time"/>
+            </td></tr>
+            <xsl:apply-templates select="$modification.list">
+                <xsl:sort select="date" order="descending" data-type="text" />
+            </xsl:apply-templates>
+        </table>
+    </xsl:template>
+
+    <!-- Last Modification template -->
+    <xsl:template match="modification">
+        <xsl:if test="position() = 1">
+            <tr><td class="header-data">
+                <span class="header-label">Last changed:&#160;</span>
+                <xsl:value-of select="date"/>
+            </td></tr>
+            <tr><td class="header-data">
+                <span class="header-label">Last log entry:&#160;</span>
+                <xsl:value-of select="comment"/>
+            </td></tr>
+        </xsl:if>
+    </xsl:template>
+</xsl:stylesheet>
diff --git a/groovy/cruise/reporting-app/xsl/javadoc.xsl b/groovy/cruise/reporting-app/xsl/javadoc.xsl
new file mode 100644
index 0000000..7104af9
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/javadoc.xsl
@@ -0,0 +1,98 @@
+<?xml version="1.0"?>
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<xsl:stylesheet
+    version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns="http://www.w3.org/TR/html4/strict.dtd" >
+
+    <xsl:output method="html"/>
+
+    <xsl:variable name="tasklist" select="/cruisecontrol/build//target/task"/>
+    <xsl:variable name="javadoc.tasklist" select="$tasklist[@name='Javadoc'] | $tasklist[@name='javadoc']"/>
+
+    <xsl:template match="/">
+
+        <xsl:variable name="javadoc.error.messages" select="$javadoc.tasklist/message[@priority='error']"/>
+        <xsl:variable name="javadoc.warn.messages" select="$javadoc.tasklist/message[@priority='warn'][not(contains(text(), 'The first sentence is interpreted to be:'))]"/>
+        <xsl:variable name="total.errorMessage.count" select="count($javadoc.warn.messages)  + count($javadoc.error.messages)"/>
+
+        <xsl:if test="$total.errorMessage.count > 0">
+            <table align="center" cellpadding="2" cellspacing="0" border="0" width="98%">
+                <tr>
+                    <!-- NOTE: total.errorMessage.count is actually the number of lines of error
+                     messages. This accurately represents the number of errors ONLY if the Ant property
+                     build.compiler.emacs is set to "true" -->
+                    <td class="compile-sectionheader">
+                        &#160;Javadoc Errors/Warnings: (<xsl:value-of select="$total.errorMessage.count"/>)
+                    </td>
+                </tr>
+                <xsl:if test="count($javadoc.error.messages) > 0">
+                    <tr>
+                        <td>
+                           <pre class="compile-error-data">
+                            <xsl:apply-templates select="$javadoc.error.messages"/>
+                           </pre>
+                        </td>
+                    </tr>
+                </xsl:if>
+                <xsl:if test="count($javadoc.warn.messages) > 0">
+                    <tr>
+                        <td>
+                           <pre class="compile-data">
+                            <xsl:apply-templates select="$javadoc.warn.messages"/>
+                           </pre>
+                        </td>
+                    </tr>
+                </xsl:if>
+            </table>
+        </xsl:if>
+
+    </xsl:template>
+
+    <xsl:template match="message[@priority='error']">
+        <xsl:value-of select="text()"/>
+        <xsl:if test="count(./../message[@priority='error']) != position()">
+            <br/>
+        </xsl:if>
+    </xsl:template>
+
+    <xsl:template match="message[@priority='warn']">
+        <xsl:value-of select="text()"/><br/>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/cruise/reporting-app/xsl/logfile.xsl b/groovy/cruise/reporting-app/xsl/logfile.xsl
new file mode 100644
index 0000000..d97708b
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/logfile.xsl
@@ -0,0 +1,313 @@
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<xsl:stylesheet version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+  <xsl:output omit-xml-declaration="yes" method="html"/>
+
+  <xsl:param name="use-empty-syntax" select="true()"/>
+  <xsl:param name="exclude-unused-prefixes" select="true()"/>
+
+  <xsl:param name="start-tag-start"     select="'&lt;'"/>
+  <xsl:param name="start-tag-end"       select="'>'"/>
+  <xsl:param name="empty-tag-end"       select="'/>'"/>
+  <xsl:param name="end-tag-start"       select="'&lt;/'"/>
+  <xsl:param name="end-tag-end"         select="'>'"/>
+  <xsl:param name="space"               select="' '"/>
+  <xsl:param name="ns-decl"             select="'xmlns'"/>
+  <xsl:param name="colon"               select="':'"/>
+  <xsl:param name="equals"              select="'='"/>
+  <xsl:param name="attribute-delimiter" select="'&quot;'"/>
+  <xsl:param name="comment-start"       select="'&lt;!--'"/>
+  <xsl:param name="comment-end"         select="'-->'"/>
+  <xsl:param name="pi-start"            select="'&lt;?'"/>
+  <xsl:param name="pi-end"              select="'?>'"/>
+
+  <xsl:template name="xml-to-string">
+    <xsl:param name="node-set" select="."/>
+    <xsl:apply-templates select="$node-set" mode="xml-to-string">
+      <xsl:with-param name="depth" select="1"/>
+    </xsl:apply-templates>
+  </xsl:template>
+
+  <xsl:template match="/" name="xml-to-string-root-rule">
+    <xsl:call-template name="xml-to-string"/>
+  </xsl:template>
+
+  <xsl:template match="/" mode="xml-to-string">
+    <xsl:param name="depth"/>
+    <xsl:apply-templates mode="xml-to-string">
+      <xsl:with-param name="depth" select="$depth"/>
+    </xsl:apply-templates>
+  </xsl:template>
+
+  <xsl:template match="*" mode="xml-to-string">
+    <xsl:param name="depth"/>
+    <xsl:variable name="element" select="."/>
+    <xsl:value-of select="$start-tag-start"/>
+    <xsl:call-template name="element-name">
+      <xsl:with-param name="text" select="name()"/>
+    </xsl:call-template>
+    <xsl:apply-templates select="@*" mode="xml-to-string"/>
+    <xsl:for-each select="namespace::*">
+      <xsl:call-template name="process-namespace-node">
+        <xsl:with-param name="element" select="$element"/>
+        <xsl:with-param name="depth" select="$depth"/>
+      </xsl:call-template>
+    </xsl:for-each>
+    <xsl:choose>
+      <xsl:when test="node() or not($use-empty-syntax)">
+        <xsl:value-of select="$start-tag-end"/>
+        <xsl:apply-templates mode="xml-to-string">
+          <xsl:with-param name="depth" select="$depth + 1"/>
+        </xsl:apply-templates>
+        <xsl:value-of select="$end-tag-start"/>
+        <xsl:call-template name="element-name">
+          <xsl:with-param name="text" select="name()"/>
+        </xsl:call-template>
+        <xsl:value-of select="$end-tag-end"/>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$empty-tag-end"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template name="process-namespace-node">
+    <xsl:param name="element"/>
+    <xsl:param name="depth"/>
+    <xsl:variable name="declaredAbove">
+      <xsl:call-template name="isDeclaredAbove">
+        <xsl:with-param name="depth" select="$depth - 1"/>
+        <xsl:with-param name="element" select="$element/.."/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:if test="(not($exclude-unused-prefixes) or ($element | $element//@* | $element//*)[namespace-uri()=current()]) and not(string($declaredAbove)) and name()!='xml'">
+      <xsl:value-of select="$space"/>
+      <xsl:value-of select="$ns-decl"/>
+      <xsl:if test="name()">
+        <xsl:value-of select="$colon"/>
+        <xsl:call-template name="ns-prefix">
+          <xsl:with-param name="text" select="name()"/>
+        </xsl:call-template>
+      </xsl:if>
+      <xsl:value-of select="$equals"/>
+      <xsl:value-of select="$attribute-delimiter"/>
+      <xsl:call-template name="ns-uri">
+        <xsl:with-param name="text" select="string(.)"/>
+      </xsl:call-template>
+      <xsl:value-of select="$attribute-delimiter"/>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template name="isDeclaredAbove">
+    <xsl:param name="element"/>
+    <xsl:param name="depth"/>
+    <xsl:if test="$depth > 0">
+      <xsl:choose>
+        <xsl:when test="$element/namespace::*[name(.)=name(current()) and .=current()]">1</xsl:when>
+        <xsl:when test="$element/namespace::*[name(.)=name(current())]"/>
+        <xsl:otherwise>
+          <xsl:call-template name="isDeclaredAbove">
+            <xsl:with-param name="depth" select="$depth - 1"/>
+            <xsl:with-param name="element" select="$element/.."/>
+          </xsl:call-template>
+        </xsl:otherwise>
+      </xsl:choose>
+    </xsl:if>
+  </xsl:template>
+
+  <xsl:template match="@*" mode="xml-to-string">
+    <xsl:value-of select="$space"/>
+    <xsl:call-template name="attribute-name">
+      <xsl:with-param name="text" select="name()"/>
+    </xsl:call-template>
+    <xsl:value-of select="$equals"/>
+    <xsl:value-of select="$attribute-delimiter"/>
+    <xsl:call-template name="attribute-value">
+      <xsl:with-param name="text" select="string(.)"/>
+    </xsl:call-template>
+    <xsl:value-of select="$attribute-delimiter"/>
+  </xsl:template>
+
+  <xsl:template match="comment()" mode="xml-to-string">
+    <xsl:value-of select="$comment-start"/>
+    <xsl:call-template name="comment-text">
+      <xsl:with-param name="text" select="string(.)"/>
+    </xsl:call-template>
+    <xsl:value-of select="$comment-end"/>
+  </xsl:template>
+
+  <xsl:template match="processing-instruction()" mode="xml-to-string">
+    <xsl:value-of select="$pi-start"/>
+    <xsl:call-template name="pi-target">
+      <xsl:with-param name="text" select="name()"/>
+    </xsl:call-template>
+    <xsl:value-of select="$space"/>
+    <xsl:call-template name="pi-text">
+      <xsl:with-param name="text" select="string(.)"/>
+    </xsl:call-template>
+    <xsl:value-of select="$pi-end"/>
+  </xsl:template>
+
+  <xsl:template match="text()" mode="xml-to-string">
+    <xsl:call-template name="text-content">
+      <xsl:with-param name="text" select="string(.)"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template name="element-name">
+    <xsl:param name="text"/>
+    <xsl:value-of select="$text"/>
+  </xsl:template>
+
+  <xsl:template name="attribute-name">
+    <xsl:param name="text"/>
+    <xsl:value-of select="$text"/>
+  </xsl:template>
+
+  <xsl:template name="attribute-value">
+    <xsl:param name="text"/>
+    <xsl:variable name="escaped-markup">
+      <xsl:call-template name="escape-markup-characters">
+        <xsl:with-param name="text" select="$text"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:choose>
+      <xsl:when test="$attribute-delimiter = &quot;'&quot;">
+        <xsl:call-template name="replace-string">
+          <xsl:with-param name="text" select="$escaped-markup"/>
+          <xsl:with-param name="replace" select="&quot;'&quot;"/>
+          <xsl:with-param name="with" select="'&amp;apos;'"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:when test="$attribute-delimiter = '&quot;'">
+        <xsl:call-template name="replace-string">
+          <xsl:with-param name="text" select="$escaped-markup"/>
+          <xsl:with-param name="replace" select="'&quot;'"/>
+          <xsl:with-param name="with" select="'&amp;quot;'"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:call-template name="replace-string">
+          <xsl:with-param name="text" select="$escaped-markup"/>
+          <xsl:with-param name="replace" select="$attribute-delimiter"/>
+          <xsl:with-param name="with" select="''"/>
+        </xsl:call-template>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+  <xsl:template name="ns-prefix">
+    <xsl:param name="text"/>
+    <xsl:value-of select="$text"/>
+  </xsl:template>
+
+  <xsl:template name="ns-uri">
+    <xsl:param name="text"/>
+    <xsl:call-template name="attribute-value">
+      <xsl:with-param name="text" select="$text"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template name="text-content">
+    <xsl:param name="text"/>
+    <xsl:call-template name="escape-markup-characters">
+      <xsl:with-param name="text" select="$text"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template name="pi-target">
+    <xsl:param name="text"/>
+    <xsl:value-of select="$text"/>
+  </xsl:template>
+
+  <xsl:template name="pi-text">
+    <xsl:param name="text"/>
+    <xsl:value-of select="$text"/>
+  </xsl:template>
+
+  <xsl:template name="comment-text">
+    <xsl:param name="text"/>
+    <xsl:value-of select="$text"/>
+  </xsl:template>
+
+  <xsl:template name="escape-markup-characters">
+    <xsl:param name="text"/>
+    <xsl:variable name="ampEscaped">
+      <xsl:call-template name="replace-string">
+        <xsl:with-param name="text" select="$text"/>
+        <xsl:with-param name="replace" select="'&amp;'"/>
+        <xsl:with-param name="with" select="'&amp;amp;'"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:variable name="ltEscaped">
+      <xsl:call-template name="replace-string">
+        <xsl:with-param name="text" select="$ampEscaped"/>
+        <xsl:with-param name="replace" select="'&lt;'"/>
+        <xsl:with-param name="with" select="'&amp;lt;'"/>
+      </xsl:call-template>
+    </xsl:variable>
+    <xsl:call-template name="replace-string">
+      <xsl:with-param name="text" select="$ltEscaped"/>
+      <xsl:with-param name="replace" select="']]>'"/>
+      <xsl:with-param name="with" select="']]&amp;gt;'"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template name="replace-string">
+    <xsl:param name="text"/>
+    <xsl:param name="replace"/>
+    <xsl:param name="with"/>
+    <xsl:variable name="stringText" select="string($text)"/>
+    <xsl:choose>
+      <xsl:when test="contains($stringText,$replace)">
+        <xsl:value-of select="substring-before($stringText,$replace)"/>
+        <xsl:value-of select="$with"/>
+        <xsl:call-template name="replace-string">
+          <xsl:with-param name="text" select="substring-after($stringText,$replace)"/>
+          <xsl:with-param name="replace" select="$replace"/>
+          <xsl:with-param name="with" select="$with"/>
+        </xsl:call-template>
+      </xsl:when>
+      <xsl:otherwise>
+        <xsl:value-of select="$stringText"/>
+      </xsl:otherwise>
+    </xsl:choose>
+  </xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/cruise/reporting-app/xsl/maven.xsl b/groovy/cruise/reporting-app/xsl/maven.xsl
new file mode 100644
index 0000000..9a38092
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/maven.xsl
@@ -0,0 +1,101 @@
+<?xml version="1.0"?>
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2003, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<xsl:stylesheet
+    version="1.0"
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns="http://www.w3.org/TR/html4/strict.dtd" >
+
+    <xsl:output method="html"/>
+
+    <xsl:variable name="mavengoal" select="/cruisecontrol/build//mavengoal"/>
+
+    <xsl:template match="/">
+
+        <xsl:variable name="maven.messages" select="$mavengoal/message"/>
+        <xsl:variable name="maven.error.messages" select="$mavengoal/message[@priority='error']"/>
+        <xsl:variable name="maven.warn.messages" select="$mavengoal/message[@priority='warn']"/>
+        <xsl:variable name="maven.info.messages" select="$mavengoal/message[@priority='info']"/>
+
+        <xsl:if test="count($maven.messages) > 0">
+            <table align="center" cellpadding="2" cellspacing="0" border="0" width="98%">
+                 <!-- Style download notifications first -->
+                 <tr class="compile-sectionheader">
+                     <td>Initial Messages</td>
+                 </tr>
+                 <tr>
+                     <td>
+                         <xsl:apply-templates select="cruisecontrol/build/message"/>
+                     </td>
+                 </tr>
+                 <xsl:apply-templates select="$mavengoal"/>
+            </table>
+        </xsl:if>
+    </xsl:template>
+
+    <xsl:template match="mavengoal">
+       <tr class="compile-sectionheader">
+       		<td>
+            	<xsl:value-of select="@name"/>
+            </td>
+       </tr>
+       <tr>
+       		<td>
+            	<xsl:apply-templates select="./message"/>
+            </td>
+       </tr>
+    </xsl:template>
+
+    <xsl:template match="message[@priority='error']">
+    	  <span class="compile-error-data">
+        <xsl:value-of select="text()"/><xsl:text disable-output-escaping="yes"><![CDATA[<br/>]]></xsl:text>
+        </span>
+    </xsl:template>
+
+    <xsl:template match="message[@priority='warn']">
+    	  <span class="compile-data">
+        <xsl:value-of select="text()"/><xsl:text disable-output-escaping="yes"><![CDATA[<br/>]]></xsl:text>
+        </span>
+    </xsl:template>
+
+    <xsl:template match="message[@priority='info']">
+    	  <span class="compile-data">
+        <xsl:value-of select="text()"/><xsl:text disable-output-escaping="yes"><![CDATA[<br/>]]></xsl:text>
+        </span>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/cruise/reporting-app/xsl/modifications.xsl b/groovy/cruise/reporting-app/xsl/modifications.xsl
new file mode 100644
index 0000000..7e4bb45
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/modifications.xsl
@@ -0,0 +1,205 @@
+<?xml version="1.0"?>
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+    <xsl:output method="html"/>
+    <xsl:variable name="modification.list" select="cruisecontrol/modifications/modification"/>
+
+    <xsl:template match="/">
+        <table align="center" cellpadding="2" cellspacing="1" border="0" width="98%">
+            <!-- Modifications -->
+            <tr>
+                <td class="modifications-sectionheader" colspan="6">
+                    &#160;Modifications since last build:&#160;
+                    (<xsl:value-of select="count($modification.list)"/>)
+                </td>
+            </tr>
+
+            <xsl:apply-templates select="$modification.list">
+                <xsl:sort select="date" order="descending" data-type="text" />
+            </xsl:apply-templates>
+
+        </table>
+    </xsl:template>
+
+    <!-- P4 changelist template
+    <modification type="p4" revision="15">
+       <revision>15</revision>
+       <user>non</user>
+       <client>non:all</client>
+       <date>2002/05/02 10:10:10</date>
+       <file action="add">
+          <filename>myfile</filename>
+          <revision>10</revision>
+       </file>
+    </modification>
+    -->
+    <xsl:template match="modification[@type='p4']">
+        <tr valign="top">
+            <xsl:if test="position() mod 2=0">
+                <xsl:attribute name="class">changelists-evenrow</xsl:attribute>
+            </xsl:if>
+            <xsl:if test="position() mod 2!=0">
+                <xsl:attribute name="class">changelists-oddrow</xsl:attribute>
+            </xsl:if>
+            <td class="modifications-data">
+                <xsl:value-of select="revision"/>
+            </td>
+            <td class="modifications-data">
+                <xsl:value-of select="user"/>
+            </td>
+            <td class="modifications-data">
+                <xsl:value-of select="client"/>
+            </td>
+            <td class="modifications-data">
+                <xsl:value-of select="date"/>
+            </td>
+            <td class="modifications-data">
+                <xsl:value-of select="comment"/>
+            </td>
+        </tr>
+        <xsl:if test="count(file) > 0">
+            <tr valign="top">
+                <xsl:if test="position() mod 2=0">
+                    <xsl:attribute name="class">changelists-evenrow</xsl:attribute>
+                </xsl:if>
+                <xsl:if test="position() mod 2!=0">
+                    <xsl:attribute name="class">changelists-oddrow</xsl:attribute>
+                </xsl:if>
+                <td class="modifications-data" colspan="6">
+                    <table align="right" cellpadding="1" cellspacing="0" border="0" width="95%">
+                        <tr>
+                            <td class="changelists-file-header" colspan="3">
+                                &#160;Files affected by this changelist:&#160;
+                                (<xsl:value-of select="count(file)"/>)
+                            </td>
+                        </tr>
+                        <xsl:apply-templates select="file"/>
+                    </table>
+                </td>
+            </tr>
+        </xsl:if>
+    </xsl:template>
+
+    <!-- used by P4 -->
+    <xsl:template match="file">
+        <tr valign="top" >
+            <xsl:if test="position() mod 2=0">
+                <xsl:attribute name="class">changelists-file-evenrow</xsl:attribute>
+            </xsl:if>
+            <xsl:if test="position() mod 2!=0">
+                <xsl:attribute name="class">changelists-file-oddrow</xsl:attribute>
+            </xsl:if>
+
+            <td class="changelists-file-spacer">
+                &#160;
+            </td>
+
+            <td class="modifications-data">
+                <b>
+                    <xsl:value-of select="@action"/>
+                </b>
+            </td>
+            <td class="modifications-data" width="100%">
+                <xsl:value-of select="filename"/>&#160;
+                <xsl:value-of select="revision"/>
+            </td>
+        </tr>
+    </xsl:template>
+
+    <!-- Modifications template -->
+    <xsl:template match="modification[file]">
+        <tr>
+            <xsl:if test="position() mod 2=0">
+                <xsl:attribute name="class">modifications-evenrow</xsl:attribute>
+            </xsl:if>
+            <xsl:if test="position() mod 2!=0">
+                <xsl:attribute name="class">modifications-oddrow</xsl:attribute>
+            </xsl:if>
+
+            <td class="modifications-data">
+                <xsl:value-of select="file/@action"/>
+            </td>
+            <td class="modifications-data">
+                <xsl:value-of select="user"/>
+            </td>
+            <td class="modifications-data">
+                <xsl:if test="file/project">
+                    <xsl:value-of select="file/project"/>
+                    <xsl:value-of select="system-property('file.separator')"/>
+                </xsl:if>
+                <xsl:value-of select="file/filename"/>
+            </td>
+            <td class="modifications-data">
+                <xsl:value-of select="comment"/>
+            </td>
+        </tr>
+    </xsl:template>
+
+    <!-- Up to version 2.1.6 the modification set format did not
+         include the file node -->
+    <xsl:template match="modification">
+        <tr>
+            <xsl:if test="position() mod 2=0">
+                <xsl:attribute name="class">modifications-evenrow</xsl:attribute>
+            </xsl:if>
+            <xsl:if test="position() mod 2!=0">
+                <xsl:attribute name="class">modifications-oddrow</xsl:attribute>
+            </xsl:if>
+
+            <td class="modifications-data">
+                <xsl:value-of select="@type"/>
+            </td>
+            <td class="modifications-data">
+                <xsl:value-of select="user"/>
+            </td>
+            <td class="modifications-data">
+                <xsl:if test="project">
+                    <xsl:value-of select="project"/>
+                    <xsl:value-of select="system-property('file.separator')"/>
+                </xsl:if>
+                <xsl:value-of select="filename"/>
+            </td>
+            <td class="modifications-data">
+                <xsl:value-of select="comment"/>
+            </td>
+        </tr>
+    </xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/cruise/reporting-app/xsl/testdetails.xsl b/groovy/cruise/reporting-app/xsl/testdetails.xsl
new file mode 100644
index 0000000..79f7008
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/testdetails.xsl
@@ -0,0 +1,313 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<xsl:output method="html"/>
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+
+<!-- ================================================================== -->
+<!-- Write a package level report                                       -->
+<!-- It creates a table with values from the document:                  -->
+<!-- Name | Tests | Errors | Failures | Time                            -->
+<!-- ================================================================== -->
+<xsl:template match="cruisecontrol" priority="1">
+    <script type="text/javascript" language="JavaScript">
+        var TestCases = new Array();
+        var SystemOut = new Array();
+        var SystemErr = new Array();
+        var Problem = new Array();
+        var cur;
+      <!--xsl:apply-templates select="//testsuite" mode="js.props" /-->
+      <xsl:apply-templates select="//testsuite" mode="js.props" />
+    </script>
+    <script type="text/javascript" language="JavaScript"><![CDATA[
+        function displayProperties (name) {
+          var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
+          var doc = win.document.open();
+          doc.write("<html><head><title>Properties of " + name + "</title>");
+          doc.write("<style>")
+          doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
+          doc.write("table tr td, table tr th { font-size: 68%; }");
+          doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
+          doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
+          doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
+          doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
+          doc.write("</style>");
+          doc.write("</head><body>");
+          doc.write("<h3>Properties of " + name + "</h3>");
+          doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
+          doc.write("<table class='properties'>");
+          doc.write("<tr><th>Name</th><th>Value</th></tr>");
+          for (prop in TestCases[name]) {
+            doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
+          }
+          doc.write("</table>");
+          doc.write("</body></html>");
+          doc.close();
+          win.focus();
+        }
+    ]]>  
+    </script>
+    <script type="text/javascript" language="JavaScript"><![CDATA[
+    function displayMessage(name) {
+        var win = window.open('','Message','scrollbars=1,resizable=1');
+        var doc = win.document.open();
+        doc.write("<html><head><title>Message</title></head>");
+        doc.write("<body><pre>");
+        doc.write(Problem[name]);
+        doc.write("</pre></body></html>");
+        doc.close();
+        win.focus();
+    }
+    ]]>
+    </script>
+    <script type="text/javascript" language="JavaScript"><![CDATA[
+    function displayOut(name) {
+        var win = window.open('','Out','scrollbars=1,resizable=1');
+        var doc = win.document.open();
+        doc.write("<html><head><title>Out</title></head>");
+        doc.write("<body><pre>");
+        doc.write(SystemOut[name]);
+        doc.write("</pre></body></html>");
+        doc.close();
+        win.focus();
+    }
+    ]]>
+    </script>
+    <script type="text/javascript" language="JavaScript"><![CDATA[
+    function displayErr(name) {
+        var win = window.open('','Err','scrollbars=1,resizable=1');
+        var doc = win.document.open();
+        doc.write("<html><head><title>Err</title></head>");
+        doc.write("<body><pre>");
+        doc.write(SystemErr[name]);
+        doc.write("</pre></body></html>");
+        doc.close();
+        win.focus();
+    }
+    ]]>
+    </script>
+    <table border="0" cellspacing="0" width="100%">
+    <xsl:call-template name="table.header" />
+    <xsl:for-each select="//testsuite">
+        <xsl:sort select="count(testcase/error)" data-type="number" order="descending" />
+        <xsl:sort select="count(testcase/failure)" data-type="number" order="descending" />
+        <xsl:sort select="@package"/>
+        <xsl:sort select="@name"/>
+
+        <xsl:call-template name="print.class" />
+        <xsl:apply-templates select="." mode="print.test" />
+        <xsl:call-template name="print.properties" />
+    </xsl:for-each>
+    </table>
+</xsl:template>
+    
+<xsl:template match="system-out|system-err" mode="print.test"/>
+
+<xsl:template match="testcase" mode="print.test">
+    <tr>
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="error">unittests-error</xsl:when>
+                <xsl:when test="failure">unittests-error</xsl:when>
+                <xsl:otherwise>unittests-data</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td />
+        <td colspan="2">
+            <xsl:value-of select="@name"/>
+        </td>
+        <td>
+            <xsl:choose>
+                <xsl:when test="error">
+                    <a>
+                        <xsl:attribute name="href">javascript:displayMessage('<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>.<xsl:value-of select='@name'/>');</xsl:attribute>
+                        Error &#187;
+                    </a>
+                </xsl:when>
+                <xsl:when test="failure">
+                    <a>
+                        <xsl:attribute name="href">javascript:displayMessage('<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>.<xsl:value-of select='@name'/>');</xsl:attribute>
+                        Failure &#187;
+                    </a>
+                </xsl:when>
+                <xsl:otherwise>Success</xsl:otherwise>
+            </xsl:choose>
+        </td>
+        <xsl:if test="not(failure|error)">
+            <td>
+                <xsl:value-of select="format-number(@time,'0.000')"/>
+            </td>
+        </xsl:if>
+    </tr>
+</xsl:template>
+
+<xsl:template name="table.header" >
+    <colgroup>
+        <col width="10%"></col>
+        <col width="45%"></col>
+        <col width="25%"></col>
+        <col width="10%"></col>
+        <col width="10%"></col>
+    </colgroup>
+    <tr valign="top" class="unittests-sectionheader" align="left" >
+        <th colspan="3">Name</th>
+        <th>Status</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+<xsl:template name="print.properties" >
+    <tr class="unittests-data">
+        <td colspan="2">
+            <a>
+                <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+                Properties &#187;
+            </a>
+        </td>
+        <td>
+            <xsl:if test="system-out/text()">
+              <a style="float:center" >
+                <xsl:attribute name="href">javascript:displayOut('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+                System.out &#187;
+              </a>
+            </xsl:if>
+        </td>
+        <td>
+            <xsl:if test="system-err/text()">
+              <a>
+                <xsl:attribute name="href">javascript:displayErr('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+                System.err &#187;
+              </a>
+            </xsl:if>
+        </td>
+    </tr>
+</xsl:template>
+
+<xsl:template name="print.class" >
+    <tr>
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="testcase/error">unittests-error</xsl:when>
+                <xsl:when test="testcase/failure">unittests-error</xsl:when>
+                <xsl:otherwise>unittests-data</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td colspan="5"><xsl:value-of select="@package"/>.<xsl:value-of select="@name"/></td>
+    </tr>
+</xsl:template>
+
+<!--
+  Write output and err into a JavaScript data structure.
+  This is based on the original idea by Erik Hatcher (ehatcher@apache.org)
+-->
+<!--xsl:template match="system-out" mode="js.props" >
+    SystemOut['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param select="." name="string"/></xsl:call-template>';
+</xsl:template>
+
+<xsl:template match="system-err" mode="js.props" >
+    SystemErr['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param select="." name="string"/></xsl:call-template>';
+</xsl:template-->
+
+<xsl:template match="error|failure" mode="js.props" >
+    Problem['<xsl:value-of select="../../@package"/>.<xsl:value-of select="../../@name"/>.<xsl:value-of select="../@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param select="." name="string"/></xsl:call-template>';
+</xsl:template>
+
+<!--
+  Write properties into a JavaScript data structure.
+  This is based on the original idea by Erik Hatcher (ehatcher@apache.org)
+-->
+<xsl:template match="properties" mode="js.props" >
+    cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
+    <xsl:for-each select="property">
+        <xsl:sort select="@name"/>
+        cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
+    </xsl:for-each>
+</xsl:template>
+    
+<xsl:template name="JS-escape">
+    <xsl:param name="string"/>		
+    <xsl:variable name="CR" select="'&#xD;'"/>					
+    <xsl:variable name="LF" select="'&#xA;'"/>
+    <xsl:variable name="CRLF" select="concat($CR, $LF)"/>			
+    <xsl:choose>
+        <!-- crlf -->
+        <xsl:when test="contains($string,$CRLF)">
+            <xsl:call-template name="JS-escape">
+                <xsl:with-param name="string" select="substring-before($string,$CRLF)"/>
+            </xsl:call-template><br/><xsl:call-template name="JS-escape">
+                <xsl:with-param name="string" select="substring-after($string,$CRLF)"/>
+            </xsl:call-template>
+        </xsl:when>
+        <!-- carriage return -->
+        <xsl:when test="contains($string,$CR)">
+            <xsl:call-template name="JS-escape">
+                <xsl:with-param name="string" select="substring-before($string,$CR)"/>
+            </xsl:call-template><br/><xsl:call-template name="JS-escape">
+                <xsl:with-param name="string" select="substring-after($string,$CR)"/>
+            </xsl:call-template>
+        </xsl:when>
+        <!-- line feed -->
+        <xsl:when test="contains($string,$LF)">
+            <xsl:call-template name="JS-escape">
+                <xsl:with-param name="string" select="substring-before($string,$LF)"/>
+            </xsl:call-template><br/><xsl:call-template name="JS-escape">
+                <xsl:with-param name="string" select="substring-after($string,$LF)"/>
+            </xsl:call-template>
+        </xsl:when>
+        <!-- single quote -->
+        <xsl:when test="contains($string,&quot;'&quot;)">
+            <xsl:call-template name="JS-escape">
+                <xsl:with-param name="string" select="substring-before($string,&quot;'&quot;)"/>
+            </xsl:call-template>\&apos;<xsl:call-template name="JS-escape">
+                <xsl:with-param name="string" select="substring-after($string,&quot;'&quot;)"/>
+            </xsl:call-template>
+        </xsl:when>
+        <!-- escape -->
+        <xsl:when test="contains($string,'\')">
+            <xsl:call-template name="JS-escape">
+                <xsl:with-param name="string" select="substring-before($string,'\')"/>
+            </xsl:call-template>\\<xsl:call-template name="JS-escape">
+                <xsl:with-param name="string" select="substring-after($string,'\')"/>
+            </xsl:call-template>
+        </xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="$string"/>
+        </xsl:otherwise>
+    </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/groovy/cruise/reporting-app/xsl/unittests.xsl b/groovy/cruise/reporting-app/xsl/unittests.xsl
new file mode 100644
index 0000000..23b0249
--- /dev/null
+++ b/groovy/cruise/reporting-app/xsl/unittests.xsl
@@ -0,0 +1,264 @@
+<?xml version="1.0"?>
+<!--********************************************************************************
+ * CruiseControl, a Continuous Integration Toolkit
+ * Copyright (c) 2001, ThoughtWorks, Inc.
+ * 651 W Washington Ave. Suite 600
+ * Chicago, IL 60661 USA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ *     + Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *
+ *     + Redistributions in binary form must reproduce the above
+ *       copyright notice, this list of conditions and the following
+ *       disclaimer in the documentation and/or other materials provided
+ *       with the distribution.
+ *
+ *     + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
+ *       names of its contributors may be used to endorse or promote
+ *       products derived from this software without specific prior
+ *       written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ********************************************************************************-->
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt">
+
+    <xsl:output method="html"/>
+
+    <xsl:variable name="testsuite.list" select="//testsuites/testsuite"/>
+    <xsl:variable name="testsuite.error.count" select="count($testsuite.list/error)"/>
+    <xsl:variable name="testcase.list" select="$testsuite.list/testcase"/>
+    <xsl:variable name="testcase.error.list" select="$testcase.list/error"/>
+    <xsl:variable name="testcase.failure.list" select="$testcase.list/failure"/>
+    <xsl:variable name="totalErrorsAndFailures" select="count($testcase.error.list) + count($testcase.failure.list) + $testsuite.error.count"/>
+
+    <xsl:template match="/">
+        <table align="center" cellpadding="2" cellspacing="0" border="0" width="98%">
+
+            <!-- Unit Tests -->
+            <tr>
+                <td class="unittests-sectionheader" colspan="4">
+                   &#160;Unit Tests: (<xsl:value-of select="count($testcase.list)"/>)
+                </td>
+            </tr>
+
+            <xsl:choose>
+                <xsl:when test="count($testsuite.list) = 0">
+                    <tr>
+                        <td colspan="2" class="unittests-data">
+                            No Tests Run
+                        </td>
+                    </tr>
+                    <tr>
+                        <td colspan="2" class="unittests-error">
+                            This project doesn't have any tests
+                        </td>
+                    </tr>
+                </xsl:when>
+
+                <xsl:when test="$totalErrorsAndFailures = 0">
+                    <tr>
+                        <td colspan="2" class="unittests-data">
+                            All Tests Passed
+                        </td>
+                    </tr>
+                    <td><table>
+                    <xsl:for-each select="$testsuite.list">
+                        <tr>
+                            <xsl:if test="position() mod 2 = 0">
+                                <xsl:attribute name="class">unittests-evenrow</xsl:attribute>
+                            </xsl:if>
+                            <xsl:if test="position() mod 2 != 0">
+                                <xsl:attribute name="class">unittests-oddrow</xsl:attribute>
+                            </xsl:if>
+                            <td colspan="2" class="unittests-data">
+                            <xsl:value-of select="concat('Suite: ', @name)"/>
+                            </td>
+                            <td colspan="2" class="unittests-data">
+                            <xsl:value-of select="concat('Tests: ', @tests)"/>
+                            </td>
+                            <td colspan="2" class="unittests-data">
+                            <xsl:value-of select="concat('Time: ', @time)"/>
+                            </td>
+                        </tr>
+                    </xsl:for-each>
+                    </table></td>
+                </xsl:when>
+            </xsl:choose>
+            <tr>
+              <td>
+                 <table align="center" cellpadding="2" cellspacing="0" border="0" width="98%">
+                    <xsl:apply-templates select="$testcase.error.list"/>
+                    <xsl:apply-templates select="$testcase.failure.list"/>
+                 </table>
+              </td>
+            </tr>
+            <tr/>
+            <tr><td colspan="2">&#160;</td></tr>
+
+            <xsl:if test="$totalErrorsAndFailures > 0">
+
+              <tr>
+                <td class="unittests-sectionheader" colspan="4">
+                    &#160;Unit Test Error Details:&#160;(<xsl:value-of select="$totalErrorsAndFailures"/>)
+                </td>
+              </tr>
+
+              <!-- (PENDING) Why doesn't this work if set up as variables up top? -->
+              <xsl:call-template name="testdetail">
+                <xsl:with-param name="detailnodes" select="//testsuites/testsuite/testcase[.//error]"/>
+              </xsl:call-template>
+
+              <xsl:call-template name="testdetail">
+                <xsl:with-param name="detailnodes" select="//testsuites/testsuite/testcase[.//failure]"/>
+              </xsl:call-template>
+
+
+              <tr><td colspan="2">&#160;</td></tr>
+            </xsl:if>
+
+
+        </table>
+    </xsl:template>
+
+    <!-- UnitTest Errors -->
+    <xsl:template match="error">
+        <tr>
+            <xsl:if test="position() mod 2 = 0">
+                <xsl:attribute name="class">unittests-oddrow</xsl:attribute>
+            </xsl:if>
+
+            <td class="unittests-data">
+                error
+            </td>
+            <td class="unittests-data" width="40%">
+                <xsl:value-of select="../@name"/>
+            </td>
+            <td class="unittests-data" width="40%">
+                <xsl:value-of select="..//..//@name"/>
+            </td>
+        </tr>
+    </xsl:template>
+
+    <!-- UnitTest Failures -->
+    <xsl:template match="failure">
+        <tr>
+            <xsl:if test="($testsuite.error.count + position()) mod 2 = 0">
+                <xsl:attribute name="class">unittests-oddrow</xsl:attribute>
+            </xsl:if>
+
+            <td class="unittests-data">
+                failure
+            </td>
+            <td class="unittests-data" width="40%">
+                <xsl:value-of select="../@name"/>
+            </td>
+            <td class="unittests-data" width="40%">
+                <xsl:value-of select="..//..//@name"/>
+            </td>
+        </tr>
+    </xsl:template>
+
+    <!-- UnitTest Errors And Failures Detail Template -->
+    <xsl:template name="testdetail">
+      <xsl:param name="detailnodes"/>
+
+      <xsl:for-each select="$detailnodes">
+
+        <tr>
+            <td colspan="2" class="unittests-data">
+                Test:&#160;<xsl:value-of select="@name"/>
+            </td>
+        </tr>
+        <tr>
+            <td colspan="2" class="unittests-data">
+                Class:&#160;<xsl:value-of select="..//@name"/>
+            </td>
+        </tr>
+
+        <xsl:if test="error">
+            <tr>
+                <td colspan="2" class="unittests-data">
+                    Type: <xsl:value-of select="error/@type" />
+                </td>
+            </tr>
+        <tr>
+            <td colspan="2" class="unittests-data">
+                Message: <xsl:value-of select="error/@message" />
+            </td>
+        </tr>
+
+        <tr>
+            <td colspan="2" class="unittests-error">
+                <PRE>
+                    <xsl:call-template name="br-replace">
+                        <xsl:with-param name="word" select="error" />
+                    </xsl:call-template>
+                </PRE>
+            </td>
+        </tr>
+        </xsl:if>
+
+        <xsl:if test="failure">
+        <tr>
+            <td colspan="2" class="unittests-data">
+                Type: <xsl:value-of select="failure/@type" />
+            </td>
+        </tr>
+        <tr>
+            <td colspan="2" class="unittests-data">
+                Message: <xsl:value-of select="failure/@message" />
+            </td>
+        </tr>
+
+        <tr>
+            <td colspan="2" class="unittests-failure">
+                <pre>
+                    <xsl:call-template name="br-replace">
+                        <xsl:with-param name="word" select="failure"/>
+                    </xsl:call-template>
+                </pre>
+            </td>
+        </tr>
+        </xsl:if>
+
+      </xsl:for-each>
+    </xsl:template>
+
+    <xsl:template name="br-replace">
+        <xsl:param name="word"/>
+<!-- </xsl:text> on next line on purpose to get newline -->
+<xsl:variable name="cr"><xsl:text>
+</xsl:text></xsl:variable>
+        <xsl:choose>
+            <xsl:when test="contains($word,$cr)">
+                <xsl:value-of select="substring-before($word,$cr)"/>
+                <br/>
+                <xsl:call-template name="br-replace">
+                <xsl:with-param name="word" select="substring-after($word,$cr)"/>
+                </xsl:call-template>
+            </xsl:when>
+            <xsl:otherwise>
+                <xsl:value-of select="$word"/>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+
+</xsl:stylesheet>
diff --git a/groovy/experimental/TestMeta.java b/groovy/experimental/TestMeta.java
new file mode 100644
index 0000000..8874d99
--- /dev/null
+++ b/groovy/experimental/TestMeta.java
@@ -0,0 +1,80 @@
+import groovy.lang.MetaClass;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.MetaClassActions;
+import org.codehaus.groovy.runtime.MetaClassActionsGenerator;
+
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class TestMeta {
+  
+  /**
+   * @param args
+   */
+  public static void main(String[] args) throws Exception {   
+    final MetaClassActions a1 = MetaClassActionsGenerator.getActions(ArrayList.class);
+    final MetaClassActions a2 = MetaClassActionsGenerator.getActions(HashMap.class);
+    final MetaClass m1 = InvokerHelper.getInstance().getMetaRegistry().getMetaClass(ArrayList.class);
+    final MetaClass m2 = InvokerHelper.getInstance().getMetaRegistry().getMetaClass(HashMap.class);
+    final ArrayList i1 = new ArrayList();
+    final HashMap i2 = new HashMap();
+    final Integer zero = new Integer(0);
+    
+    i1.add(new Integer(0));
+    i1.add(new Integer(1));
+    i1.add(new Integer(0));
+    i1.add(new Integer(3));
+    
+    System.out.println(m1.invokeMethod(i1, "count", new Object[]{zero}));
+    System.out.println(a1.doInvokeMethod(i1, "count", new Object[]{zero}));
+    
+    final String method = "toArray";
+    
+    long start = System.currentTimeMillis();
+    
+    for (int i = 0; i != 10000000; i++) {
+      m1.invokeMethod(i1, method, new Object[]{});
+    }
+    
+    System.out.println("Time taken via MetaClass: " + (System.currentTimeMillis() - start));
+    
+    start = System.currentTimeMillis();
+    
+    for (int i = 0; i != 10000000; i++) {
+      a1.doInvokeMethod(i1, method, new Object[]{});
+    }
+    
+    System.out.println("Time taken via MetaClassActions: " + (System.currentTimeMillis() - start));
+    
+    start = System.currentTimeMillis();
+    
+    for (int i = 0; i != 10000000; i++) {
+      i1.toArray();
+    }
+    
+    System.out.println("Time taken via direct call: " + (System.currentTimeMillis() - start));
+  }
+}
\ No newline at end of file
diff --git a/groovy/experimental/groovy/lang/MOP.java b/groovy/experimental/groovy/lang/MOP.java
new file mode 100644
index 0000000..ab8e1db
--- /dev/null
+++ b/groovy/experimental/groovy/lang/MOP.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package groovy.lang;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+/**
+ * This inteface defines thew Meta Object Protocol implemented by the MetaClass
+ * Note that this inteface is purely for documantation purposes
+ * An object implementing this interface is not neccesarily a vaild MetaClass
+ * A MetaClass needs to implement may more methods
+ *
+ */
+public interface MOP {
+    static final Object NOT_CALLED = new Object();
+    static final Object NO_PROPERTY = new Object();
+    static final Object NO_ATTRIBUTE = new Object();
+
+    /**
+     * Invoke a public method on an object.
+     * Public methods are those declared public in the class and those added via mechanisms like DefaultGroovyMethods.
+     * If no suitable method is found a groovy.lang.MissingMethodException is thrown.
+     * @param object
+     * The object on which the call is to be made
+     * @param objectType
+     * If objectType is not null then method to be called is selected as though the object was cast to this type before making the call.
+     * @param methodName
+     * The name of the method to be called
+     * @param arguments
+     * The arguments to the call.
+     * If NOT_CALLED is returned and there is a private or protected method which
+     * should be called then this array will contain the parameters to that method. The MetaClass will have done any conversions
+     * (e.g. GString to String) needed to allow the call to suceed.
+     * If any other value is returned then the values in this array are undefined. In this case the MetaClass may or may not
+     * change these values. DO NOT use these values unless NOT_CALLED is returned.
+     * @param argumentTypes
+     * This array must be the same length as the arguments arguments array.
+     * It contains the type of each parameter if known or null if unknown.
+     * The if there are multiple methods with the same name and number of parameters then this information
+     * contributes to the selection of the correct method to call.
+     * If NOT_CALLED is returned and there is a private or protected method which
+     * should be called then this array will contain the classes which represent the types of the parameters to that method.
+     * If any other value is returned then the values in this array are undefined. In this case the MetaClass may or may not
+     * change these values. DO NOT use these values unless NOT_CALLED is returned.
+     * @return
+     * The result of calling the method (null is returned if the method returns void).
+     */
+    Object invokeMethod( Object object,  Class objectType,  String methodName,  Object[] arguments,  Class[] argumentTypes);
+    
+    Object getProperty( Object object,  Class objectType,  String property);
+    
+    void setProperty( Object object,  Class objectType,  String property,  Object newValue,  Class newValueType);
+    
+    Object getAttribute( Object object,  Class objectType,  String attribute);
+    
+    void setAttribute( Object object,  Class objectType,  String attribute,  Object newValue,  Class newValueType);
+    
+    /**
+     * Invoke a public method on an object.
+     * These public methods are those declared public in the class and those added via mechanisms like DefaultGroovyMethods.
+     * If no public method can be found but there exists protected or private methods which would match the name, number and type
+     * of the parameters then the value NOT_CALLED is returned.
+     * Oherwise a groovy.lang.MissingMethodException is thrown.
+     * @param object
+     * The object on which the call is to be made
+     * @param thisType
+     * The type of the class in which this call was made. This must never be null.
+     * Note this type valie may not be the same as object.getClass() as it my be a superclass of that class.
+     * @param methodName
+     * The name of the method to be called
+     * @param arguments
+     * The arguments to the call.
+     * If NOT_CALLED is returned and there is a private or protected method which
+     * should be called then this array will contain the parameters to that method. The MetaClass will have done any conversions
+     * (e.g. GString to String) needed to allow the call to succeed.
+     * If any other value is returned then the values in this array are undefined. In this case the MetaClass may or may not
+     * change these values. DO NOT use these values unless NOT_CALLED is returned.
+     * @param argumentTypes
+     * This array must be the same length as the arguments arguments array.
+     * It contains the type of each parameter if known or null if unknown.
+     * The if there are multiple methods with the same name and number of parameters then this information
+     * contributes to the selection of the correct method to call.
+     * If NOT_CALLED is returned and there is a private or protected method which
+     * should be called then this array will contain the classes which represent the types of the parameters to that method.
+     * If any other value is returned then the values in this array are undefined. In this case the MetaClass may or may not
+     * change these values. DO NOT use these values unless NOT_CALLED is returned.
+     * @return
+     * Either the result of calling the method (null is returned if the method returns void) or the value NOT_CALLED.
+     * NOT_CALLED is returned if a suitable method exists but the MetaClass did not call it because it is not public.
+     * In this case the caller is free to try to invoke the method itself (e.g. by executing generated bytecode).
+     * If there are multiple methods with the same name and number of parameters then the values on the array passed
+     * as argumentTypes should be used to choose the correct one to call.
+     */
+    Object invokeThisMethod( Object object,  Class thisType,  String methodName,  Object[] arguments,  Class[] argumentTypes);
+    
+    Object getThisProperty( Object object,  Class thisType,  String property);
+    
+    boolean setThisProperty( Object object,  Class thisType,  String property,  Object newValue,  Class newValueType);
+    
+    Object getThisAttribute( Object object,  Class thisType,  String attribute);
+    
+    boolean setThisAttribute( Object object,  Class thisType,  String attribute,  Object newValue,  Class newValueType);
+    
+    /**
+     * Invoke an added method on an object. Methods are added via mechanisms like DefaultGroovyMethods.
+     * If no added method can be found but there exists public, protected or private methods on the specified superclass
+     * which would match the name, number and type of the parameters then the value NOT_CALLED is returned.
+     * Oherwise a groovy.lang.MissingMethodException is thrown.
+     * @param object
+     * The object on which the call is to be made
+     * @param superclassType
+     * The type of the superclass. This must never be null.
+     * @param methodName
+     * The name of the method to be called
+     * @param arguments
+     * The arguments to the call.
+     * If NOT_CALLED is returned and there is a private or protected method which
+     * should be called then this array will contain the parameters to that method. The MetaClass will have done any conversions
+     * (e.g. GString to String) needed to allow the call to succeed.
+     * If any other value is returned then the values in this array are undefined. In this case the MetaClass may or may not
+     * change these values. DO NOT use these values unless NOT_CALLED is returned.
+     * @param argumentTypes
+     * This array must be the same length as the arguments arguments array.
+     * It contains the type of each parameter if known or null if unknown.
+     * The if there are multiple methods with the same name and number of parameters then this information
+     * contributes to the selection of the correct method to call.
+     * If NOT_CALLED is returned and there is a method which
+     * should be called then this array will contain the classes which represent the types of the parameters to that method.
+     * If any other value is returned then the values in this array are undefined. In this case the MetaClass may or may not
+     * change these values. DO NOT use these values unless NOT_CALLED is returned.
+     * @return
+     * Either the result of calling the method (null is returned if the method returns void) or the value NOT_CALLED.
+     * NOT_CALLED is returned if a suitable method exists but the MetaClass did not call it because it is not added.
+     * In this case the caller is free to try to invoke the method itself (e.g. by executing generated bytecode).
+     * If there are multiple methods with the same name and number of parameters then the values on the array passed
+     * as argumentTypes should be used to choose the correct one to call.
+     */  
+    Object invokeSuperMethod( Object object,  Class superclassType,  String methodName,  Object[] arguments,  Class[] argumentTypes);
+    
+    Object getSuperProperty( Object object,  Class superclassType,  String property);
+    
+    boolean setSuperProperty( Object object,  Class superclassType,  String property,  Object newValue,  Class newValueType);
+    
+    Object getSuperAttribute( Object object,  Class superclassType,  String attribute);
+    
+    boolean setSuperAttribute( Object object,  Class superclassType,  String attribute,  Object newValue,  Class newValueType);
+
+}
diff --git a/groovy/experimental/groovy/lang/NewMetaClass.java b/groovy/experimental/groovy/lang/NewMetaClass.java
new file mode 100644
index 0000000..56a3f13
--- /dev/null
+++ b/groovy/experimental/groovy/lang/NewMetaClass.java
@@ -0,0 +1,341 @@
+package groovy.lang;
+
+
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public abstract class NewMetaClass implements MOP {
+    public static final Object NOT_CONSTRUCTED = new Object();
+    
+    protected static final Object[] NO_PARAMETERS = new Object[0];
+    protected static final Class[] NO_PARAMETER_TYPES = new Class[0];
+    
+    //
+    // The methods forming the Meta Object Protocol
+    
+    //
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#invokeMethod(java.lang.Object, java.lang.Class, java.lang.String, java.lang.Object[], java.lang.Class[])
+     */
+    public abstract Object invokeMethod(final Object object, final Class objectType, final String methodName, final Object[] arguments, final Class[] argumentTypes);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#getProperty(java.lang.Object, java.lang.Class, java.lang.String)
+     */
+    public abstract Object getProperty(final Object object, final Class objectType, final String property);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#setProperty(java.lang.Object, java.lang.Class, java.lang.String, java.lang.Object, java.lang.Class)
+     */
+    public abstract void setProperty(final Object object, final Class objectType, final String property, final Object newValue, final Class newValueType);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#getAttribute(java.lang.Object, java.lang.Class, java.lang.String)
+     */
+    public abstract Object getAttribute(final Object object, final Class objectType, final String attribute);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#setAttribute(java.lang.Object, java.lang.Class, java.lang.String, java.lang.Object, java.lang.Class)
+     */
+    public abstract void setAttribute(final Object object, final Class objectType, final String attribute, final Object newValue, final Class newValueType);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#invokeThisMethod(java.lang.Object, java.lang.Class, java.lang.String, java.lang.Object[], java.lang.Class[])
+     */
+    public abstract Object invokeThisMethod(final Object object, final Class thisType, final String methodName, final Object[] arguments, final Class[] argumentTypes);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#getThisProperty(java.lang.Object, java.lang.Class, java.lang.String)
+     */
+    public abstract Object getThisProperty(final Object object, final Class thisType, final String property);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#setThisProperty(java.lang.Object, java.lang.Class, java.lang.String, java.lang.Object, java.lang.Class)
+     */
+    public abstract boolean setThisProperty(final Object object, final Class thisType, final String property, final Object newValue, final Class newValueType);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#getThisAttribute(java.lang.Object, java.lang.Class, java.lang.String)
+     */
+    public abstract Object getThisAttribute(final Object object, final Class thisType, final String attribute);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#setThisAttribute(java.lang.Object, java.lang.Class, java.lang.String, java.lang.Object, java.lang.Class)
+     */
+    public abstract boolean setThisAttribute(final Object object, final Class thisType, final String attribute, final Object newValue, final Class newValueType);
+
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#invokeSuperMethod(java.lang.Object, java.lang.Class, java.lang.String, java.lang.Object[], java.lang.Class[])
+     */
+    public abstract Object invokeSuperMethod(final Object object, final Class superclassType, final String methodName, final Object[] arguments, final Class[] argumentTypes);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#getSuperProperty(java.lang.Object, java.lang.Class, java.lang.String)
+     */
+    public abstract Object getSuperProperty(final Object object, final Class superclassType, final String property);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#setSuperProperty(java.lang.Object, java.lang.Class, java.lang.String, java.lang.Object, java.lang.Class)
+     */
+    public abstract boolean setSuperProperty(final Object object, final Class superclassType, final String property, final Object newValue, final Class newValueType);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#getSuperAttribute(java.lang.Object, java.lang.Class, java.lang.String)
+     */
+    public abstract Object getSuperAttribute(final Object object, final Class superclassType, final String attribute);
+    
+    /* (non-Javadoc)
+     * @see groovy.lang.MOP#setSuperAttribute(java.lang.Object, java.lang.Class, java.lang.String, java.lang.Object, java.lang.Class)
+     */
+    public abstract boolean setSuperAttribute(final Object object, final Class superclassType, final String attribute, final Object newValue, final Class newValueType);
+
+    //
+    // Methods which may migrate to the MOP at some time in the future
+    //
+    
+    /**
+     * Construct an instance of the class represented by this instance of the MetaClass.
+     * Only public constructors may be called. There is currently no way of dynamically adding constructors but
+     * if one is introduced then this method will use them.
+     * If no suitable constuctor is found then a groovy.lang.MissingConstructorException is thrown
+     * @param arguments
+     * The arguments to the constructor call. The values in this array are undefined after the call is made.
+     * The MetaClass may or may not change these vaues. DO NOT use these vaules after the call.
+     * @param argumentTypes
+     * This array must be the same length as the arguments array.
+     * It contains the type of each parameter if known or null if unknown.
+     * The if there are overloaded constructors then this information contributes to the selection of the correct constructor to call.
+     * The MetaClass may or may not change these vaues. DO NOT use these values after the call.
+     * @return
+     * A new instance of the class represented by this instance of MetaClass.
+     */
+    public abstract Object invokeConstructor(final Object[] arguments, final Class[] argumentTypes);
+    
+    /**
+     * Construct an instance of the class represented by this instance of the MetaClass.
+     * Only public constructors will be called directly. There is currently no way of dynamically adding constructors but
+     * if one is introduced then this method will use them.
+     * If a protected or private constructor is the best match for the parameters then NOT_CONSTRUCTED is returned
+     * otherwise a groovy.lang.MissingConstructorException is thrown
+     * @param arguments
+     * The arguments to the constructor.
+     * If NOT_CONSTUCTED is returned and there is a private or protected constuctor which
+     * should be called then this array will contain the parameters to that constructor. The MetaClass will have done any conversions
+     * (e.g. GString to String) needed to allow the constuctor call to suceed.
+     * If any other value is returned then the values in this array are undefined. In this case the MetaClass may or may not
+     * change these values. DO NOT use these values unless NOT_CONSTUCTED is returned.
+     * @param argumentTypes
+     * This array must be the same length as the arguments arguments array.
+     * It contains the type of each parameter if known or null if unknown.
+     * The if there are multiple constructors with the same name and number of parameters then this information
+     * contributes to the selection of the correct method to call.
+     * If NOT_CONSTUCTED is returned and there is a private or protected constuctor which
+     * should be called then this array will contain the classes which represent the types of the parameters to that constructor.
+     * If any other value is returned then the values in this array are undefined. In this case the MetaClass may or may not
+     * change these values. DO NOT use these values unless NOT_CALLED is returned.
+     * @return
+     * Either a new instance of the class represented by this instance of MetaClass or NOT_CONSTRUCTED.
+     * NOT_CONSTRUCTED is returned if a suitable constructor exists but the MetaClass did not call it because it is not public.
+     * In this case the caller is free to try to invoke the constuctor itself (e.g. by executing generated bytecode). 
+     * If there are multiple constuctors with the same name and number of parameters then the values on the array passed
+     * as argumentTypes should be used to choose the correct one to call.
+     */
+    public abstract Object invokeThisConstructor(final Object[] arguments, final Class[] argumentTypes);
+    
+    //
+    // Additional methods for use when some or all of the type information is not available
+    // We don't bother doing this with super calls properety and attribute acess as they are far less common
+    //
+    
+    public Object invokeConstructor(final Object[] arguments) {
+        return invokeConstructor(arguments, new Class[arguments.length]);
+    }
+    
+    public Object invokeMethod(final Object object, final Class objectType, final String methodName, final Object[] arguments) {
+        return invokeMethod(object, objectType, methodName, arguments, new Class[arguments.length]);
+    }
+    
+    public Object invokeMethod(final Object object, final String methodName, final Object[] arguments, final Class[] argumentTypes) {
+        return invokeMethod(object, (Class)null, methodName, arguments, argumentTypes);
+    }
+    
+    public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
+        return invokeMethod(object, (Class)null, methodName, arguments, new Class[arguments.length]);
+    }
+    
+    public Object getProperty(final Object object, final String property) {
+        return getProperty(object, (Class)null, property);
+    }
+    
+    public void setProperty(final Object object, final Class objectType, final String property, final Object newValue) {
+        setProperty(object, objectType, property, newValue, (Class)null);
+    }
+    
+    public void setProperty(final Object object, final String property, final Object newValue, final Class newValueType) {
+        setProperty(object, (Class)null, property, newValue, newValueType);
+    }
+    
+    public void setProperty(final Object object, final String property, final Object newValue) {
+        setProperty(object, (Class)null, property, newValue, (Class)null);
+    }
+    
+    public Object getAttribute(final Object object, final String attribute) {
+        return getAttribute(object, (Class)null, attribute);
+    }
+    
+    public void setAttribute(final Object object, final Class objectType, final String attribute, final Object newValue) {
+        setAttribute(object, objectType, attribute, newValue, (Class)null);
+    }
+    
+    public void setAttribute(final Object object, final String attribute, final Object newValue, final Class newValueType) {
+        setAttribute(object, (Class)null, attribute, newValue, newValueType);
+    }
+    
+    public void setAttribute(final Object object, final String attribute, final Object newValue) {
+        setAttribute(object, (Class)null, attribute, newValue, (Class)null);
+    }
+    
+    public Object invokeThisMethod(final Object object, final Class thisType, final String methodName, final Object[] arguments) {
+        return invokeThisMethod(object, thisType, methodName, arguments, new Class[arguments.length]);
+    }
+    
+    public boolean setThisProperty(final Object object, final Class thisType, final String property, final Object newValue) {
+        return setThisProperty(object, thisType, property, newValue, (Class)null);
+    }
+    
+    public boolean setThisProperty(final Object object, final String property, final Object newValue) {
+        return setThisProperty(object, (Class)null, property, newValue, (Class)null);
+    }
+    
+    //
+    // Methods used to optimise method calling when there are 0, 1, 2, 3 or 4 parameters
+    // We don't bother doing this with super calls as they are far less common
+    //
+       
+    public Object invokeMethod(final Object object, final String methodName) {
+        return invokeMethod(object, (Class)null, methodName, NO_PARAMETERS, NO_PARAMETER_TYPES);
+    }
+       
+    public Object invokeMethod(final Object object, final Class objectType, final String methodName) {
+        return invokeMethod(object, objectType, methodName, NO_PARAMETERS, NO_PARAMETER_TYPES);
+    }
+       
+    public Object invokeMethod(final Object object, final String methodName, final Object p1, final Class c1) {
+        return invokeMethod(object, (Class)null, methodName, new Object[] {p1}, new Class[] {c1});
+    }
+       
+    public Object invokeMethod(final Object object, final Class objectType, final String methodName, final Object p1, final Class c1) {
+        return invokeMethod(object, objectType, methodName, new Object[] {p1}, new Class[] {c1});
+    }
+       
+    public Object invokeMethod(final Object object, final String methodName, final Object p1) {
+        return invokeMethod(object, (Class)null, methodName, new Object[] {p1}, new Class[1]);
+    }
+       
+    public Object invokeMethod(final Object object, final Class objectType, final String methodName, final Object p1) {
+        return invokeMethod(object, objectType, methodName, new Object[] {p1}, new Class[1]);
+    }
+       
+    public Object invokeMethod(final Object object, final String methodName, final Object p1, final Object p2, final Class c1, final Class c2) {
+        return invokeMethod(object, (Class)null, methodName, new Object[] {p1, p2}, new Class[] {c1, c2});
+    }
+       
+    public Object invokeMethod(final Object object, final Class objectType, final String methodName, final Object p1, final Object p2, final Class c1, final Class c2) {
+        return invokeMethod(object, objectType, methodName, new Object[] {p1, p2}, new Class[] {c1, c2});
+    }
+       
+    public Object invokeMethod(final Object object, final String methodName, final Object p1, final Object p2) {
+        return invokeMethod(object, (Class)null, methodName, new Object[] {p1, p2}, new Class[2]);
+    }
+       
+    public Object invokeMethod(final Object object, final Class objectType, final String methodName, final Object p1, final Object p2) {
+        return invokeMethod(object, objectType, methodName, new Object[] {p1, p2}, new Class[2]);
+    }
+       
+    public Object invokeMethod(final Object object, final String methodName, final Object p1, final Object p2, final Object p3, final Class c1, final Class c2, final Class c3) {
+        return invokeMethod(object, (Class)null, methodName, new Object[] {p1, p2, p3}, new Class[] {c1, c2, c3});
+    }
+       
+    public Object invokeMethod(final Object object, final Class objectType, final String methodName, final Object p1, final Object p2, final Object p3, final Class c1, final Class c2, final Class c3) {
+        return invokeMethod(object, objectType, methodName, new Object[] {p1, p2, p3}, new Class[] {c1, c2, c3});
+    }
+       
+    public Object invokeMethod(final Object object, final String methodName, final Object p1, final Object p2, final Object p3) {
+        return invokeMethod(object, (Class)null, methodName, new Object[] {p1, p2, p3}, new Class[3]);
+    }
+       
+    public Object invokeMethod(final Object object, final Class objectType, final String methodName, final Object p1, final Object p2, final Object p3) {
+        return invokeMethod(object, objectType, methodName, new Object[] {p1, p2, p3}, new Class[3]);
+    }
+       
+    public Object invokeMethod(final Object object, final String methodName, final Object p1, final Object p2, final Object p3, final Object p4, final Class c1, final Class c2, final Class c3, final Class c4) {
+        return invokeMethod(object, (Class)null, methodName, new Object[] {p1, p2, p3, p4}, new Class[] {c1, c2, c3, c4});
+    }
+       
+    public Object invokeMethod(final Object object, final Class objectType, final String methodName, final Object p1, final Object p2, final Object p3, final Object p4, final Class c1, final Class c2, final Class c3, final Class c4) {
+        return invokeMethod(object, objectType, methodName, new Object[] {p1, p2, p3, p4}, new Class[] {c1, c2, c3, c4});
+    }
+       
+    public Object invokeMethod(final Object object, final String methodName, final Object p1, final Object p2, final Object p3, final Object p4) {
+        return invokeMethod(object, (Class)null, methodName, new Object[] {p1, p2, p3, p4}, new Class[4]);
+    }
+       
+    public Object invokeMethod(final Object object, final Class objectType, final String methodName, final Object p1, final Object p2, final Object p3, final Object p4) {
+        return invokeMethod(object, objectType, methodName, new Object[] {p1, p2, p3, p4}, new Class[4]);
+    }
+       
+    public Object invokeThisMethod(final Object object, final Class thisType, final String methodName) {
+        return invokeThisMethod(object, thisType, methodName, NO_PARAMETERS, NO_PARAMETER_TYPES);
+    }
+       
+    public Object invokeThisMethod(final Object object, final Class thisType, final String methodName, final Object p1, final Class c1) {
+        return invokeThisMethod(object, thisType, methodName, new Object[] {p1}, new Class[] {c1});
+    }
+       
+    public Object invokeThisMethod(final Object object, final Class thisType, final String methodName, final Object p1) {
+        return invokeThisMethod(object, thisType, methodName, new Object[] {p1}, new Class[1]);
+    }
+
+    public Object invokeThisMethod(final Object object, final Class thisType, final String methodName, final Object p1, final Object p2, final Class c1, final Class c2) {
+        return invokeThisMethod(object, thisType, methodName, new Object[] {p1, p2}, new Class[] {c1, c2});
+    }
+
+    public Object invokeThisMethod(final Object object, final Class thisType, final String methodName, final Object p1, final Object p2) {
+        return invokeThisMethod(object, thisType, methodName, new Object[] {p1, p2}, new Class[2]);
+    }
+
+    public Object invokeThisMethod(final Object object, final Class thisType, final String methodName, final Object p1, final Object p2, final Object p3, final Class c1, final Class c2, final Class c3) {
+        return invokeThisMethod(object, thisType, methodName, new Object[] {p1, p2, p3}, new Class[] {c1, c2, c3});
+    }
+
+    public Object invokeThisMethod(final Object object, final Class thisType, final String methodName, final Object p1, final Object p2, final Object p3) {
+        return invokeThisMethod(object, thisType, methodName, new Object[] {p1, p2, p3}, new Class[3]);
+    }
+
+    public Object invokeThisMethod(final Object object, final Class thisType, final String methodName, final Object p1, final Object p2, final Object p3, final Object p4, final Class c1, final Class c2, final Class c3, final Class c4) {
+        return invokeThisMethod(object, thisType, methodName, new Object[] {p1, p2, p3, p4}, new Class[] {c1, c2, c3, c4});
+    }
+
+    public Object invokeThisMethod(final Object object, final Class thisType, final String methodName, final Object p1, final Object p2, final Object p3, final Object p4) {
+        return invokeThisMethod(object, thisType, methodName, new Object[] {p1, p2, p3, p4}, new Class[4]);
+    }
+}
diff --git a/groovy/experimental/org/codehaus/groovy/runtime/MetaClassActions.java b/groovy/experimental/org/codehaus/groovy/runtime/MetaClassActions.java
new file mode 100644
index 0000000..cb2bd9a
--- /dev/null
+++ b/groovy/experimental/org/codehaus/groovy/runtime/MetaClassActions.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.codehaus.groovy.runtime;
+
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public abstract class MetaClassActions {
+  protected int chooseMethod(final Class[][] formalParameterLists, final Object[] actualParameters) {
+    return 0; // TODO: implement parameter choosing code
+  }
+  
+  public abstract Object doInvokeMethod(Object target, String name, Object[] args) throws Exception;
+}
diff --git a/groovy/experimental/org/codehaus/groovy/runtime/MetaClassActionsGenerator.java b/groovy/experimental/org/codehaus/groovy/runtime/MetaClassActionsGenerator.java
new file mode 100644
index 0000000..146544c
--- /dev/null
+++ b/groovy/experimental/org/codehaus/groovy/runtime/MetaClassActionsGenerator.java
@@ -0,0 +1,382 @@
+package org.codehaus.groovy.runtime;
+import groovy.lang.MetaMethod;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Writer;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.WeakHashMap;
+
+import org.codehaus.groovy.runtimemetaclassactionssupport.DefaultGroovyInstanceMethodGenerator;
+import org.codehaus.groovy.runtimemetaclassactionssupport.DefaultGroovyStaticMethodGenerator;
+import org.codehaus.groovy.runtimemetaclassactionssupport.InstanceMethodGenerator;
+import org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator;
+import org.codehaus.groovy.runtimemetaclassactionssupport.ReflectionMethodGenerator;
+import org.codehaus.groovy.runtimemetaclassactionssupport.StaticMethodGenerator;
+
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class MetaClassActionsGenerator {
+  private static final Map actionsInfoMap = new WeakHashMap();
+  
+  static {
+    actionsInfoMap.put(Object.class, new ActionsInfo(Object.class));
+  }
+
+  public static MetaClassActions getActions(final Class claz) {
+    return getActionsInfo(claz).getActions();
+  }
+  
+  protected static ActionsInfo getActionsInfo(final Class claz) {
+  ActionsInfo classInfo = (ActionsInfo)actionsInfoMap.get(claz);
+  
+    if (classInfo == null) {
+    final Class superClass = claz.getSuperclass();
+    
+      if (superClass != null) {
+        getActionsInfo(superClass); // ensure that the superclass information has been created
+      }
+      
+      classInfo = new ActionsInfo(claz);
+      actionsInfoMap.put(claz, classInfo);
+    }
+    
+    return classInfo;
+  }
+}
+
+class ActionsInfo {
+  private static Map defaultGroovyMethodsMap = new HashMap();
+  static {
+    try {
+      final Method[] defaultGroovyMethods = Class.forName("org.codehaus.groovy.runtime.DefaultGroovyMethods").getDeclaredMethods();
+      
+      for (int i = 0; i != defaultGroovyMethods.length; i++) {
+        final Method method = defaultGroovyMethods[i];
+        
+        if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) == (Modifier.PUBLIC | Modifier.STATIC)) {
+        final Class[] parameterTypes = method.getParameterTypes();
+        
+          if (parameterTypes.length != 0) {
+          List methodList = (List)defaultGroovyMethodsMap.get(parameterTypes[0]);
+          
+            if (methodList == null) {
+              methodList = new LinkedList();
+              defaultGroovyMethodsMap.put(parameterTypes[0], methodList);
+            }
+
+            methodList.add(new DefaultGroovyInstanceMethodGenerator(method));
+          }
+        }
+      }
+    } catch (SecurityException e) {
+      e.printStackTrace();
+    } catch (ClassNotFoundException e) {
+      e.printStackTrace();
+    }
+  }
+  
+  private static Map defaultGroovyStaticMethodsMap = new HashMap();
+  static {
+    try {
+      final Method[] defaultGroovyStaticMethods = Class.forName("org.codehaus.groovy.runtime.DefaultGroovyStaticMethods").getDeclaredMethods();
+      
+      for (int i = 0; i != defaultGroovyStaticMethods.length; i++) {
+        final Method method = defaultGroovyStaticMethods[i];
+        
+        if ((method.getModifiers() & (Modifier.PUBLIC | Modifier.STATIC)) == (Modifier.PUBLIC | Modifier.STATIC)) {
+        final Class[] parameterTypes = method.getParameterTypes();
+        
+          if (parameterTypes.length != 0) {
+          List methodList = (List)defaultGroovyStaticMethodsMap.get(parameterTypes[0]);
+          
+            if (methodList == null) {
+              methodList = new LinkedList();
+              defaultGroovyStaticMethodsMap.put(parameterTypes[0], methodList);
+            }
+
+            methodList.add(new DefaultGroovyStaticMethodGenerator(method));
+          }
+        }
+      }
+    } catch (SecurityException e) {
+      e.printStackTrace();
+    } catch (ClassNotFoundException e) {
+      e.printStackTrace();
+    }
+  }
+  
+  private MetaClassActions actions = null;
+  private final List generators = new LinkedList();
+
+  public ActionsInfo(final Class claz) {
+  final Package pack = claz.getPackage();
+  final String packageName = "groovy.runtime.metaclassactions." + ((pack == null) ? "" : pack.getName() + ".");
+  
+    setUpGenerators(claz);  // Need to do this even if the class is already generated
+  
+    try {
+    final Class actionsClass = Class.forName(packageName + claz.getSimpleName() + "MetaClassActions");
+    
+      this.actions = (MetaClassActions)actionsClass.newInstance();
+    } catch (final Exception e1) {
+    final File generatedFile = new File("generated/" + packageName.replace('.', '/'));
+      
+      generatedFile.mkdirs();
+      
+      try {
+      final Writer out = new FileWriter(new File(generatedFile,  claz.getSimpleName() + "MetaClassActions.java"));
+      
+        try {
+          out.write(generateJavaFile(claz));
+        } finally {    
+          out.close();
+        }
+      } catch (final IOException e) {
+         e.printStackTrace();
+      }
+    }  
+  }
+  
+  public MetaClassActions getActions() {
+    return this.actions;
+  }
+  
+  private void setUpGenerators(final Class claz) {
+  final Method[] methods = claz.getDeclaredMethods();
+  final List defaultMethods = (List)defaultGroovyMethodsMap.get(claz);
+  final List defaultStaticMethods = (List)defaultGroovyStaticMethodsMap.get(claz);
+  
+    if (defaultMethods != null) {
+      this.generators.addAll(defaultMethods);
+    }
+    
+    if (defaultStaticMethods != null) {
+      this.generators.addAll(defaultStaticMethods);
+    }
+    
+    for (int i = 0; i != methods.length; i++) {
+    final Method method = methods[i];
+    final int methodModifiers = method.getModifiers();
+    
+      if ((methodModifiers & Modifier.PUBLIC) != 0) {        
+        if ((methodModifiers & Modifier.STATIC) != 0) {
+          this.generators.add(new StaticMethodGenerator(method));
+        } else {
+          this.generators.add(new InstanceMethodGenerator(method));
+        }
+      } else if ((methodModifiers & Modifier.PROTECTED) != 0) {
+        this.generators.add(new ReflectionMethodGenerator(method));
+      }
+    }
+    
+    if (claz != Object.class) {
+    final Class superClass = claz.getSuperclass();
+    
+      if (superClass != null) {
+        this.generators.addAll(MetaClassActionsGenerator.getActionsInfo(superClass).generators);
+      }
+      
+      final Class[] interfaces = claz.getInterfaces();
+      
+      for (int i = 0; i != interfaces.length; i++) {
+        this.generators.addAll(MetaClassActionsGenerator.getActionsInfo(interfaces[i]).generators);
+      }
+    }
+    
+    Collections.sort(this.generators, new Comparator() {
+      public int compare(final Object lhs, final Object rhs) {
+      final int lhsNumberOfParameters = ((MethodGenerator)lhs).getNumberOfParameters();
+      final int rhsNumberOfParameters = ((MethodGenerator)rhs).getNumberOfParameters();
+      
+        if (lhsNumberOfParameters  == rhsNumberOfParameters) {
+        final String lhsName = ((MethodGenerator)lhs).getName();
+        final String rhsName = ((MethodGenerator)rhs).getName();
+        
+          return lhsName.compareTo(rhsName);
+        } else if (lhsNumberOfParameters  < rhsNumberOfParameters) {
+          return -1;
+        } else {
+          return 1;
+        }
+      }
+    });
+    
+    if (this.generators.size() > 1) {
+    MethodGenerator g1 = (MethodGenerator)this.generators.get(this.generators.size() - 1);
+      
+      for (int i = this.generators.size() - 2; i > -1 ; i--) {
+      final MethodGenerator g2 = (MethodGenerator)this.generators.get(i);
+      
+        if (g1.isOverloaded(g2)) {
+          this.generators.remove(i + 1);
+          g1 = g1.processOverloaded(claz, g2);
+          this.generators.set(i, g1);
+        } else {
+          g1 = g2;
+        }
+      }
+    }
+  }
+  
+  private String generateJavaFile(final Class claz) {
+  final StringBuffer code = new StringBuffer();
+    
+    generateStartOfFile(claz, code);
+    
+    final Iterator iter = this.generators.iterator();
+    int methodIndex = 0;
+    while (iter.hasNext()) {
+    final MethodGenerator gen = (MethodGenerator)iter.next();
+    
+      code.append(gen.generateDeclaration(methodIndex++));
+    }
+    
+    generateInvokemethod(claz, code);
+    
+    generateEndOfFile(claz, code);
+    
+    return code.toString();
+  }
+  
+  private void addMethod(final Set methodSet, final Map methodMap, final List methods) {
+  final Iterator methodIterator = methods.iterator();
+    
+    while (methodIterator.hasNext()) {
+      final MetaMethod method = (MetaMethod)methodIterator.next();
+      final String name = method.getName();
+      
+      methodSet.add(name);
+      
+      List methodList = (List)methodMap.get(name);
+      if (methodList == null) {
+        methodList = new LinkedList();
+        
+        methodMap.put(name, methodList);
+      }
+      methodList.add(method);
+    }
+  }
+  
+  private void generateStartOfFile(final Class claz, final StringBuffer code) {
+  final Package pack = claz.getPackage();
+  final String packageName = (pack == null) ? "" : "." + pack.getName();
+  
+    code.append("package groovy.runtime.metaclassactions").append(packageName).append(";\n");
+    code.append("import java.lang.reflect.Method;\n");
+    code.append("import java.security.AccessController;\n");
+    code.append("import java.security.PrivilegedAction;\n");
+    code.append("public class ").append(claz.getSimpleName()).append("MetaClassActions extends org.codehaus.groovy.runtime.MetaClassActions {\n");
+  }
+  
+  private void generateInvokemethod(final Class claz, final StringBuffer code) {
+    
+    code.append("public Object doInvokeMethod(final Object target, final String name, final Object[] args) ");
+    code.append("throws Exception {\n");
+    
+    if (this.generators.size() != 0) {
+    final List generatorSubset = new LinkedList();
+    MethodGenerator firstGenerator = (MethodGenerator)this.generators.get(0);
+    int currentArgsLength = firstGenerator.getNumberOfParameters();
+    char firstChar = firstGenerator.getName().charAt(0);
+    final Iterator iter = this.generators.iterator();
+    
+      while (iter.hasNext()) {
+      final MethodGenerator generator = (MethodGenerator)iter.next();
+      
+        if (currentArgsLength == generator.getNumberOfParameters()) {
+          generatorSubset.add(generator);
+        } else {
+          generateCalls(claz, code, currentArgsLength, generatorSubset);
+          currentArgsLength = generator.getNumberOfParameters();
+        }
+      }
+      
+      generateCalls(claz, code, currentArgsLength, generatorSubset);
+      
+      code.setLength(code.length() - 6);  // chop the trailing " else "     
+    }
+    
+    code.append("\nreturn groovy.lang.MetaClass.NO_METHOD_FOUND;");
+    code.append("\n}\n");
+  }
+  
+  private void generateCalls(final Class claz, final StringBuffer code, final int currentArgsLength, final List generatorSubset) {
+    if (generatorSubset.size() != 0) {
+      code.append("if (args.length == ").append(currentArgsLength).append(") {\n");
+      code.append("switch(name.charAt(0)) {");
+      
+      boolean firstCase = true;
+      final Iterator iter1 = generatorSubset.iterator();
+      char fc = 0;
+      
+      while (iter1.hasNext()) {
+      final MethodGenerator generator1 = (MethodGenerator)iter1.next();
+      final String name = generator1.getName();
+      
+        if (fc != name.charAt(0)) {
+          if (firstCase) {
+            firstCase = false;
+          } else {
+            code.append(" else {\n");
+            code.append("return groovy.lang.MetaClass.NO_METHOD_FOUND;\n");
+            code.append("}");
+          }
+          fc = name.charAt(0);
+          code.append("\ncase '").append(fc).append("' :\n");
+          code.append("if (\"").append(generator1.getName()).append("\".equals(name)) {\n");
+          code.append(generator1.generateCall(claz));
+          code.append("}");
+        } else {
+          code.append(" else if (\"").append(generator1.getName()).append("\".equals(name)) {\n");
+          code.append(generator1.generateCall(claz));
+          code.append("}");
+        }
+      }
+      
+      code.append(" else {\n");
+      code.append("return groovy.lang.MetaClass.NO_METHOD_FOUND;\n");
+      code.append("}\n");
+      code.append("default:\n");
+      code.append("return groovy.lang.MetaClass.NO_METHOD_FOUND;\n");
+      code.append("}\n");
+      code.append("} else ");
+      
+      generatorSubset.clear();
+    }
+  }
+  
+  private void generateEndOfFile(final Class claz, final StringBuffer code) {
+    code.append("}\n");
+  }
+}
diff --git a/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/DefaultGroovyInstanceMethodGenerator.java b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/DefaultGroovyInstanceMethodGenerator.java
new file mode 100644
index 0000000..30b4666
--- /dev/null
+++ b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/DefaultGroovyInstanceMethodGenerator.java
@@ -0,0 +1,70 @@
+package org.codehaus.groovy.runtimemetaclassactionssupport;
+import java.lang.reflect.Method;
+
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class DefaultGroovyInstanceMethodGenerator extends MethodGenerator {
+
+  /**
+   * @param method
+   */
+  public DefaultGroovyInstanceMethodGenerator(Method method) {
+    super(method);
+  }
+  
+  public int getNumberOfParameters() {
+    return this.method.getParameterTypes().length - 1;
+  }
+  
+  /* (non-Javadoc)
+   * @see MethodGenerator#getParameterTypes()
+   */
+  public Class[] getParameterTypes() {
+  final Class[] parameterTypes = this.method.getParameterTypes();
+  final Class[] result = new Class[parameterTypes.length - 1];
+  
+    System.arraycopy(parameterTypes, 1, result, 0, result.length);
+    return result;
+  }
+
+  protected void startCall(final StringBuffer code) {
+    code.append(DEFAULT_GROOVY_METHODS).append('.').append(this.method.getName()).append('(');
+  }
+  
+  protected StringBuffer addParameters(final StringBuffer code, final Class[] parameterTypes) {   
+  final Class[] newParameterTypes = new Class[parameterTypes.length - 1];
+    
+    System.arraycopy(parameterTypes, 1, newParameterTypes, 0, newParameterTypes.length);
+    
+    code.append('(');
+    getFullName(parameterTypes[0], code);
+    
+    if (newParameterTypes.length == 0) {
+      code.append(")target");
+    } else {
+      code.append(")target, ");
+    }
+    
+    return super.addParameters(code, newParameterTypes);
+  }
+}
diff --git a/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/DefaultGroovyStaticMethodGenerator.java b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/DefaultGroovyStaticMethodGenerator.java
new file mode 100644
index 0000000..8a156c6
--- /dev/null
+++ b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/DefaultGroovyStaticMethodGenerator.java
@@ -0,0 +1,41 @@
+package org.codehaus.groovy.runtimemetaclassactionssupport;
+import java.lang.reflect.Method;
+
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class DefaultGroovyStaticMethodGenerator extends MethodGenerator {
+
+  /**
+   * @param method
+   */
+  public DefaultGroovyStaticMethodGenerator(Method method) {
+    super(method);
+  }
+
+  /* (non-Javadoc)
+   * @see MethodGenerator#startCall(java.lang.StringBuffer)
+   */
+  protected void startCall(StringBuffer code) {
+    code.append(DEFAULT_STATIC_GROOVY_METHODS).append('.').append(this.method.getName()).append('(');
+  }
+}
diff --git a/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/InstanceMethodGenerator.java b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/InstanceMethodGenerator.java
new file mode 100644
index 0000000..863d970
--- /dev/null
+++ b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/InstanceMethodGenerator.java
@@ -0,0 +1,35 @@
+package org.codehaus.groovy.runtimemetaclassactionssupport;
+
+import java.lang.reflect.Method;
+
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class InstanceMethodGenerator extends MethodGenerator {
+  public InstanceMethodGenerator(final Method method) {
+    super(method);
+  }
+  
+  protected void startCall(final StringBuffer code) {
+    code.append("((").append(this.method.getDeclaringClass().getName()).append(")target).").append(this.method.getName()).append('(');
+  }
+}
diff --git a/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/MethodGenerator.java b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/MethodGenerator.java
new file mode 100644
index 0000000..7d67444
--- /dev/null
+++ b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/MethodGenerator.java
@@ -0,0 +1,193 @@
+package org.codehaus.groovy.runtimemetaclassactionssupport;
+import groovy.lang.GString;
+
+import java.lang.reflect.Method;
+
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public abstract class MethodGenerator {
+  protected final static String DEFAULT_GROOVY_METHODS = "org.codehaus.groovy.runtime.DefaultGroovyMethods";
+  protected final static String DEFAULT_STATIC_GROOVY_METHODS = "org.codehaus.groovy.runtime.DefaultGroovyStaticMethods";
+  
+  protected final Method method;
+    
+  /**
+   * @param method
+   */
+  public MethodGenerator(Method method) {
+    this.method = method;
+  }
+  
+  public String getName() {
+    return this.method.getName();
+  }
+  
+  public int getNumberOfParameters() {
+    return this.method.getParameterTypes().length;
+  }
+  
+  public Class[] getParameterTypes() {
+    return this.method.getParameterTypes();
+  }
+  
+  public boolean isOverloaded(final MethodGenerator other) {
+    return getName().equals(other.getName()) && getNumberOfParameters() == other.getNumberOfParameters();
+  }
+  
+  public MethodGenerator processOverloaded(final Class claz, final MethodGenerator other) {
+  final Class[] p1 = getParameterTypes();
+  final Class[] p2 = other.getParameterTypes();
+  
+    for (int i = 0; i != p1.length; i++) {
+      if (p1[i] != p2[i]) {
+        return new OverloadedMethodsGenerator(this, other); // We have an overloaded method 
+      }
+    }
+    
+    // We have a duplicate - return the one which has its declaring class as the current class
+    
+    if (this.method.getDeclaringClass() == claz) {
+      return this;
+    } else {
+      return other;
+    }
+  }
+   
+  public String generateDeclaration(int methodIndex) {
+    return "";
+  }
+  
+  public String generateCall(final Class claz) {
+  final StringBuffer code = new StringBuffer();
+  final Class returnType = method.getReturnType();
+    
+    if (returnType.isPrimitive()) {
+      if (returnType == void.class) {
+        makeCall(code).append(";\nreturn null;\n");
+      } else if (returnType == int.class) {
+        code.append("return new Integer(");
+        makeCall(code).append(");\n");  
+      } else if (returnType == long.class) {
+        code.append("return new Long(");
+        makeCall(code).append(");\n");  
+      } else if (returnType == short.class) {
+        code.append("return new Short(");
+        makeCall(code).append(");\n");  
+      } else if (returnType == char.class) {
+        code.append("return new Char(");
+        makeCall(code).append(");\n");  
+      } else if (returnType == byte.class) {
+        code.append("return new Byte(");
+        makeCall(code).append(");\n");  
+      } else if (returnType == boolean.class) {
+        code.append("return new Boolean(");
+        makeCall(code).append(");\n");  
+      } else if (returnType == float.class) {
+        code.append("return new Float(");
+        makeCall(code).append(");\n");  
+      } else if (returnType == double.class) {
+        code.append("return new Double(");
+        makeCall(code).append(");\n");  
+      } else {
+        code.append("**** ERROR ***** found primitive return type: " + returnType.getName());
+      }
+    } else {
+      code.append("return ");
+      makeCall(code).append(";\n");  
+    }
+    
+    return code.toString();
+  }
+  
+  private StringBuffer makeCall(final StringBuffer code) {
+    startCall(code);
+    addParameters(code, this.method.getParameterTypes()).append(')');
+    
+    return code;
+  }
+  
+  protected abstract void startCall(StringBuffer code);
+  
+  protected StringBuffer addParameters(final StringBuffer code, final Class[] parameterTypes) {
+    if (parameterTypes.length != 0) {
+      for (int i = 0; i != parameterTypes.length; i++) {
+      final Class parameterType = parameterTypes[i]; 
+        
+        if (parameterType.isPrimitive()) {
+          if (parameterType == int.class) {
+            code.append("((Number)").append("args[");
+            code.append(i).append("]).intValue(), ");
+          } else if (parameterType == long.class) {
+            code.append("((Number)").append("args[");
+            code.append(i).append("]).longValue(), ");
+          } else if (parameterType == short.class) {
+            code.append("((Number)").append("args[");
+            code.append(i).append("]).shortValue(), ");
+          } else if (parameterType == char.class) {
+            code.append("((Character)").append("args[");
+            code.append(i).append("]).charValue(), ");
+          } else if (parameterType == byte.class) {
+            code.append("((Number)").append("args[");
+            code.append(i).append("]).byteValue(), ");
+          } else if (parameterType == boolean.class) {
+            code.append("((Boolean)").append("args[");
+            code.append(i).append("]).booleanValue(), ");
+          } else if (parameterType == float.class) {
+            code.append("((Number)").append("args[");
+            code.append(i).append("]).floatValue(), ");
+          } else if (parameterType == double.class) {
+            code.append("((Number)").append("args[");
+            code.append(i).append("]).doubleValue(), ");
+          } else {
+            code.append("**** ERROR ***** found primative parameter type: " + parameterType.getName());
+          }
+        } else if (parameterType == String.class) {
+          code.append("args[").append(i).append("].toString(), ");
+        } else if (parameterType == GString.class) {
+          code.append("(groovy.lang.GString)((args[").append(i).append("] instanceof groovy.lang.GString) ? ");
+          code.append("args[").append(i).append("] : ");
+          code.append("new groovy.lang.GString(args[").append(i).append("].toString()){public String[] getStrings() {return (String [])this.getValues();}}), ");
+        } else {
+          code.append('(');
+          getFullName(parameterType, code);
+          code.append(")args[").append(i).append("], ");
+        }
+      }
+      
+      code.setLength(code.length() - 2);  // trim the last "' " off
+    }
+    
+    return code;
+  }
+  
+  protected StringBuffer getFullName(final Class claz, final StringBuffer code) {
+  final Package classPackage = (claz.isArray()) ? claz.getComponentType().getPackage() : claz.getPackage(); 
+  
+    if (classPackage != null) {
+      code.append(classPackage.getName()).append('.') ;
+    }
+
+    return code.append(claz.getSimpleName());
+  }
+}
+
diff --git a/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/OverloadedMethodsGenerator.java b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/OverloadedMethodsGenerator.java
new file mode 100644
index 0000000..a7c0a60
--- /dev/null
+++ b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/OverloadedMethodsGenerator.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.codehaus.groovy.runtimemetaclassactionssupport;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class OverloadedMethodsGenerator extends MethodGenerator {
+  private final MethodGenerator firstMethod;
+  private final List generatorList = new LinkedList();
+  private int methodIndex = 0;
+  
+  public OverloadedMethodsGenerator(final MethodGenerator firstMethod, final MethodGenerator secondMethod) {
+    super(null);
+    
+    this.firstMethod = firstMethod;
+    this.generatorList.add(firstMethod);
+    this.generatorList.add(secondMethod);
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator#generateCall(java.lang.Class)
+   */
+  public String generateCall(final Class claz) {
+  final StringBuffer code = new StringBuffer();
+  final Iterator iter = this.generatorList.iterator();
+  int i = 0;
+  
+    code.append("switch(chooseMethod(possible").append(getName()).append("Parameters").append(this.methodIndex).append(", args)) {\n");
+    while (iter.hasNext()) {
+    final MethodGenerator generator = (MethodGenerator)iter.next();
+      
+      code.append("case ").append(i++).append(": \n");
+      code.append(generator.generateCall(claz));
+    }
+    code.append("default:\n");
+    code.append("return groovy.lang.MetaClass.NO_METHOD_FOUND;\n");
+    code.append("}\n");
+
+    return code.toString();
+  }
+  
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator#startCall(java.lang.StringBuffer)
+   */
+  protected void startCall(final StringBuffer code) {
+
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator#generateDeclaration(int)
+   */
+  public String generateDeclaration(final int methodIndex) {
+  final StringBuffer code = new StringBuffer();
+  final Iterator iter = this.generatorList.iterator();
+  int i = 1;
+  
+    this.methodIndex = methodIndex;
+  
+    while (iter.hasNext()) {
+    final MethodGenerator generator = (MethodGenerator)iter.next();
+    
+      code.append(generator.generateDeclaration((methodIndex << 16) | i )); // TODO: find a better way of getting unique indexes
+    }
+    
+    code.append("private final Class[][] possible").append(getName()).append("Parameters").append(methodIndex).append(" = new Class[][]{\n");
+    final Iterator iter1 = this.generatorList.iterator();
+
+    while (iter1.hasNext()) {
+    final MethodGenerator generator = (MethodGenerator)iter1.next();
+    final Class[] parameterTypes = generator.getParameterTypes();
+    
+      code.append("new Class[] {");
+      
+      for (int j = 0; j != parameterTypes.length; j++) {
+      final Class parameterType = parameterTypes[j];
+      
+        if (parameterType.isPrimitive()) {
+          if (parameterType == int.class) {
+            code.append("int");
+          } else if (parameterType == long.class) {
+            code.append("long");
+          } else if (parameterType == short.class) {
+            code.append("short");
+          } else if (parameterType == char.class) {
+            code.append("char");
+          } else if (parameterType == byte.class) {
+            code.append("byte");
+          } else if (parameterType == boolean.class) {
+            code.append("boolean");
+          } else if (parameterType == float.class) {
+            code.append("float");
+          } else if (parameterType == double.class) {
+            code.append("double");
+          } else {
+            code.append("**** ERROR ***** found primative parameter type: " + parameterType.getName());
+          }
+        } else {
+          getFullName(parameterType, code);
+        }
+        code.append(".class, ");
+      }
+      
+      code.setLength(code.length() - 2);  // Trim the trailing ", "
+      code.append("},\n ");
+    }
+    
+    code.setLength(code.length() - 3);  // Trim the trailing ",\n "
+    code.append("\n};\n");
+    return code.toString();
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator#getName()
+   */
+  public String getName() {
+    return firstMethod.getName();
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator#getNumberOfParameters()
+   */
+  public int getNumberOfParameters() {
+    return firstMethod.getNumberOfParameters();
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator#getParameterTypes()
+   */
+  public Class[] getParameterTypes() {
+    return firstMethod.getParameterTypes();
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator#isOverloaded(org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator)
+   */
+  public boolean isOverloaded(final MethodGenerator other) {
+     return firstMethod.isOverloaded(other);
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator#processOverloaded(java.lang.Class, org.codehaus.groovy.runtimemetaclassactionssupport.MethodGenerator)
+   */
+  public MethodGenerator processOverloaded(final Class claz, final MethodGenerator other) {
+    if (other instanceof OverloadedMethodsGenerator) {
+    final Iterator iter = ((OverloadedMethodsGenerator)other).generatorList.iterator();
+    
+      while (iter.hasNext()) {
+        processOverloaded(claz, (MethodGenerator)iter.next());
+      }
+    } else {
+    final Class[] p1 = other.getParameterTypes();
+  
+      for (int i = 0; i != this.generatorList.size(); i++) {
+      final MethodGenerator generator = (MethodGenerator)this.generatorList.get(i);
+      final Class[] p2 = generator.getParameterTypes();
+      int j = 0;
+      
+        while (p1[j] == p2[j] && ++j != p1.length);
+        
+        if (j == p1.length) {
+          // other is a duplicate of this method
+          if (other.method.getDeclaringClass() == claz) {
+            this.generatorList.add(i, other);
+          }
+          
+          return this;
+        }
+      }
+      
+      this.generatorList.add(other);
+    }
+    
+    return this;
+  }
+}
diff --git a/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/ReflectionMethodGenerator.java b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/ReflectionMethodGenerator.java
new file mode 100644
index 0000000..d751c1a
--- /dev/null
+++ b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/ReflectionMethodGenerator.java
@@ -0,0 +1,106 @@
+package org.codehaus.groovy.runtimemetaclassactionssupport;
+import java.lang.reflect.Method;
+
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class ReflectionMethodGenerator extends MethodGenerator {
+  private int methodIndex;
+  
+  /**
+   * @param method
+   */
+  public ReflectionMethodGenerator(Method method) {
+    super(method);
+  }
+
+  /* (non-Javadoc)
+   * @see MethodGenerator#generateCall()
+   */
+  public String generateCall(final Class claz) {
+  final StringBuffer code = new StringBuffer();
+    
+    code.append("return ").append(this.method.getName() + this.methodIndex).append("Method.invoke(target, args);\n");
+
+    return code.toString();
+  }
+
+  public String generateDeclaration(final int methodIndex) {
+  final StringBuffer code = new StringBuffer();
+    
+    this.methodIndex = methodIndex;
+    
+    code.append("private Method ").append(this.method.getName() + this.methodIndex).append("Method;\n");
+    code.append("{\ntry {\n").append(this.method.getName() + this.methodIndex).append("Method = ");
+    code.append(this.method.getDeclaringClass().getName()).append(".class.getDeclaredMethod(\"").append(this.method.getName()).append("\", new Class[] {");
+    
+    final Class[] parameterTypes = this.method.getParameterTypes();
+    if (parameterTypes.length != 0){
+      for (int i = 0; i != parameterTypes.length; i++) {
+      final Class parameterType = parameterTypes[i];
+      
+        if (parameterType.isPrimitive()) {
+          if (parameterType == void.class) {
+            code.append("void.class, ");
+          } else if (parameterType == int.class) {
+            code.append("void.class, ");
+          } else if (parameterType == long.class) {
+            code.append("void.class, ");
+          } else if (parameterType == short.class) {
+            code.append("void.class, ");
+          } else if (parameterType == char.class) {
+            code.append("void.class, ");
+          } else if (parameterType == byte.class) {
+            code.append("void.class, ");
+          } else if (parameterType == boolean.class) {
+            code.append("void.class, ");
+          } else if (parameterType == float.class) {
+            code.append("void.class, ");
+          } else if (parameterType == double.class) {
+            code.append("void.class, ");
+          } else {
+            code.append("**** ERROR ***** found primative return type: " + parameterType.getName());
+          }
+        } else {
+          getFullName(parameterType, code);
+          code.append(".class, ");
+        }
+      }
+      
+      code.setLength(code.length() - 2);   // trim the last ", " off
+    }
+    
+    code.append("});\n");
+    code.append("AccessController.doPrivileged(new PrivilegedAction() {\npublic Object run() {\n");
+    code.append(this.method.getName() + this.methodIndex).append("Method.setAccessible(true);\nreturn null;\n}\n});\n");
+    code.append("} catch (final NoSuchMethodException e) {}\n}\n");
+    
+    return code.toString();
+  }
+  
+  /* (non-Javadoc)
+   * @see MethodGenerator#startCall(java.lang.StringBuffer)
+   */
+  protected void startCall(StringBuffer code) {
+  }
+
+}
diff --git a/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/StaticMethodGenerator.java b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/StaticMethodGenerator.java
new file mode 100644
index 0000000..2cae3ce
--- /dev/null
+++ b/groovy/experimental/org/codehaus/groovy/runtimemetaclassactionssupport/StaticMethodGenerator.java
@@ -0,0 +1,38 @@
+package org.codehaus.groovy.runtimemetaclassactionssupport;
+import java.lang.reflect.Method;
+
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class StaticMethodGenerator extends MethodGenerator {
+
+  /**
+   * @param method
+   */
+  public StaticMethodGenerator(final Method method) {
+    super(method);
+  }
+
+  protected void startCall(final StringBuffer code) {
+    code.append(this.method.getDeclaringClass().getName()).append('.').append(this.method.getName()).append('(');
+  }
+}
diff --git a/groovy/pom.xml b/groovy/pom.xml
new file mode 100644
index 0000000..d26c9a6
--- /dev/null
+++ b/groovy/pom.xml
@@ -0,0 +1,775 @@
+<?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>org.codehaus.groovy</groupId>
+    <artifactId>groovy</artifactId>
+    <name>Groovy</name>
+    <packaging>jar</packaging>
+    
+    <version>1.5.0</version>
+    
+    <description>
+        Groovy: A powerful, dynamic language for the JVM
+    </description>
+    
+    <url>http://groovy.codehaus.org/</url>
+    
+    <organization>
+        <name>The Codehaus</name>
+        <url>http://codehaus.org</url>
+    </organization>
+    
+    <inceptionYear>2003</inceptionYear>
+    
+    <scm>
+        <connection>scm:svn:http://svn.groovy.codehaus.org/browse/groovy/trunk/groovy/groovy-core</connection>
+        <developerConnection>scm:svn:https://${maven.username}@svn.groovy.codehaus.org/browse/groovy/trunk/groovy/groovy-core</developerConnection>
+        <url>http://svn.groovy.codehaus.org/browse/groovy/trunk/groovy/groovy-core</url>
+    </scm>
+
+    <mailingLists>
+        <mailingList>
+            <name>Groovy JSR Discussion List</name>
+            <archive>http://dir.gmane.org/gmane.comp.lang.groovy.jsr</archive>
+        </mailingList>
+        <mailingList>
+            <name>Groovy Developer List</name>
+            <archive>http://dir.gmane.org/gmane.comp.lang.groovy.devel</archive>
+        </mailingList>
+        <mailingList>
+            <name>Groovy User List</name>
+            <archive>http://dir.gmane.org/gmane.comp.lang.groovy.user</archive>
+        </mailingList>
+    </mailingLists>
+
+    <distributionManagement>
+        <repository>
+            <id>codehaus.org</id>
+            <name>Groovy Central Repository</name>
+            <url>dav:https://dav.codehaus.org/repository/groovy/</url>
+        </repository>
+        <snapshotRepository>
+            <id>codehaus.org</id>
+            <name>Groovy Central Development Repository</name>
+            <url>dav:https://dav.codehaus.org/snapshots.repository/groovy/</url>
+        </snapshotRepository>
+        <!-- not currently used -->
+        <site>
+            <id>codehaus.org</id>
+            <name>Groovy Web Site</name>
+            <url>dav:https://dav.codehaus.org/groovy</url>
+        </site>
+    </distributionManagement>
+
+    <developers>
+        <developer>
+            <name>Guillaume Laforge</name>
+            <id>glaforge</id>
+            <email></email>
+            <organization>Octo Technology</organization>
+            <roles>
+                <role>Project Manager</role>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>bob mcwhirter</name>
+            <id>bob</id>
+            <email>bob@werken.com</email>
+            <organization>The Werken Company</organization>
+            <roles>
+                <role>Founder</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>James Strachan</name>
+            <id>jstrachan</id>
+            <email>james@coredevelopers.com</email>
+            <organization>Core Developers Network</organization>
+            <roles>
+                <role>Founder</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Joe Walnes</name>
+            <id>joe</id>
+            <email></email>
+            <organization>ThoughtWorks</organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Chris Stevenson</name>
+            <id>skizz</id>
+            <email></email>
+            <organization>ThoughtWorks</organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Jamie McCrindle</name>
+            <id>jamiemc</id>
+            <email></email>
+            <organization>Three</organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Matt Foemmel</name>
+            <id>mattf</id>
+            <email></email>
+            <organization>ThoughtWorks</organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Sam Pullara</name>
+            <id>spullara</id>
+            <email>sam@sampullara.com</email>
+            <organization></organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Kasper Nielsen</name>
+            <id>kasper</id>
+            <email></email>
+            <organization></organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Travis Kay</name>
+            <id>travis</id>
+            <email></email>
+            <organization></organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Zohar Melamed</name>
+            <id>zohar</id>
+            <email></email>
+            <organization></organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>John Wilson</name>
+            <id>jwilson</id>
+            <email>tug@wilson.co.uk</email>
+            <organization>The Wilson Partnership</organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Chris Poirier</name>
+            <id>cpoirier</id>
+            <email>cpoirier@dreaming.org</email>
+            <organization></organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Christiaan ten Klooster</name>
+            <id>ckl</id>
+            <email>ckl@dacelo.nl</email>
+            <organization>Dacelo WebDevelopment</organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Steve Goetze</name>
+            <id>goetze</id>
+            <email>goetze@dovetail.com</email>
+            <organization>Dovetailed Technologies, LLC</organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Bing Ran</name>
+            <id>bran</id>
+            <email>b55r@sina.com</email>
+            <organization>Leadingcare</organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Jeremy Rayner</name>
+            <id>jez</id>
+            <email>jeremy.rayner@gmail.com</email>
+            <organization>javanicus</organization>
+            <roles>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>John Stump</name>
+            <id>jstump</id>
+            <email>johnstump2@yahoo.com</email>
+            <organization></organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Jochen Theodorou</name>
+            <id>blackdrag</id>
+            <email>blackdrag@gmx.org</email>
+            <organization></organization>
+            <roles>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Russel Winder</name>
+            <id>russel</id>
+            <email>russel@russel.org.uk</email>
+            <organization>Concertant LLP &amp; It'z Interactive Ltd</organization>
+            <roles>
+                <role>Developer</role>
+                <role>Founder of Gant</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Pilho Kim</name>
+            <id>phk</id>
+            <email>phkim@cluecom.co.kr</email>
+            <organization></organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Christian Stein</name>
+            <id>cstein</id>
+            <email>sormuras@gmx.de</email>
+            <organization>CTSR.de</organization>
+            <roles>
+                <role>Developer Emeritus</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Dierk Koenig</name>
+            <id>mittie</id>
+            <email>dierk.koenig@canoo.com</email>
+            <organization>Canoo Engineering AG</organization>
+            <roles>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Paul King</name>
+            <id>paulk</id>
+            <email>paulk@asert.com.au</email>
+            <organization>ASERT, Australia</organization>
+            <roles>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Guillaume Alleon</name>
+            <id>galleon</id>
+            <email>guillaume.alleon@gmail.com</email>
+            <organization></organization>
+            <roles>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Jason Dillon</name>
+            <id>user57</id>
+            <email>jason@planet57.com</email>
+            <organization></organization>
+            <roles>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Danno Ferrin</name>
+            <id>shemnon</id>
+            <email></email>
+            <organization></organization>
+            <roles>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>James Williams</name>
+            <id>jwill</id>
+            <email></email>
+            <organization></organization>
+            <roles>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Andres Almiray</name>
+            <id>aalmiray</id>
+            <email>aalmiray@users.sourceforge.net</email>
+            <organization></organization>
+            <roles>
+                <role>Developer</role>
+            </roles>
+        </developer>
+        <developer>
+            <name>Marc Guillemot</name>
+            <id>mguillem</id>
+            <email>mguillemot@yahoo.fr</email>
+            <organization></organization>
+            <roles>
+                <role>Developer</role>
+            </roles>
+        </developer>
+    </developers>
+
+    <contributors>
+        <contributor>
+            <name>Joern Eyrich</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Robert Kuzelj</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Rod Cope</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Yuri Schimke</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>James Birchfield</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Robert Fuller</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Sergey Udovenko</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Hallvard Traetteberg</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Peter Reilly</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Brian McCallister</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Richard Monson-Haefel</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Brian Larson</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Artur Biesiadowski</name>
+            <email>abies@pg.gda.pl</email>
+        </contributor>
+        <contributor>
+            <name>Ivan Z. Ganza</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Arjun Nayyar</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Mark Chu-Carroll</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Mark Turansky</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Jean-Louis Berliet</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Graham Miller</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Marc Palmer</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Tugdual Grall</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Edwin Tellman</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Evan A Slatis</name>
+            <email></email>
+        </contributor>
+        <contributor>
+            <name>Hamlet D'Arcy</name>
+            <email></email>
+        </contributor>
+    </contributors>
+
+    <dependencies>
+
+        <!-- core dependencies -->
+        <dependency>
+            <groupId>antlr</groupId>
+            <artifactId>antlr</artifactId>
+            <version>2.7.6</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>asm</groupId>
+            <artifactId>asm</artifactId>
+            <version>2.2</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.2</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <!-- dependencies used by optional features -->
+
+        <!-- only used for command line tools (and CliBuilder) -->
+        <dependency>
+            <groupId>commons-cli</groupId>
+            <artifactId>commons-cli</artifactId>
+            <version>1.0</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-lang</groupId>
+                    <artifactId>commons-lang</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- only used for Ant tasks & scripting tools -->
+        <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant</artifactId>
+            <version>1.7.0</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant-junit</artifactId>
+            <version>1.7.0</version>
+            <scope>runtime</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.ant</groupId>
+            <artifactId>ant-launcher</artifactId>
+            <version>1.7.0</version>
+            <scope>runtime</scope>
+        </dependency>
+
+        <!-- only used for testing helper classes -->
+        <dependency>
+            <groupId>jmock</groupId>
+            <artifactId>jmock</artifactId>
+            <version>1.2.0</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>jmock</groupId>
+            <artifactId>jmock-cglib</artifactId>
+            <version>1.2.0</version>
+            <scope>test</scope>
+            <!-- exclude incompatible version of ASM -->
+            <exclusions>
+                <exclusion>
+                    <groupId>cglib</groupId>
+                    <artifactId>cglib-full</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>cglib</groupId>
+            <artifactId>cglib-nodep</artifactId>
+            <version>2.1_3</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>asm</groupId>
+            <artifactId>asm-util</artifactId>
+            <version>2.2</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>asm</groupId>
+            <artifactId>asm-attrs</artifactId>
+            <version>2.2</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>asm</groupId>
+            <artifactId>asm-analysis</artifactId>
+            <version>2.2</version>
+            <scope>compile</scope>
+        </dependency>
+        <dependency>
+            <groupId>asm</groupId>
+            <artifactId>asm-tree</artifactId>
+            <version>2.2</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <!-- only used for BSF adapter-->
+
+        <dependency>
+            <groupId>bsf</groupId>
+            <artifactId>bsf</artifactId>
+            <version>2.4.0</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+            <exclusions>
+                <exclusion>
+                    <groupId>commons-logging</groupId>
+                    <artifactId>commons-logging</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+            <version>1.1</version>
+            <scope>runtime</scope>
+            <optional>true</optional>
+            <exclusions>
+                <exclusion>
+                    <groupId>log4j</groupId>
+                    <artifactId>log4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>logkit</groupId>
+                    <artifactId>logkit</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>avalon-framework</groupId>
+                    <artifactId>avalon-framework</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <!-- only used for JMX utilities -->
+        <dependency>
+            <groupId>mx4j</groupId>
+            <artifactId>mx4j</artifactId>
+            <version>3.0.2</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>mockobjects</groupId>
+            <artifactId>mockobjects-core</artifactId>
+            <version>0.09</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>xmlunit</groupId>
+            <artifactId>xmlunit</artifactId>
+            <version>1.1</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- used for SQL library -->
+        <dependency>
+            <groupId>hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+            <version>1.8.0.7</version>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- used for servlet / gsp -->
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.4</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>jsp-api</artifactId>
+            <version>2.0</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+
+        <!-- used to dump out the AST -->
+        <dependency>
+            <groupId>com.thoughtworks.xstream</groupId>
+            <artifactId>xstream</artifactId>
+            <version>1.2.2</version>
+            <scope>compile</scope>
+            <optional>true</optional>
+        </dependency>
+        
+        <!-- Used for richer interactive groovysh support -->
+        <dependency>
+            <groupId>jline</groupId>
+            <artifactId>jline</artifactId>
+            <version>0.9.93</version>
+            <scope>compile</scope>
+        </dependency>
+    </dependencies>
+    
+    <!--
+    NOTE: The following build configuration is primarily intended to support
+          generate of IDE configuration files.  Basic support to build is also
+          included by invoking 'ant' to do the real work.
+    -->
+    
+    <build>
+        <sourceDirectory>src/main</sourceDirectory>
+        <testSourceDirectory>src/test</testSourceDirectory>
+        
+        <defaultGoal>install</defaultGoal>
+        
+        <pluginManagement>
+            <plugins>
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-antrun-plugin</artifactId>
+                    <version>1.1</version>
+                </plugin>
+                
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-idea-plugin</artifactId>
+                    <version>2.1</version>
+                    <configuration>
+                        <jdkName>1.4</jdkName>
+                        <jdkLevel>1.4</jdkLevel>
+                        <linkModules>true</linkModules>
+                    </configuration>
+                </plugin>
+                
+                <!--
+                NOTE: Tell the compiler and surefire to not do anything, all handled by Ant.
+                -->
+                
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-compiler-plugin</artifactId>
+                    <version>2.0.2</version>
+                    <configuration>
+                        <excludes>
+                            <exclude>**</exclude>
+                        </excludes>
+                        <skip>true</skip>
+                    </configuration>
+                </plugin>
+                
+                <plugin>
+                    <groupId>org.apache.maven.plugins</groupId>
+                    <artifactId>maven-surefire-plugin</artifactId>
+                    <version>2.3</version>
+                    <configuration>
+                        <skip>true</skip>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+        
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>install</phase>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                        <configuration>
+                            <tasks>
+                                <exec executable="ant" dir="${pom.basedir}">
+                                    <arg value="installRepo"/>
+                                </exec>
+                            </tasks>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    
+    <profiles>
+        <profile>
+            <id>idea</id>
+            
+            <build>
+                <defaultGoal>validate</defaultGoal>
+                
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-idea-plugin</artifactId>
+                        <configuration>
+                            <overwrite>true</overwrite>
+                            <downloadSources>true</downloadSources>
+                            <downloadJavadocs>true</downloadJavadocs>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <phase>validate</phase>
+                                <goals>
+                                    <goal>clean</goal>
+                                    <goal>idea</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+        
+        <!--
+        TODO: Add an eclipse profile to help folks that use that IDE setup configuration files ?
+        -->
+    </profiles>
+
+</project>
diff --git a/groovy/security/GroovyJarTest.jar b/groovy/security/GroovyJarTest.jar
new file mode 100644
index 0000000..eb59c7d
--- /dev/null
+++ b/groovy/security/GroovyJarTest.jar
Binary files differ
diff --git a/groovy/security/groovy.policy b/groovy/security/groovy.policy
new file mode 100644
index 0000000..0c9095a
--- /dev/null
+++ b/groovy/security/groovy.policy
@@ -0,0 +1,249 @@
+/* Notes on the contents of this policy file:
+ *
+ * The following methods in groovy have privileged operations wrapping
+ * setAccessible.  If these wrappers are not provided, most codebases below
+ * must have the following grant: 
+ * permission java.lang.reflect.ReflectPermission "suppressAccessChecks";
+ *  MetaMethod.createMetaMethod
+ *  MetaMethod.invoke(Object Object[])
+ *  ReflectionMetaMethod.invoke(Object Object[])
+ *  DefaultGoovyMethods.dump(Object)
+ */
+
+/*
+ * This keystore contains the public key of the pair used to sign GroovyTestJar.jar
+ * See SecurityTest.testReadSignedJar()
+ */
+keystore "file:${user.dir}/security/groovykeys";
+
+/*
+ * ================= Codebases requiring java.security.AllPermission ===============
+ * The following codebases require java.security.AllPermission.  They are considered trusted
+ * for purposes of groovy security enforcement.  In a deployed groovy environment, these classes
+ * would all be in a set of jars.  If these jars are signed, the grants could be strengthened to
+ * by adding a signedBy clause to the grant.
+ */
+ 
+//spg:  Looks like james switched the output destination to bin from target/classes
+//in his 7/13/04 update to the eclipse .classpath.  This permission allows the 
+//Security tests to run properly in eclipse (they were unaffected when running from maven)
+grant codeBase "file:${user.dir}/bin/-" {
+	permission java.security.AllPermission;
+};
+
+grant codeBase "file:${user.dir}/target/classes/-" {
+	permission java.security.AllPermission;
+};
+
+grant codeBase "file:${user.dir}/target/instrumented-classes/-" {
+	permission java.security.AllPermission;
+};
+
+grant codeBase "file:${user.dir}/target/test-classes/-" {
+	permission java.security.AllPermission;
+};
+
+grant codeBase "file:${groovy.lib}/-" {
+	permission java.security.AllPermission;
+};
+
+/*
+ * When running from maven, this codebase is required.
+ * If not running from maven, the codesource will not be found, but will not cause an error.
+ */
+grant codeBase "file:${maven.home}/-" {
+	permission java.security.AllPermission;
+};
+
+/*
+ * When running from ant, this codebase is required.
+ * If not running from ant, the codesource will not be found, but will not cause an error.
+ * TODO: narrow this down to specific jars
+ */
+grant codeBase "file:${user.dir}/target/lib/-" {
+	permission java.security.AllPermission;
+};
+
+/*
+ * When running the junit plugin from within eclipse, this codebase is required.
+ * If not running from eclipse, the codesource will not be found, but will not cause an error.
+ */
+grant codeBase "file:${user.dir}/../../plugins/-" {
+	permission java.security.AllPermission;
+};
+
+/*
+ * ================= Default codebases created by groovy. ===============
+ * The following codebases are assigned when groovy parses a groovy script
+ */
+
+/* 
+ * GroovyShell.parse(InputStream,...) is given a codebase of "groovy.shell" because no actual
+ * codebase can be determined.  The other forms of parse(File) and parse(GroovyCodeSource) allow
+ * for more control.  These permission should be set to control scripts that are passed into
+ * the shell in the form of a string or other groovy code of unknown provenance.
+ */
+grant codeBase "file:/groovy/shell" {
+	permission java.lang.RuntimePermission "accessDeclaredMembers";
+};
+
+/*
+ * Similar to "file:/groovy/shell", but implying a direct call to GroovyClassLoader.parse() without
+ * passing through GroovyShell.
+ */
+grant codeBase "file:/groovy/script" {
+	permission java.lang.RuntimePermission "accessDeclaredMembers";
+	//permission java.util.PropertyPermission "groovy.output.dir", "read";
+};
+
+/*
+ * The TestSupport class has a loadClass method that takes a ClassNode and runs it
+ * through defineClass() of the class loader.  The codebase for this operation is
+ * set to "/groovy/testSupport".
+ */
+grant codeBase "file:/groovy/testSupport" {
+	permission java.lang.RuntimePermission "accessDeclaredMembers";
+};
+
+/*
+ * ================= SecurityTest codebases  ===============
+ * The following grants are for individual security test cases where the
+ * codebase is explicity specified (e.g. the script is a raw string rather
+ * than read from a file).  Note that even though the codebases conform to
+ * the file URL syntax, they are not physical files: they represent virtual
+ * codebases.
+ * The permission grant that is commented out is the permission that is 
+ * expected to be missing by the test case.  It is here as a comment for
+ * information.
+ */
+ 
+//Since a codebase is a URL, we can make use of the natural hierarchy permission implications
+//to grant a 'global' accessDeclaredPermission here.
+grant codeBase "file:/groovy/security/-" {
+	permission java.lang.RuntimePermission "accessDeclaredMembers";
+};
+
+grant codeBase "file:/groovy/security/testForbiddenPackage" {
+	//permission java.lang.RuntimePermission "accessClassInPackage.sun.*";
+};
+
+grant codeBase "file:/groovy/security/javax/print/deny" {
+	//permission java.lang.RuntimePermission "accessClassInPackage.javax.print";
+};
+
+grant codeBase "file:/groovy/security/javax/print/allow" {
+	permission java.lang.RuntimePermission "accessClassInPackage.javax.print";
+};
+
+/*
+ * ================= .groovy script file codebases  ===============
+ * The following grants are for individual security test cases.
+ * The permission grant that is commented out is the permission that is 
+ * expected to be missing by the test case.  It is here as a comment for
+ * information.
+ */
+grant codeBase "file:${user.dir}/src/test/-" {
+	// Required by most groovy scripts during execution because of the heavy use of reflection/
+ 	// introspection.  The groovy code could potentially be changed to eliminate this requirement
+ 	// by adding privileged operations in many places.
+	permission java.lang.RuntimePermission "accessDeclaredMembers";
+
+	// SHOULD NOT HAVE TO DO THIS! - Level.getLocalizedName() should have a doPrivileged block.
+	// Any test that logs, will encounter this (although it will be masked by the method
+	permission java.lang.RuntimePermission "accessClassInPackage.sun.util.logging.resources";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/security/forbiddenCodeBase.gvy" {
+	//The following grant is commented out so that the test case will throw an AccessControlException
+	permission groovy.security.GroovyCodeSourcePermission "groovy/security/forbiddenCodeBase";
+};
+
+// Grant permission to .groovy files extracted from a signed jar that has been signedBy "Groovy"
+grant signedBy "Groovy" {
+	permission java.lang.RuntimePermission "accessDeclaredMembers";
+	permission java.util.PropertyPermission "user.home", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/bugs/BadScriptNameBug.groovy" {
+	permission java.util.PropertyPermission "java.class.path", "read";
+	permission java.io.FilePermission "<<ALL FILES>>", "read";
+	permission java.lang.RuntimePermission "createClassLoader";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/ClosureListenerTest.groovy" {
+	permission java.util.PropertyPermission "java.awt.headless", "read";
+	// below seems like a new requirement in jdk 1.6/1.7
+	permission java.util.PropertyPermission "os.version", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/ClosureMethodTest.groovy" {
+	permission java.io.FilePermission "${user.dir}${/}src${/}test${/}-", "read";
+	permission java.util.PropertyPermission "file.encoding", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/GroovyMethodsTest.groovy" {
+	permission java.util.PropertyPermission "*", "read,write";
+	//spg 2006-02-09 added ALL FILES execute because a test case 
+	//invokes Runtime.exec().  This is OK and permission should be granted here.
+	permission java.io.FilePermission "<<ALL FILES>>", "execute";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/ClosureWithDefaultParamTest.groovy" {
+	permission java.io.FilePermission "src${/}test${/}-", "read";
+	permission java.util.PropertyPermission "file.encoding", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/bugs/ConstructorBug.groovy" {
+	permission java.lang.RuntimePermission "createClassLoader";
+	permission java.io.FilePermission "src${/}test${/}groovy${/}bugs${/}TestBase.groovy", "read";
+	permission java.io.FilePermission "src${/}test${/}groovy${/}bugs${/}TestDerived.groovy", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/bugs/ForAndSqlBug.groovy" {
+	permission java.io.FilePermission "target${/}test-classes${/}*", "read, write";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/bugs/ForLoopBug.groovy" {
+	permission java.io.FilePermission "target${/}test-classes${/}*", "read, write";
+	permission java.io.FilePermission "target${/}test-classes${/}*", "read, write";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/bugs/Groovy303_Bug.groovy" {
+	permission java.awt.AWTPermission "showWindowWithoutWarningBanner";
+	// below seems like a new requirement in jdk 1.5
+	permission java.util.PropertyPermission "os.version", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/script/ScriptTest.groovy" {
+	permission java.io.FilePermission "src${/}test${/}groovy${/}-", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/script/EvalInScript.groovy" {
+	permission java.io.FilePermission "src${/}test${/}groovy${/}script${/}HelloWorld.groovy", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/sql/PersonTest.groovy" {
+	permission java.io.FilePermission "target${/}test-classes${/}groovy${/}sql${/}-", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/sql/SqlCompleteTest.groovy" {
+	permission java.io.FilePermission "target${/}test-classes${/}groovy${/}sql${/}-", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/sql/SqlCompleteWithoutDataSourceTest.groovy" {
+    //TODO: what should this be see other TODO about narrowing down main directories
+	//permission java.io.FilePermission "${groovy.lib}${/}axion${/}jars${/}axion-1.0-M3-dev.jar", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/sql/SqlTest.groovy" {
+	permission java.io.FilePermission "target${/}test-classes${/}groovy${/}sql${/}-", "read";
+};
+
+grant codeBase "file:${user.dir}/src/test/groovy/util/AntTest.groovy" {
+	permission java.security.AllPermission;
+};
+
+grant codeBase "file:${user.dir}/src/test/org/codehaus/groovy/classgen/MetaClassTest.groovy" {
+	permission java.io.FilePermission "target${/}test-classes${/}-", "read";
+};
+
diff --git a/groovy/security/groovykeys b/groovy/security/groovykeys
new file mode 100644
index 0000000..4fc24da
--- /dev/null
+++ b/groovy/security/groovykeys
Binary files differ
diff --git a/groovy/src/bin/groovy b/groovy/src/bin/groovy
new file mode 100644
index 0000000..9938cbd
--- /dev/null
+++ b/groovy/src/bin/groovy
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+##############################################################################
+##                                                                          ##
+##  Groovy JVM Bootstrap for UN*X                                           ##
+##                                                                          ##
+##  use -cp or -classpath just as in java to use a custom classpath         ##
+##                                                                          ##
+##############################################################################
+
+##
+## $Revision$
+## $Date$
+##
+
+GROOVY_APP_NAME=Groovy
+DIRNAME=`dirname "$0"`
+JAVA_OPTS="$JAVA_OPTS -Dscript.name=$0"
+export JAVA_OPTS
+. "$DIRNAME/startGroovy"
+
+startGroovy groovy.ui.GroovyMain "$@"
diff --git a/groovy/src/bin/groovy.bat b/groovy/src/bin/groovy.bat
new file mode 100644
index 0000000..4f480af
--- /dev/null
+++ b/groovy/src/bin/groovy.bat
@@ -0,0 +1,18 @@
+@if "%DEBUG%" == "" @echo off
+
+@rem 
+@rem $Revision$ $Date$
+@rem 
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" groovy.ui.GroovyMain %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
\ No newline at end of file
diff --git a/groovy/src/bin/groovyConsole b/groovy/src/bin/groovyConsole
new file mode 100644
index 0000000..44a9ebf
--- /dev/null
+++ b/groovy/src/bin/groovyConsole
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+##############################################################################
+##                                                                          ##
+##  Groovy Console script for UN*X                                          ##
+##                                                                          ##
+##############################################################################
+
+##
+## $Revision$
+## $Date$
+##
+
+GROOVY_APP_NAME=GroovyConsole
+DIRNAME=`dirname "$0"`
+. "$DIRNAME/startGroovy"
+
+startGroovy groovy.ui.Console "$@"
diff --git a/groovy/src/bin/groovyConsole.bat b/groovy/src/bin/groovyConsole.bat
new file mode 100644
index 0000000..34a0989
--- /dev/null
+++ b/groovy/src/bin/groovyConsole.bat
@@ -0,0 +1,18 @@
+@if "%DEBUG%" == "" @echo off
+
+@rem 
+@rem $Revision$ $Date$
+@rem 
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" groovy.ui.Console %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
\ No newline at end of file
diff --git a/groovy/src/bin/groovyc b/groovy/src/bin/groovyc
new file mode 100644
index 0000000..bedf306
--- /dev/null
+++ b/groovy/src/bin/groovyc
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+##############################################################################
+##                                                                          ##
+##  Groovy JVM Bootstrap for UN*X                                           ##
+##                                                                          ##
+##############################################################################
+
+##
+## $Revision$
+## $Date$
+##
+
+GROOVY_APP_NAME=GroovyC
+DIRNAME=`dirname "$0"`
+. "$DIRNAME/startGroovy"
+
+startGroovy org.codehaus.groovy.tools.FileSystemCompiler "$@"
diff --git a/groovy/src/bin/groovyc.bat b/groovy/src/bin/groovyc.bat
new file mode 100644
index 0000000..92fc041
--- /dev/null
+++ b/groovy/src/bin/groovyc.bat
@@ -0,0 +1,18 @@
+@if "%DEBUG%" == "" @echo off
+
+@rem 
+@rem $Revision$ $Date$
+@rem 
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" org.codehaus.groovy.tools.FileSystemCompiler %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
\ No newline at end of file
diff --git a/groovy/src/bin/groovysh b/groovy/src/bin/groovysh
new file mode 100644
index 0000000..a49fcf2
--- /dev/null
+++ b/groovy/src/bin/groovysh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+##############################################################################
+##                                                                          ##
+##  Groovy Shell script for UN*X                                            ##
+##                                                                          ##
+##############################################################################
+
+##
+## $Revision$
+## $Id$
+##
+
+GROOVY_APP_NAME=GroovyShell
+DIRNAME=`dirname "$0"`
+. "$DIRNAME/startGroovy"
+
+if [ "x$OLDSHELL" != "x" ]; then
+    startGroovy groovy.ui.InteractiveShell "$@"
+else
+    startGroovy org.codehaus.groovy.tools.shell.Main "$@"
+fi
+
diff --git a/groovy/src/bin/groovysh.bat b/groovy/src/bin/groovysh.bat
new file mode 100644
index 0000000..60ac751
--- /dev/null
+++ b/groovy/src/bin/groovysh.bat
@@ -0,0 +1,21 @@
+@if "%DEBUG%" == "" @echo off
+
+@rem 
+@rem $Revision$ $Date$
+@rem
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+set CLASSNAME=groovy.ui.InteractiveShell
+if "%OLDSHELL%" == "" set CLASSNAME=org.codehaus.groovy.tools.shell.Main
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" %CLASSNAME% %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
\ No newline at end of file
diff --git a/groovy/src/bin/java2groovy b/groovy/src/bin/java2groovy
new file mode 100644
index 0000000..c7d2de2
--- /dev/null
+++ b/groovy/src/bin/java2groovy
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+##############################################################################
+##                                                                          ##
+##  Groovy JVM Bootstrap for UN*X                                           ##
+##                                                                          ##
+##############################################################################
+
+##
+## $Revision: 4241 $
+## $Date: 2006-11-16 17:30:40 +0000 (Thu, 16 Nov 2006) $
+##
+
+GROOVY_APP_NAME=Java2Groovy
+DIRNAME=`dirname "$0"`
+. "$DIRNAME/startGroovy"
+
+startGroovy org.codehaus.groovy.antlr.java.Java2GroovyMain "$@"
diff --git a/groovy/src/bin/java2groovy.bat b/groovy/src/bin/java2groovy.bat
new file mode 100644
index 0000000..2185ab5
--- /dev/null
+++ b/groovy/src/bin/java2groovy.bat
@@ -0,0 +1,18 @@
+@if "%DEBUG%" == "" @echo off
+
+@rem 
+@rem $Revision: 2770 $ $Date: 2005-08-29 11:49:42 +0100 (Mon, 29 Aug 2005) $
+@rem 
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+:begin
+@rem Determine what directory it is in.
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.\
+
+"%DIRNAME%\startGroovy.bat" "%DIRNAME%" org.codehaus.groovy.antlr.java.Java2GroovyMain %*
+
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
diff --git a/groovy/src/bin/startGroovy b/groovy/src/bin/startGroovy
new file mode 100644
index 0000000..d386c43
--- /dev/null
+++ b/groovy/src/bin/startGroovy
@@ -0,0 +1,265 @@
+# -*- mode:sh -*-
+
+##############################################################################
+##                                                                          ##
+##  Groovy JVM Bootstrap for UN*X                                           ##
+##                                                                          ##
+##############################################################################
+
+##
+## $Revision$
+## $Date$
+##
+
+PROGNAME=`basename "$0"`
+
+#DIRNAME=`dirname "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "${PROGNAME}: $*"
+}
+
+die ( ) {
+    warn "$*"
+    exit 1
+}
+
+earlyInit ( ) {
+    return
+}
+
+lateInit ( ) {
+    return
+}
+
+GROOVY_STARTUP="$HOME/.groovy/startup"
+if [ -r "$GROOVY_STARTUP" ] ; then
+    . "$GROOVY_STARTUP"
+fi
+
+earlyInit
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;; 
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+esac
+
+if [ "$1" = "-cp" -o "$1" = "-classpath" ] ; then
+    CP=$2
+    shift 2
+fi
+
+# Attempt to set JAVA_HOME if it's not already set.
+if [ -z "$JAVA_HOME" ] ; then
+    if $darwin ; then 
+        [ -z "$JAVA_HOME" -a -d "/Library/Java/Home" ] && export JAVA_HOME="/Library/Java/Home"
+        [ -z "$JAVA_HOME" -a -d "/System/Library/Frameworks/JavaVM.framework/Home" ] && export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home"
+    else
+        javaExecutable="`which javac`"
+        [ -z "$javaExecutable" -o "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ] && die "JAVA_HOME not set and cannot find javac to deduce location, please set JAVA_HOME."
+        # readlink(1) is not available as standard on Solaris 10.
+        readLink=`which readlink`
+        [ `expr "$readLink" : '\([^ ]*\)'` = "no" ] && die "JAVA_HOME not set and readlink not available, please set JAVA_HOME."
+        javaExecutable="`readlink -f \"$javaExecutable\"`"
+        javaHome="`dirname \"$javaExecutable\"`"
+        javaHome=`expr "$javaHome" : '\(.*\)/bin'`
+        export JAVA_HOME="$javaHome"
+    fi
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+    [ -n "$GROOVY_HOME" ] && GROOVY_HOME=`cygpath --unix "$GROOVY_HOME"`
+    [ -n "$JAVACMD" ] && JAVACMD=`cygpath --unix "$JAVACMD"`
+    [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+    [ -n "$CP" ] && CP=`cygpath --path --unix "$CP"`
+fi
+
+# Attempt to set GROOVY_HOME if it is not already set.
+if [ -z "$GROOVY_HOME" -o ! -d "$GROOVY_HOME" ] ; then
+    # Resolve links: $0 may be a link to groovy's home.
+    PRG="$0"
+    # Need this for relative symlinks.
+    while [ -h "$PRG" ] ; do
+        ls=`ls -ld "$PRG"`
+        link=`expr "$ls" : '.*-> \(.*\)$'`
+        if expr "$link" : '/.*' > /dev/null; then
+            PRG="$link"
+        else
+            PRG=`dirname "$PRG"`"/$link"
+        fi
+    done
+    SAVED="`pwd`"
+    cd "`dirname \"$PRG\"`/.."
+    GROOVY_HOME="`pwd -P`"
+    cd "$SAVED"
+fi
+
+# Set the default Groovy config if no specific one is mentioned.
+if [ -z "$GROOVY_CONF" ] ; then
+    GROOVY_CONF="$GROOVY_HOME/conf/groovy-starter.conf"
+fi
+STARTER_CLASSPATH="$GROOVY_HOME/lib/@GROOVYJAR@"
+
+# Create the final classpath. Setting a classpath using the -cp or -classpath option means not to use the
+# global classpath. Groovy behaves then the same as the java interpreter
+if [ -n "$CP" ] ; then
+    CP="$CP":.
+elif [ -n "$CLASSPATH" ] ; then
+    CP="$CLASSPATH":.
+else
+    CP=.
+fi
+
+# Determine the Java command to use to start the JVM.
+if [ -z "$JAVACMD" ] ; then
+    if [ -n "$JAVA_HOME" ] ; then
+        if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+            # IBM's JDK on AIX uses strange locations for the executables
+            JAVACMD="$JAVA_HOME/jre/sh/java"
+        else
+            JAVACMD="$JAVA_HOME/bin/java"
+        fi
+    else
+        JAVACMD="java"
+    fi
+fi
+if [ ! -x "$JAVACMD" ] ; then
+    die "JAVA_HOME is not defined correctly, can not execute: $JAVACMD"
+fi
+if [ -z "$JAVA_HOME" ] ; then
+    warn "JAVA_HOME environment variable is not set"
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# Setup Profiler
+useprofiler=false
+if [ "$PROFILER" != "" ] ; then
+    if [ -r "$PROFILER" ] ; then
+        . $PROFILER
+        useprofiler=true
+    else
+        die "Profiler file not found: $PROFILER"
+    fi
+fi
+
+# For Darwin, use classes.jar for TOOLS_JAR
+TOOLS_JAR="$JAVA_HOME/lib/tools.jar"
+#if $darwin; then
+#    TOOLS_JAR="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Classes/classes.jar"
+#fi
+
+# For Darwin, add GROOVY_APP_NAME to the JAVA_OPTS as -Xdock:name
+if $darwin; then
+    JAVA_OPTS="$JAVA_OPTS -Xdock:name=$GROOVY_APP_NAME"
+# we may also want to set -Xdock:image    
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    GROOVY_HOME=`cygpath --path --mixed "$GROOVY_HOME"`
+    JAVA_HOME=`cygpath --path --mixed "$JAVA_HOME"`
+    GROOVY_CONF=`cygpath --path --mixed "$GROOVY_CONF"`
+    CP=`cygpath --path --mixed "$CP"`    
+    TOOLS_JAR=`cygpath --path --mixed "$TOOLS_JAR"`
+    STARTER_CLASSPATH=`cygpath --path --mixed "$STARTER_CLASSPATH"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GROOVY_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GROOVY_CYGPATTERN)"
+    fi
+    # Now convert the arguments
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        if [ $CHECK -ne 0 ] ; then
+            convArg=`cygpath --path --ignore --mixed "$arg"`
+        else
+            convArg=$arg
+        fi
+        ARGUMENTS="$ARGUMENTS $convArg"
+    done
+
+    startGroovy ( ) {
+        CLASS=$1
+        shift
+        # Start the Profiler or the JVM
+        if $useprofiler ; then
+            runProfiler
+        else
+            exec "$JAVACMD" $JAVA_OPTS \
+                -classpath "$STARTER_CLASSPATH" \
+                -Dprogram.name="$PROGNAME" \
+                -Dgroovy.starter.conf="$GROOVY_CONF" \
+                -Dgroovy.home="$GROOVY_HOME" \
+                -Dtools.jar="$TOOLS_JAR" \
+                $STARTER_MAIN_CLASS \
+                --main $CLASS \
+                --conf "$GROOVY_CONF" \
+                --classpath "$CP" \
+                $ARGUMENTS
+        fi
+    }
+                
+else
+    startGroovy ( ) {
+        CLASS=$1
+        shift
+        # Start the Profiler or the JVM
+        if $useprofiler ; then
+            runProfiler
+        else
+            exec "$JAVACMD" $JAVA_OPTS \
+                -classpath "$STARTER_CLASSPATH" \
+                -Dprogram.name="$PROGNAME" \
+                -Dgroovy.starter.conf="$GROOVY_CONF" \
+                -Dgroovy.home="$GROOVY_HOME" \
+                -Dtools.jar="$TOOLS_JAR" \
+                $STARTER_MAIN_CLASS \
+                --main $CLASS \
+                --conf "$GROOVY_CONF" \
+                --classpath "$CP" \
+                "$@"
+        fi
+    }    
+fi
+
+STARTER_MAIN_CLASS=org.codehaus.groovy.tools.GroovyStarter
+
+lateInit
diff --git a/groovy/src/bin/startGroovy.bat b/groovy/src/bin/startGroovy.bat
new file mode 100644
index 0000000..f0c5c0c
--- /dev/null
+++ b/groovy/src/bin/startGroovy.bat
@@ -0,0 +1,134 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem                                                                         ##
+@rem  Groovy JVM Bootstrap for Windowz                                       ##
+@rem                                                                         ##
+@rem ##########################################################################
+
+@rem 
+@rem $Revision$ $Date$
+@rem 
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~1
+shift
+
+set CLASS=%~1
+shift
+
+if exist "%USERPROFILE%/.groovy/preinit.bat" call "%USERPROFILE%/.groovy/preinit.bat"
+
+@rem Determine the command interpreter to execute the "CD" later
+set COMMAND_COM="cmd.exe"
+if exist "%SystemRoot%\system32\cmd.exe" set COMMAND_COM="%SystemRoot%\system32\cmd.exe"
+if exist "%SystemRoot%\command.com" set COMMAND_COM="%SystemRoot%\command.com"
+
+@rem Use explicit find.exe to prevent cygwin and others find.exe from being used
+set FIND_EXE="find.exe"
+if exist "%SystemRoot%\system32\find.exe" set FIND_EXE="%SystemRoot%\system32\find.exe"
+if exist "%SystemRoot%\command\find.exe" set FIND_EXE="%SystemRoot%\command\find.exe"
+
+:check_JAVA_HOME
+@rem Make sure we have a valid JAVA_HOME
+if not "%JAVA_HOME%" == "" goto have_JAVA_HOME
+
+echo.
+echo ERROR: Environment variable JAVA_HOME has not been set.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+echo.
+goto end
+
+:have_JAVA_HOME
+@rem Validate JAVA_HOME
+%COMMAND_COM% /C DIR "%JAVA_HOME%" 2>&1 | %FIND_EXE% /I /C "%JAVA_HOME%" >nul
+if not errorlevel 1 goto check_GROOVY_HOME
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+echo.
+goto end
+
+:check_GROOVY_HOME
+@rem Define GROOVY_HOME if not set
+if "%GROOVY_HOME%" == "" set GROOVY_HOME=%DIRNAME%..
+
+@rem classpath handling
+set CP=
+if "x%~1" == "x-cp" set CP=%~2
+if "x%~1" == "x-classpath" set CP=%~2
+if "x" == "x%CP%" goto init
+shift 
+shift
+ 
+:init
+@rem get name of script to launch with full path
+set GROOVY_SCRIPT_NAME=%~f1
+@rem Get command-line arguments, handling Windowz variants
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.  
+set CMD_LINE_ARGS=
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
+shift
+goto win9xME_args_slurp
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+set STARTER_CLASSPATH=%GROOVY_HOME%\lib\@GROOVYJAR@
+
+if exist "%USERPROFILE%/.groovy/init.bat" call "%USERPROFILE%/.groovy/init.bat"
+
+@rem Setting a classpath using the -cp or -classpath option means not to use
+@rem the global classpath. Groovy behaves then the same as the java 
+@rem interpreter
+if "x" == "x%CP%" goto empty_cp
+:non_empty_cp
+set CP=%CP%;.
+goto after_cp
+:empty_cp
+set CP=.
+if "x" == "x%CLASSPATH%" goto after_cp
+set STARTER_CLASSPATH=%STARTER_CLASSPATH%;%CLASSPATH%
+:after_cp
+
+set STARTER_MAIN_CLASS=org.codehaus.groovy.tools.GroovyStarter
+set STARTER_CONF=%GROOVY_HOME%\conf\groovy-starter.conf
+
+set JAVA_EXE=%JAVA_HOME%\bin\java.exe
+set TOOLS_JAR=%JAVA_HOME%\lib\tools.jar
+
+if "%JAVA_OPTS%" == "" set JAVA_OPTS="-Xmx128m"
+set JAVA_OPTS=%JAVA_OPTS% -Dprogram.name="%PROGNAME%"
+set JAVA_OPTS=%JAVA_OPTS% -Dgroovy.home="%GROOVY_HOME%"
+set JAVA_OPTS=%JAVA_OPTS% -Dtools.jar="%TOOLS_JAR%"
+set JAVA_OPTS=%JAVA_OPTS% -Dgroovy.starter.conf="%STARTER_CONF%"
+set JAVA_OPTS=%JAVA_OPTS% -Dscript.name="%GROOVY_SCRIPT_NAME%"
+
+if exist "%USERPROFILE%/.groovy/postinit.bat" call "%USERPROFILE%/.groovy/postinit.bat"
+
+@rem Execute Groovy
+"%JAVA_EXE%" %JAVA_OPTS% -classpath "%STARTER_CLASSPATH%" %STARTER_MAIN_CLASS% --main %CLASS% --conf "%STARTER_CONF%" --classpath "%CP%" %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" endlocal
+
+@rem Optional pause the batch file
+if "%GROOVY_BATCH_PAUSE%" == "on" pause
+
diff --git a/groovy/src/conf/groovy-starter.conf b/groovy/src/conf/groovy-starter.conf
new file mode 100644
index 0000000..d72ab59
--- /dev/null
+++ b/groovy/src/conf/groovy-starter.conf
@@ -0,0 +1,23 @@
+##############################################################################
+##                                                                          ##
+##  Groovy Classloading Configuration                                       ##
+##                                                                          ##
+##############################################################################
+
+##
+## $Revision$ $Date$
+##
+## Note: do not add classes from java.lang here. No rt.jar and on some
+##       platforms no tools.jar
+##
+## See http://groovy.codehaus.org/api/org/codehaus/groovy/tools/LoaderConfiguration.html
+## for the file format
+
+    # load required libraries
+    load !{groovy.home}/lib/*.jar
+
+    # load user specific libraries
+    load !{user.home}/.groovy/lib/*.jar
+    
+    # tools.jar for ant tasks
+    load ${tools.jar}
diff --git a/groovy/src/examples/commandLineTools/AntMap.groovy b/groovy/src/examples/commandLineTools/AntMap.groovy
new file mode 100644
index 0000000..f4cc02f
--- /dev/null
+++ b/groovy/src/examples/commandLineTools/AntMap.groovy
@@ -0,0 +1,67 @@
+#!/bin/env groovy
+// 
+// convert an ant build file into a format suitable for http://sf.net/projects/freemind
+//
+// by Jeremy Rayner - 2 Dec 2004
+// inspired by Sam Newman ( http://www.magpiebrain.com/archives/2004/12/02/antgui )
+//
+// usage:   groovy AntMap > build.mm
+
+import groovy.util.XmlParser
+
+buildFileName = "build.xml"  // default
+
+// handle command line params
+if (args.length > 0) {
+	buildFileName = args[0]
+}
+
+// header
+println "<map version='0.7.1'>"
+project = new XmlParser().parse(buildFileName)
+name = project['@name']
+println "<node TEXT='${name}'>"
+level = 0
+
+printChildren(project,level)
+
+
+
+def void printChildren(node,level) {
+	level++
+	node.each {
+		name = huntForName(it)
+		if (name != null) {
+			if (level > 1) {
+				println "<node TEXT='${name}' POSITION='right'>"
+			} else if (it.name() == 'property' || it.name() == 'path' ) {
+				if (it.children().size() > 0) {
+					println "<node TEXT='${name}' POSITION='left' FOLDED='true'>"
+				} else {
+					println "<node TEXT='${name}' POSITION='left'>"
+				}
+			} else if (it.children().size() > 0) {
+				println "<node TEXT='${name}' POSITION='right' FOLDED='true'>"
+			} else {
+				println "<node TEXT='${name}' POSITION='right'>"
+			}
+		}
+		if (it.children().size() > 0) printChildren(it,level)
+		if (name!=null)	println "</node>"
+	}
+}
+
+// footer
+println "</node></map>"
+
+
+def String huntForName(node) {
+	preferNodeNames = ["junitreport"]
+	if (node == null) return null
+	if (preferNodeNames.contains(node.name())) return node.name()
+	if (node['@name'] != null) return node['@name']
+	if (node['@todir'] != null) return node['@todir']
+	if (node['@dir'] != null) return node['@dir']
+	if (node['@refid'] != null) return node['@refid']
+	return node.name()
+}
diff --git a/groovy/src/examples/commandLineTools/BigTests.groovy b/groovy/src/examples/commandLineTools/BigTests.groovy
new file mode 100644
index 0000000..337c943
--- /dev/null
+++ b/groovy/src/examples/commandLineTools/BigTests.groovy
@@ -0,0 +1,36 @@
+#!/bin/env groovy
+// 
+// output tests in a junit xml report that took greater than specified time
+//
+// by Jeremy Rayner - 15 Dec 2004
+//
+// usage:   groovy BigTests.groovy <TEST.xml> <time in secs>
+
+import groovy.util.XmlParser
+import java.io.File
+
+fileName = ""  // default
+timeCutOff = new Float("1.0")
+
+if (args.length > 1) {
+	fileName = args[0]
+        timeCutOff = new Float(args[1])
+} else {
+    println "usage: groovy BigTests.groovy <TEST.xml> <time in secs>"
+}
+
+testSuite = new XmlParser().parse(fileName)
+name = testSuite['@name']
+println "TestSuite: ${name}"
+bigTests = [:]
+testSuite.each {
+    if ("testcase" == it.name()) {
+        classname = it['@classname']
+        name = it['@name']
+        time = new Float(it['@time'])
+        if (time > timeCutOff) {
+            println "  ${time} - ${classname}.${name}()"
+        }
+    }
+}
+
diff --git a/groovy/src/examples/commandLineTools/ListFiles.groovy b/groovy/src/examples/commandLineTools/ListFiles.groovy
new file mode 100644
index 0000000..129a981
--- /dev/null
+++ b/groovy/src/examples/commandLineTools/ListFiles.groovy
@@ -0,0 +1,27 @@
+import java.util.Arrays
+
+public class ListFilesManualTest { 
+
+    static void main(args) {
+        println("Called main with ${args}")
+        listFiles(Arrays.asList(args))
+    }
+    
+    static String getPath(file) {
+        return file.absolutePath
+    }
+
+    static void listFiles(dirs) {
+        println("called with ${dirs}")
+        
+        for(dir in dirs) {
+            println("dir: ${dir}")
+            
+	        def files = new java.io.File(dir).listFiles()
+
+    	    for (f in files) {
+        	    println(getPath(f))
+        	} 
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/examples/commandLineTools/Reflections.groovy b/groovy/src/examples/commandLineTools/Reflections.groovy
new file mode 100644
index 0000000..fdeeef9
--- /dev/null
+++ b/groovy/src/examples/commandLineTools/Reflections.groovy
@@ -0,0 +1,18 @@
+/**
+ * Echoes back whatever is thrown at it (with a <br> at end for browsers) ...
+ * @author <a href="mailto:jeremy.rayner@gmail.com">Jeremy Rayner</a>
+ * 
+ * invoke using
+ *    groovy -l 80 Reflections.groovy
+ * 
+ *       (where 80 is the port to listen for requests upon)
+ */
+
+// echo, echo, echo...
+println "${line} <br>"
+
+//assume no input means we've finished...
+if (line == "") {
+    // clean up gracefully, closing sockets etc
+    return "success"
+}
diff --git a/groovy/src/examples/commandLineTools/SimpleWebServer.groovy b/groovy/src/examples/commandLineTools/SimpleWebServer.groovy
new file mode 100644
index 0000000..0815108
--- /dev/null
+++ b/groovy/src/examples/commandLineTools/SimpleWebServer.groovy
@@ -0,0 +1,74 @@
+/**
+ * Simple web server
+ * @author <a href="mailto:jeremy.rayner@gmail.com">Jeremy Rayner</a>
+ * 
+ * invoke using
+ *    groovy -l 80 SimpleWebServer.groovy
+ * 
+ *       (where 80 is the port to listen for requests upon)
+ */
+import java.io.File
+
+if (init) { 
+    headers = [:] 
+    binaryTypes = ["gif","jpg","png"]          
+    mimeTypes = [
+        "css" : "text/css",         
+        "gif" : "image/gif",
+        "htm" : "text/html",         
+        "html": "text/html",         
+        "jpg" : "image/jpeg",         
+        "png" : "image/png"
+    ]                                 
+}
+
+// parse the request
+if (line.toLowerCase().startsWith("get")) {
+    content = line.tokenize()[1]
+} else {
+    h = line.tokenize(":")
+    headers[h[0]] = h[1]
+}
+
+// all done, now process request
+if (line.size() == 0) {
+    processRequest()
+    return "success"
+}
+
+// ------------------------
+
+def processRequest() {
+    if (content.indexOf("..") < 0) { //simplistic security
+        // simple file browser rooted from current dir
+        f = new File("." + content)
+        if (f.isDirectory()) {
+            printDirectoryListing(f)
+        } else {
+            extension = content.substring(content.lastIndexOf(".") + 1)
+            printHeaders(mimeTypes.get(extension,"text/plain"))          
+                      
+            if (binaryTypes.contains(extension)) {
+                socket.outputStream.write(f.readBytes())
+            } else {
+                println(f.text)
+            }
+        }
+    }
+}
+
+def printDirectoryListing(f) {
+    printHeaders("text/html")          
+    println "<html><head></head><body>"
+    for (i in f.list().toList().sort()) {
+        if ("/" == content) { content = "" } // special case for root document
+        println "<a href='${content}/${i}'>${i}</a><br>"
+    }
+    println "</body></html>"
+}
+
+def printHeaders(mimeType) {
+    println "HTTP/1.0 200 OK"
+    println "Content-Type: ${mimeType}"
+    println ""          
+}
diff --git a/groovy/src/examples/console/MortgageCalculator.groovy b/groovy/src/examples/console/MortgageCalculator.groovy
new file mode 100644
index 0000000..e0c69c6
--- /dev/null
+++ b/groovy/src/examples/console/MortgageCalculator.groovy
@@ -0,0 +1,77 @@
+/** 
+ * Mortgage Calculator
+ * @author: Jeremy Rayner
+ * based on algorithms by Jeff Louie, Dr W Carlini and Newton
+ */
+
+println "__..::~~'''~~::..__"
+println "Mortgage Calculator"
+println "~~~~~~~~~~~~~~~~~~~"
+println "Please input 3 of the 4 values in your mortgage calculation"
+println "This program will then calculate the value you leave blank"
+println ""
+
+def variables = [
+    "Amount of mortgage" : 0.0, 
+    "Annual interest rate (%)" : 0.0, 
+    "Loan duration (months)" : 0.0, 
+    "Monthly payments" : 0.0
+]
+
+for (entry in variables.entrySet()) {
+    print("${entry.key}:")
+    def userInput = System.in.readLine()
+    if ("" == userInput) {
+        valueToCalculate = entry.key
+    } else {
+        entry.value = userInput.toDouble()
+    }
+}
+
+println "$valueToCalculate = ${calculateValueOf(valueToCalculate)}"
+
+
+
+
+
+def calculateValueOf(valueToCalculate) {
+    def result = 0
+    def principal = variables["Amount of mortgage"]
+    def interest = variables["Annual interest rate (%)"] / 1200
+    def months = variables["Loan duration (months)"]
+    def payment = variables["Monthly payments"]
+
+    switch (valueToCalculate) {
+    case "Amount of mortgage":
+        result = 1 + interest
+        result = 1/Math.pow(result,months)
+        result = ((1-result)/interest) * payment
+        break           
+    case "Loan duration (months)":
+        result = (1 - (principal * interest / payment))
+        result = Math.log(result)
+        result = - result / Math.log(1 + interest)  
+        break
+    case "Monthly payments":
+        result = 1 + interest
+        result = 1 / Math.pow(result,months)
+        result = (principal * interest) / (1 - result)
+        break          
+    case "Annual interest rate (%)":
+        result = payment / principal
+        def diff = 100; def accuracy = 0.00001; def maxIterations = 1000
+        def index = 0
+        while ((diff > accuracy) && (index < maxIterations)) {
+            def temp = result
+            def numerator = (principal * temp / payment) + Math.pow((1 + temp), -months) - 1
+            def denominator= (principal / payment) - months * Math.pow((1 + temp), (-months - 1))
+            result = temp - (numerator / denominator)
+            diff = result - temp
+            diff = Math.abs(diff)
+            index++
+        }
+        result *= 1200
+        break           
+    }
+    return result
+}
diff --git a/groovy/src/examples/console/knowYourTables.groovy b/groovy/src/examples/console/knowYourTables.groovy
new file mode 100644
index 0000000..fbba15b
--- /dev/null
+++ b/groovy/src/examples/console/knowYourTables.groovy
@@ -0,0 +1,16 @@
+/** 
+ * Simple mathematics quiz
+ * @author: Jeremy Rayner
+ * based on algorithms from INPUT/Marshall Cavendish/1984
+ */
+while (true) {
+    try {
+        def n = (int)(Math.random() * 12) + 1
+        println "What is $n times 9?"
+        def a = System.in.readLine().toInteger()
+        if (a == n * 9) println "Correct"
+              
+    } catch (Exception e) {
+        println "The computer didn't understand your input"
+    }
+}
diff --git a/groovy/src/examples/console/thinkOfANumber.groovy b/groovy/src/examples/console/thinkOfANumber.groovy
new file mode 100644
index 0000000..d62c9ec
--- /dev/null
+++ b/groovy/src/examples/console/thinkOfANumber.groovy
@@ -0,0 +1,21 @@
+/** 
+ * Simple game
+ * @author: Jeremy Rayner
+ * based on algorithms from INPUT/Marshall Cavendish/1984
+ */
+while (true) {
+    try {
+        int x = Math.random() * 6
+        print "The computer has chosen a number between 0 and 5. Can you guess it?"
+              
+        def line = System.in.readLine()
+        int g = line.toInteger()
+        if (g == x) {
+           println "Well done" 
+        } else {
+           println "Tough luck - you're wrong"
+        }
+     } catch (NumberFormatException e) {
+         println "The computer didn't understand '$line'"
+     }
+}
diff --git a/groovy/src/examples/groovy/j2ee/CreateData.groovy b/groovy/src/examples/groovy/j2ee/CreateData.groovy
new file mode 100644
index 0000000..2127041
--- /dev/null
+++ b/groovy/src/examples/groovy/j2ee/CreateData.groovy
@@ -0,0 +1,21 @@
+println("Called with context: " + context)
+
+db = context.lookup("/client/tools/DatabaseHome").create()
+
+println("About to do queries on: " + db) 
+
+queries = [  "CREATE TABLE account ( ssn CHAR(11) PRIMARY KEY, first_name CHAR(20), last_name CHAR(20), balance INT)",			 
+"CREATE TABLE entity ( id INT PRIMARY KEY AUTO INCREMENT, first_name CHAR(20), last_name CHAR(20) )"]
+
+for (sql in queries) {
+    println("evaluating: " + sql)
+	db.execute(sql)
+}
+
+println("creating entity bean")
+
+context.lookup("/client/tests/entity/bmp/BasicBmpHome").create("Groovy Dain")
+
+println("Done")
+
+"OK"
\ No newline at end of file
diff --git a/groovy/src/examples/groovy/j2ee/J2eeConsole.java b/groovy/src/examples/groovy/j2ee/J2eeConsole.java
new file mode 100644
index 0000000..f3841a9
--- /dev/null
+++ b/groovy/src/examples/groovy/j2ee/J2eeConsole.java
@@ -0,0 +1,107 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.j2ee;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyShell;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import java.util.Properties;
+
+/**
+ * A J2EE console
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class J2eeConsole {
+
+    public static void main(String[] args) {
+        if (args.length <= 0) {
+            System.out.println("Usage: home [configuaration] [localcopy]");
+            return;
+        }
+
+        String home = args[0];
+
+        Properties p = new Properties();
+        System.setProperty("openejb.home", home);
+        p.put(Context.INITIAL_CONTEXT_FACTORY, "org.openejb.client.LocalInitialContextFactory");
+        p.put("openejb.loader", "embed");
+        p.put("openejb.home", home);
+
+        if (args.length > 1) {
+            String conf = args[1];
+            System.setProperty("openejb.configuration", conf);
+            p.put("openejb.configuration", conf);
+        }
+        if (args.length > 2) {
+            String copy = args[2];
+            System.setProperty("openejb.localcopy", copy);
+            p.put("openejb.localcopy", copy);
+        }
+        try {
+            InitialContext ctx = new InitialContext(p);
+
+            GroovyShell shell = new GroovyShell();
+            shell.setVariable("context", ctx);
+            //shell.evaluate("src/test/groovy/j2ee/CreateData.groovy");
+
+            //shell.evaluate("src/main/groovy/ui/Console.groovy");
+            GroovyObject console = (GroovyObject) InvokerHelper.invokeConstructorOf("groovy.ui.Console", null);
+            console.setProperty("shell", shell);
+            console.invokeMethod("run", null);
+            /*
+            */
+        }
+        catch (Exception e) {
+            System.out.println("Caught: " + e);
+        }
+    }
+}
diff --git a/groovy/src/examples/groovy/model/MvcDemo.groovy b/groovy/src/examples/groovy/model/MvcDemo.groovy
new file mode 100644
index 0000000..b8bbe57
--- /dev/null
+++ b/groovy/src/examples/groovy/model/MvcDemo.groovy
@@ -0,0 +1,44 @@
+package groovy.model
+
+import java.awt.BorderLayout
+import javax.swing.BorderFactory
+import groovy.swing.SwingBuilder
+/**
+ * 
+ */
+class MvcDemo {
+    
+    def frame
+    def swing
+    
+    void run() {
+        def swing = new SwingBuilder()
+        
+        def frame = swing.frame(title:'MVC Demo', location:[200,200], size:[300,200]) {
+            menuBar {
+		        menu(text:'Help') {
+		            menuItem() {
+		                action(name:'About', closure:{ showAbout() })
+		            }
+		        }
+		    }
+		    panel(layout:new BorderLayout()) {
+    	        scrollPane(constraints:BorderLayout.CENTER) {
+    	            table() {
+    	                tableModel(list:[ ['name':'James', 'location':'London'], ['name':'Bob', 'location':'Atlanta'] ]) {
+                            propertyColumn(header:'Name', propertyName:'name')
+                            propertyColumn(header:'Location', propertyName:'location')
+    	                }
+    	            }
+    	        }
+		    }
+		}        
+		frame.show()
+    }
+ 
+    void showAbout() {
+ 		def pane = swing.optionPane(message:'This demo shows how you can create UI models from simple MVC models')
+ 		def dialog = pane.createDialog(frame, 'About MVC Demo')
+ 		dialog.show()
+    }
+}
diff --git a/groovy/src/examples/groovy/swing/Demo.java b/groovy/src/examples/groovy/swing/Demo.java
new file mode 100644
index 0000000..210ffff
--- /dev/null
+++ b/groovy/src/examples/groovy/swing/Demo.java
@@ -0,0 +1,84 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.swing;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyCodeSource;
+import groovy.lang.GroovyClassLoader;
+import groovy.util.GroovyTestCase;
+
+import java.io.File;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class Demo extends GroovyTestCase {
+    ClassLoader parentLoader = getClass().getClassLoader();
+    protected GroovyClassLoader loader =
+    	(GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+    		public Object run() {
+    			return new GroovyClassLoader(parentLoader);
+    		}
+    	});
+
+    public static void main(String[] args) throws Exception {
+        Demo demo = new Demo();
+        GroovyObject object = demo.compile("src/examples/groovy/swing/SwingDemo.groovy");
+        object.invokeMethod("run", null);
+    }
+
+    protected GroovyObject compile(String fileName) throws Exception {
+        Class groovyClass = loader.parseClass(new GroovyCodeSource(new File(fileName)));
+        GroovyObject object = (GroovyObject) groovyClass.newInstance();
+        assertTrue(object != null);
+        return object;
+    }
+}
diff --git a/groovy/src/examples/groovy/swing/MyTableModel.java b/groovy/src/examples/groovy/swing/MyTableModel.java
new file mode 100644
index 0000000..c4ae050
--- /dev/null
+++ b/groovy/src/examples/groovy/swing/MyTableModel.java
@@ -0,0 +1,106 @@
+package groovy.swing;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.table.AbstractTableModel;
+
+/** 
+ * A sample table model
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MyTableModel extends AbstractTableModel {
+
+    private static final Logger log = Logger.getLogger(MyTableModel.class.getName());
+
+    public MyTableModel() {
+    }
+
+    final String[] columnNames = { "First Name", "Last Name", "Sport", "# of Years", "Vegetarian" };
+    final Object[][] data = { { "Mary", "Campione", "Snowboarding", new Integer(5), new Boolean(false)}, {
+            "Alison", "Huml", "Rowing", new Integer(3), new Boolean(true)
+            }, {
+            "Kathy", "Walrath", "Chasing toddlers", new Integer(2), new Boolean(false)
+            }, {
+            "Mark", "Andrews", "Speed reading", new Integer(20), new Boolean(true)
+            }, {
+            "Angela", "Lih", "Teaching high school", new Integer(4), new Boolean(false)
+            }
+    };
+
+    public int getColumnCount() {
+        return columnNames.length;
+    }
+
+    public int getRowCount() {
+        return data.length;
+    }
+
+    public String getColumnName(int col) {
+        return columnNames[col];
+    }
+
+    public Object getValueAt(int row, int col) {
+        return data[row][col];
+    }
+
+    /*
+     * JTable uses this method to determine the default renderer/
+     * editor for each cell.  If we didn't implement this method,
+     * then the last column would contain text ("true"/"false"),
+     * rather than a check box.
+     */
+    public Class getColumnClass(int c) {
+        return getValueAt(0, c).getClass();
+    }
+
+    /*
+     * Don't need to implement this method unless your table's
+     * editable.
+     */
+    public boolean isCellEditable(int row, int col) {
+        //Note that the data/cell address is constant,
+        //no matter where the cell appears onscreen.
+        if (col < 2) {
+            return false;
+        }
+        else {
+            return true;
+        }
+    }
+
+    /*
+     * Don't need to implement this method unless your table's
+     * data can change.
+     */
+    public void setValueAt(Object value, int row, int col) {
+        if (log.isLoggable(Level.FINE)) {
+            log.fine(
+                "Setting value at " + row + "," + col + " to " + value + " (an instance of " + value.getClass() + ")");
+        }
+
+        if (data[0][col] instanceof Integer && !(value instanceof Integer)) {
+            //With JFC/Swing 1.1 and JDK 1.2, we need to create    
+            //an Integer from the value; otherwise, the column     
+            //switches to contain Strings.  Starting with v 1.3,   
+            //the table automatically converts value to an Integer,
+            //so you only need the code in the 'else' part of this 
+            //'if' block.                                          
+            //XXX: See TableEditDemo.java for a better solution!!!
+            try {
+                data[row][col] = new Integer(value.toString());
+                fireTableCellUpdated(row, col);
+            }
+            catch (NumberFormatException e) {
+                log.log(Level.SEVERE, "The \"" + getColumnName(col) + "\" column accepts only integer values.");
+            }
+        }
+        else {
+            data[row][col] = value;
+            fireTableCellUpdated(row, col);
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/examples/groovy/swing/SwingDemo.groovy b/groovy/src/examples/groovy/swing/SwingDemo.groovy
new file mode 100644
index 0000000..e895539
--- /dev/null
+++ b/groovy/src/examples/groovy/swing/SwingDemo.groovy
@@ -0,0 +1,105 @@
+package groovy.swing
+
+import java.awt.BorderLayout
+import javax.swing.BorderFactory
+import groovy.model.MvcDemo
+
+class SwingDemo {
+
+    def swing = new SwingBuilder()
+    def frame
+    
+    static void main(args) {
+        def demo = new SwingDemo()
+        demo.run()
+    }
+
+    void run() {
+        def frame = swing.frame(
+            title:'This is a Frame',
+            location:[100,100],
+            size:[800,400],
+            defaultCloseOperation:javax.swing.WindowConstants.EXIT_ON_CLOSE) {
+
+            menuBar {
+                menu(text:'File') {
+                    menuItem() {
+                        action(name:'New', closure:{ println("clicked on the new menu item!") })
+                    }
+                    menuItem() {
+                        action(name:'Open', closure:{ println("clicked on the open menu item!") })
+                    }
+                    separator()
+                    menuItem() {
+                        action(name:'Save', enabled:false, closure:{ println("clicked on the Save menu item!") })
+                    }
+                }
+                menu(text:'Demos') {
+                    menuItem() {
+                        action(name:'Simple TableModel Demo', closure:{ showGroovyTableDemo() })
+                    }
+                    menuItem() {
+                        action(name:'MVC Demo', closure:{ showMVCDemo() })
+                    }
+                    menuItem() {
+                        action(name:'TableLayout Demo', closure:{ showTableLayoutDemo() })
+                    }
+                }
+                menu(text:'Help') {
+                    menuItem() {
+                        action(name:'About', closure:{ showAbout() })
+                    }
+                }
+            }
+            splitPane {
+                panel(layout:new BorderLayout(), border:BorderFactory.createTitledBorder(BorderFactory.createLoweredBevelBorder(), 'titled border')) {
+                    vbox(constraints:BorderLayout.NORTH) {
+                        panel(layout:new BorderLayout()) {
+                            label(text:'Name', constraints:BorderLayout.WEST, toolTipText:'This is the name field')
+                            textField(text:'James', constraints:BorderLayout.CENTER, toolTipText:'Enter the name into this field')
+                        }
+                        panel(layout:new BorderLayout()) {
+                            label(text:'Location', constraints:BorderLayout.WEST, toolTipText:'This is the location field')
+                            comboBox(items:['Atlanta', 'London', 'New York'], constraints:BorderLayout.CENTER, toolTipText:'Choose the location into this field')
+                        }
+                        button(text:'Click Me', actionPerformed:{event -> println("closure fired with event: " + event) })
+                    }
+                    scrollPane(constraints:BorderLayout.CENTER, border:BorderFactory.createRaisedBevelBorder()) {
+                        textArea(text:'Some text goes here', toolTipText:'This is a large text area to type in text')
+                    }
+                }
+                scrollPane {
+                    table(model:new MyTableModel())
+                }
+            }
+        }
+        frame.show()
+    }
+    
+    void showAbout() {
+        // this version doesn't auto-size & position the dialog
+        /*
+        def dialog = swing.dialog(owner:frame, title:'About GroovySwing') {
+            optionPane(message:'Welcome to the wonderful world of GroovySwing')
+        }
+        */
+         def pane = swing.optionPane(message:'Welcome to the wonderful world of GroovySwing')
+         def dialog = pane.createDialog(frame, 'About GroovySwing')
+         dialog.show()
+    }
+    
+    void showGroovyTableDemo() {
+        def demo = new TableDemo()
+        demo.run()
+    }
+
+    void showMVCDemo() {
+        def demo = new MvcDemo()
+        demo.run()
+    }
+
+    void showTableLayoutDemo() {
+        def demo = new TableLayoutDemo()
+        demo.run()
+    }
+}
diff --git a/groovy/src/examples/groovy/swing/TableDemo.groovy b/groovy/src/examples/groovy/swing/TableDemo.groovy
new file mode 100644
index 0000000..a41f683
--- /dev/null
+++ b/groovy/src/examples/groovy/swing/TableDemo.groovy
@@ -0,0 +1,52 @@
+package groovy.swing
+
+import java.awt.BorderLayout
+import javax.swing.BorderFactory
+
+/**
+ * Demonstrates the use of the Groovy TableModels for viewing tables of any List of objects
+ */
+class TableDemo {
+    
+    // properties
+    def frame
+    def swing
+    
+    static void main(args) {
+        def demo = new TableDemo()
+        demo.run()
+    }
+    
+    void run() {
+        swing = new SwingBuilder()
+        
+        frame = swing.frame(title:'Groovy TableModel Demo', location:[200,200], size:[300,200]) {
+            menuBar {
+                menu(text:'Help') {
+                    menuItem() {
+                        action(name:'About', closure:{ showAbout() })
+                    }
+                }
+            }
+            panel(layout:new BorderLayout()) {
+                scrollPane(constraints:BorderLayout.CENTER) {
+                    table() {
+                        def model = [['name':'James', 'location':'London'], ['name':'Bob', 'location':'Atlanta'], ['name':'Geir', 'location':'New York']]
+
+                        tableModel(list:model) {
+                            closureColumn(header:'Name', read:{row -> return row.name})
+                            closureColumn(header:'Location', read:{row -> return row.location})
+                        }
+                    }
+                }
+            }
+        }
+        frame.show()
+    }
+    
+    void showAbout() {
+         def pane = swing.optionPane(message:'This demo shows how GroovySwing can use Groovy closures to create simple table models')
+         def dialog = pane.createDialog(frame, 'About GroovySwing')
+         dialog.show()
+    }
+}
diff --git a/groovy/src/examples/groovy/swing/TableLayoutDemo.groovy b/groovy/src/examples/groovy/swing/TableLayoutDemo.groovy
new file mode 100644
index 0000000..f63ccf6
--- /dev/null
+++ b/groovy/src/examples/groovy/swing/TableLayoutDemo.groovy
@@ -0,0 +1,57 @@
+package groovy.swing
+
+import java.awt.BorderLayout
+import javax.swing.BorderFactory
+
+/**
+ * Demonstrates the use of the table layout
+ */
+class TableLayoutDemo {
+    
+    def frame
+    def swing
+    
+    void run() {
+        swing = new SwingBuilder()
+        
+        frame = swing.frame(title:'TableLayout Demo', location:[200,200], size:[300,200]) {
+            menuBar {
+                menu(text:'Help') {
+                    menuItem() {
+                        action(name:'About', closure:{ showAbout() })
+                    }
+                }
+            }
+            tableLayout {
+                tr {
+                    td {
+                        label(text:'name')
+                    }
+                    td(colfill:true) {
+                        textField(text:'James')
+                    }
+                }
+                tr {
+                    td {
+                        label(text:'location')
+                    }
+                    td(colfill:true) {
+                        textField(text:'London')
+                    }
+                }
+                tr {
+                    td(colspan:2, align:'center') {
+                        button(text:'OK')
+                    }
+                }
+            }
+        }
+        frame.show()
+    }
+    
+    void showAbout() {
+         def pane = swing.optionPane(message:'This demo shows how you can use HTML style table layouts with Swing components')
+         def dialog = pane.createDialog(frame, 'About TableLayout Demo')
+         dialog.show()
+    }
+}
diff --git a/groovy/src/examples/groovy2d/paintingByNumbers.groovy b/groovy/src/examples/groovy2d/paintingByNumbers.groovy
new file mode 100644
index 0000000..a112ef5
--- /dev/null
+++ b/groovy/src/examples/groovy2d/paintingByNumbers.groovy
@@ -0,0 +1,48 @@
+/** 
+ * Simple patchwork graphics demo
+ * @author: Jeremy Rayner, changes by Dierk Koenig
+ */
+import javax.swing.WindowConstants as WC
+import groovy.swing.SwingBuilder
+
+def width = 500, height = 400, blockSize = 10
+def g = createGraphics(width, height)
+
+// main loop
+while (true) {
+    drawBlock(width, height, blockSize, g)
+}
+
+// random integer
+def rnd(upperBound){
+    (int)(Math.random() * upperBound)
+}
+
+// draw a random coloured square within bounds
+def drawBlock(w, h, b, g) {
+    def row    = rnd(h / b)
+    def column = rnd(w / b)
+    def colour = new java.awt.Color(rnd(255),rnd(255),rnd(255))
+    g.color = colour
+    g.fillRect(column * b, row * b, b, b)
+}
+
+// create a new frame and clear screen
+def createGraphics(w, h) {
+    def frame = new SwingBuilder().frame(
+        title:'Painting by numbers',
+        location:[20,20],
+        size:[w, h],
+        defaultCloseOperation:WC.EXIT_ON_CLOSE
+    )
+    frame.show()
+              
+    // obtain graphics context
+    def gfx = frame.graphics
+              
+    // clear screen
+    gfx.color = java.awt.Color.BLACK
+    gfx.fillRect(0, 0, w, h)
+
+    return gfx
+}
diff --git a/groovy/src/examples/searchEngine/Indexer.groovy b/groovy/src/examples/searchEngine/Indexer.groovy
new file mode 100644
index 0000000..ba414bc
--- /dev/null
+++ b/groovy/src/examples/searchEngine/Indexer.groovy
@@ -0,0 +1,59 @@
+import org.apache.lucene.analysis.standard.StandardAnalyzer
+import org.apache.lucene.document.Document
+import org.apache.lucene.document.Field
+import org.apache.lucene.index.IndexWriter
+
+/**
+ * Indexer: traverses a file system and indexes .txt files
+ *
+ * @author Jeremy Rayner <groovy@ross-rayner.com>
+ * based on examples in the wonderful 'Lucene in Action' book
+ * by Erik Hatcher and Otis Gospodnetic ( http://www.lucenebook.com )
+ *
+ * requires a lucene-1.x.x.jar from http://lucene.apache.org
+ */
+
+if (args.size() != 2 ) {
+    throw new Exception("Usage: groovy -cp lucene-1.4.3.jar Indexer <index dir> <data dir>")
+}
+def indexDir = new File(args[0]) // Create Lucene index in this directory
+def dataDir = new File(args[1]) // Index files in this directory
+
+def start = new Date().time
+def numIndexed = index(indexDir, dataDir)
+def end = new Date().time
+
+println "Indexing $numIndexed files took ${end - start} milliseconds"
+
+def index(indexDir, dataDir) {
+    if (!dataDir.exists() || !dataDir.directory) {
+        throw new IOException("$dataDir does not exist or is not a directory")
+    }
+    def writer = new IndexWriter(indexDir, new StandardAnalyzer(), true) // Create Lucene index
+    writer.useCompoundFile = false
+
+    dataDir.eachFileRecurse {
+        if (it.name =~ /.txt$/) { // Index .txt files only
+            indexFile(writer,it)
+        }
+    }
+    def numIndexed = writer.docCount()
+    writer.optimize()
+    writer.close() // Close index
+    return numIndexed
+}
+
+void indexFile(writer, f) {
+    if (f.hidden || !f.exists() || !f.canRead() || f.directory) { return }
+
+    println "Indexing $f.canonicalPath"
+    def doc = new Document()
+
+    // Construct a Field that is tokenized and indexed, but is not stored in the index verbatim.
+    doc.add(Field.Text("contents", new FileReader(f)))
+
+    // Construct a Field that is not tokenized, but is indexed and stored.
+    doc.add(Field.Keyword("filename",f.canonicalPath))
+
+    writer.addDocument(doc) // Add document to Lucene index
+}
\ No newline at end of file
diff --git a/groovy/src/examples/searchEngine/Searcher.groovy b/groovy/src/examples/searchEngine/Searcher.groovy
new file mode 100644
index 0000000..61f6a82
--- /dev/null
+++ b/groovy/src/examples/searchEngine/Searcher.groovy
@@ -0,0 +1,38 @@
+import org.apache.lucene.analysis.standard.StandardAnalyzer
+import org.apache.lucene.queryParser.QueryParser
+import org.apache.lucene.search.IndexSearcher
+import org.apache.lucene.store.FSDirectory
+
+/**
+ * Searcher: searches a Lucene index for a query passed as an argument
+ *
+ * @author Jeremy Rayner <groovy@ross-rayner.com>
+ * based on examples in the wonderful 'Lucene in Action' book
+ * by Erik Hatcher and Otis Gospodnetic ( http://www.lucenebook.com )
+ *
+ * requires a lucene-1.x.x.jar from http://lucene.apache.org
+ */
+
+if (args.size() != 2) {
+    throw new Exception("Usage: groovy -cp lucene-1.4.3.jar Searcher <index dir> <query>")
+}
+def indexDir = new File(args[0]) // Index directory create by Indexer
+def q = args[1] // Query string
+
+if (!indexDir.exists() || !indexDir.directory) {
+    throw new Exception("$indexDir does not exist or is not a directory")
+}
+
+def fsDir = FSDirectory.getDirectory(indexDir, false)
+def is = new IndexSearcher(fsDir) // Open index
+
+def query = QueryParser.parse(q, "contents", new StandardAnalyzer()) // Parse query
+def start = new Date().time
+def hits = is.search(query) // Search index
+def end = new Date().time
+
+println "Found ${hits.length()} document(s) (in ${end - start} milliseconds) that matched query '$q':"
+
+for ( i in 0 ..< hits.length() ) {
+    println(hits.doc(i)["filename"]) // Retrieve matching document and display filename
+}
diff --git a/groovy/src/examples/swing/BindingExample.groovy b/groovy/src/examples/swing/BindingExample.groovy
new file mode 100644
index 0000000..fe6612e
--- /dev/null
+++ b/groovy/src/examples/swing/BindingExample.groovy
@@ -0,0 +1,61 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ *

+ * The real interesting part of this example are in the three properties of button:

+ *

+ *           text: bind(source:textField, sourceProperty:'text'),

+ *           margin: bind(source:slider, sourceProperty:'value', converter:{[it, it, it, it] as Insets}),

+ *           enabled: bind(source:checkBox, sourceProperty:'selected')

+ *

+ * This is where the real magic goes on, causing the button to react to the changes

+ * in the source widgets values.

+ */

+

+import groovy.swing.SwingBuilder

+import java.awt.GridBagConstraints as gb

+import java.awt.Insets

+

+sb = SwingBuilder.build() {

+    frame(id:'frame', defaultCloseOperation:javax.swing.JFrame.DISPOSE_ON_CLOSE) {

+        gridBagLayout()

+

+        label("Text:", constraints:gbc(anchor:gb.WEST, insets:[6,6,3,3] as Insets))

+        textField("Change Me!", id:'textField', constraints:gbc(fill:gb.HORIZONTAL, gridwidth:gb.REMAINDER, insets:[6,3,3,6] as Insets))

+

+        label("Margin:", constraints:gbc(anchor:gb.WEST, insets:[3,6,3,3] as Insets))

+        slider(id:'slider', value:5, constraints:gbc(fill:gb.HORIZONTAL, gridwidth:gb.REMAINDER, insets:[3,3,3,6] as Insets))

+

+        panel()

+        checkBox("Enbled", id:'checkBox', constraints:gbc(anchor:gb.WEST, gridwidth:gb.REMAINDER, insets:[3,3,3,6] as Insets))

+

+        separator(constraints:gbc(fill:gb.HORIZONTAL, gridwidth:gb.REMAINDER))

+

+        button(constraints:gbc(anchor:gb.CENTER, gridwidth:gb.REMAINDER, gridheight:gb.REMAINDER, weightx:1.0, weighty:1.0, insets:[3,6,6,6] as Insets),

+            text: bind(source:textField, sourceProperty:'text'),

+            margin: bind(source:slider, sourceProperty:'value', converter:{[it, it, it, it] as Insets}),

+            enabled: bind(source:checkBox, sourceProperty:'selected')

+        )

+    }

+}

+frame = sb.frame

+frame.pack()

+frame.setSize(frame.width + 100, frame.height + 200)

+frame.show()

diff --git a/groovy/src/examples/swing/BloglinesClient.groovy b/groovy/src/examples/swing/BloglinesClient.groovy
new file mode 100644
index 0000000..0183240
--- /dev/null
+++ b/groovy/src/examples/swing/BloglinesClient.groovy
@@ -0,0 +1,164 @@
+/*
+ * BloglinesClient.groovy - an example of the Bloglines Web Services
+ *
+ * Written by Marc Hedlund <marc@precipice.org>, September 2004.
+ * 
+ * Mangled by John Wilson September 2004
+ *
+ * Small adaptions to JSR Version by Dierk Koenig, June 2005
+ *
+ * Used in Marc's article at:
+ *    http://www.oreillynet.com/pub/a/network/2004/09/28/bloglines.html
+ *
+ * Requirements:
+ *   - install Groovy as detailed at <http://groovy.codehaus.org/>.
+ *   - put commons-httpclient-3.0-rc3.jar into GROOVY_HOME/lib
+ *       see <http://jakarta.apache.org/commons/httpclient/>.
+ *       note: this is currently designed for HttpClient2.x and not HttpClient3.x
+ *
+ * To Launch:
+ *   groovy BloglinesClient.groovy
+ *
+ * This work is licensed under the Creative Commons Attribution
+ * License. To view a copy of this license, visit
+ * <http://creativecommons.org/licenses/by/2.0/> or send a letter to
+ * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+ */
+
+import groovy.swing.SwingBuilder
+import java.awt.BorderLayout
+import javax.swing.JOptionPane
+import javax.swing.JSplitPane
+import javax.swing.JTree
+import javax.swing.ListSelectionModel
+import javax.swing.WindowConstants
+import javax.swing.tree.DefaultMutableTreeNode
+import javax.swing.tree.TreeSelectionModel
+import org.apache.commons.httpclient.HttpClient
+import org.apache.commons.httpclient.UsernamePasswordCredentials
+import org.apache.commons.httpclient.methods.GetMethod
+
+//Set up global variables and data types
+server = 'rpc.bloglines.com'
+
+class Feed { def name; def id; def unread; String toString() { (unread == "0" ? name : "${name} (${unread})") } }
+
+class Item { def title; def description; String toString() { title } }
+
+//Ask the user for account information (using simple dialogs)
+email = 
+JOptionPane.showInputDialog(null, "Email address:", "Log in to Bloglines", 
+			      JOptionPane.QUESTION_MESSAGE)
+password = 
+JOptionPane.showInputDialog(null, "Password:", "Log in to Bloglines", 
+			      JOptionPane.QUESTION_MESSAGE)
+
+//Use HTTPClient for web requests since the server requires authentication
+client = new HttpClient()
+credentials = new UsernamePasswordCredentials(email, password)
+client.state.setCredentials("Bloglines RPC", server, credentials)
+
+abstractCallBloglines = { method, parameters ->
+  url = "http://${server}/${method}${parameters}"
+  try {
+    get = new GetMethod(url)
+    get.doAuthentication = true
+    client.executeMethod(get)
+    return get.responseBodyAsStream
+  } catch (Exception e) {
+    println "Error retrieving <${url}>: ${e}"
+  }
+}
+
+callBloglinesListsub = abstractCallBloglines.curry('listsubs', '')
+callBloglinesGetItems = abstractCallBloglines.curry('getitems')
+
+//Get the list of subscriptions and parse it into a GPath structure
+opml = new XmlSlurper().parse(callBloglinesListsub())
+
+//Descend into the subscription outline, adding to the feed tree as we go
+treeTop = new DefaultMutableTreeNode("My Feeds")
+parseOutline(opml.body.outline.outline, treeTop)
+
+def parseOutline(parsedXml, treeLevel) {
+  parsedXml.each{ outline ->
+    if (outline['@xmlUrl'] != null) {  // this is an individual feed
+      feed = new Feed(name:outline['@title'], id:outline['@BloglinesSubId'], 
+                      unread:outline['@BloglinesUnread'])
+      treeLevel.add(new DefaultMutableTreeNode(feed))
+    } else {  // this is a folder of feeds
+      folder = new DefaultMutableTreeNode(outline['@title'])
+      parseOutline(outline.outline, folder)
+      treeLevel.add(folder)
+    }
+  }
+}
+
+//Build the base user interface objects and configure them
+swing = new SwingBuilder()
+feedTree = new JTree(treeTop)
+itemList = swing.list()
+itemText = swing.textPane(contentType:'text/html', editable:false)
+model = feedTree.selectionModel
+model.selectionMode = TreeSelectionModel.SINGLE_TREE_SELECTION
+itemList.selectionMode = ListSelectionModel.SINGLE_SELECTION
+
+//Set up the action closures that will react to user selections
+listItems = { feed ->
+  rssStream = callBloglinesGetItems("?s=${feed.id}&n=0")  
+  if (rssStream != null) {
+    try {
+      rss = new XmlSlurper().parse(rssStream)
+      itemList.listData =  rss.channel.item.collect(new Vector()) {
+		new Item(title:it.title, description:it.description)
+       }
+       feed.unread = "0"  // update the unread item count in the feed list
+     } catch (Exception e) {
+       println "Error during <${feed.name}> RSS parse: ${e}"
+    }
+  }
+}
+
+feedTree.valueChanged = { event ->
+  itemText.text = ""  // clear any old item text
+  node = (DefaultMutableTreeNode) feedTree.getLastSelectedPathComponent()
+  if (node != null) {
+    feed = node.userObject
+     if (feed instanceof Feed && feed.unread != "0") {
+       listItems(feed)
+     }
+  }
+}
+
+itemList.valueChanged = { event ->
+  item = event.source.selectedValue
+  if (item instanceof Item && item?.description != null) {
+    itemText.text = "<html><body>${item.description}</body></html>"
+  }
+}
+
+//Put the user interface together and display it
+gui = 
+swing.frame(title:'Bloglines Client', location:[100,100], size:[800,600], 
+	      defaultCloseOperation:WindowConstants.EXIT_ON_CLOSE) {
+
+ panel(layout:new BorderLayout()) {
+   splitPane(orientation:JSplitPane.HORIZONTAL_SPLIT, dividerLocation:200) {
+     scrollPane {
+		  widget(feedTree)
+	 }
+
+      splitPane(orientation:JSplitPane.VERTICAL_SPLIT, dividerLocation:150) {
+        scrollPane(constraints:BorderLayout.CENTER) {
+	        widget(itemList)
+	    }
+
+	    scrollPane(constraints:BorderLayout.CENTER) {
+	      widget(itemText)
+	    }
+      }
+    }
+  }
+}
+
+gui.show()
diff --git a/groovy/src/examples/swing/ModelNodeExample.groovy b/groovy/src/examples/swing/ModelNodeExample.groovy
new file mode 100644
index 0000000..85eb211
--- /dev/null
+++ b/groovy/src/examples/swing/ModelNodeExample.groovy
@@ -0,0 +1,71 @@
+import groovy.swing.SwingBuilder

+import static javax.swing.WindowConstants.*

+import static java.awt.GridBagConstraints.*

+

+def bean = new ObservableMap([name:'Alice', phone:'719-555-1212', addr:'42 Other Way'])

+

+def swing = SwingBuilder.build {

+ frame(id:'frame', 

+       pack:true, 

+       show:true,

+       defaultCloseOperation:DISPOSE_ON_CLOSE)

+ {

+  model(bean, id:'beanModel', bind:false)

+

+  gridBagLayout()

+

+  label('Name:', constraints:gbc(insets:[6,6,3,3]))

+  textField(text:beanModel.name,

+            id:'name',

+            columns:20,

+            constraints:gbc(gridwidth:REMAINDER,

+                            fill:HORIZONTAL,

+                            weightx:1,

+                            insets:[6,3,3,6]))

+

+  label('Phone:', constraints:gbc(insets:[3,6,3,3]))

+  textField(text:beanModel.phone, 

+            id:'phone', 

+            columns:20, 

+            constraints:gbc(gridwidth:REMAINDER, 

+                            fill:HORIZONTAL, 

+                            weightx:1, 

+                            insets:[3,3,3,6]))

+

+  label('Address:', constraints:gbc(insets:[3,6,3,3]))

+  textField(text:beanModel.addr, 

+            id:'addr', 

+            columns:20, 

+            constraints:gbc(gridwidth:REMAINDER, 

+                            fill:HORIZONTAL, 

+                            weightx:1, 

+                            insets:[3,3,3,6]))

+

+  button('Reset', actionPerformed:{beanModel.update()}, 

+                  constraints:gbc(gridwidth:2, 

+                                  anchor:EAST, 

+                                  weightx:1, 

+                                  insets:[9,0,0,6]))

+  button('Submit', 

+         constraints:gbc(insets:[9,0,0,0]),

+         actionPerformed: { 

+             beanModel.reverseUpdate()

+             output.text = ("name = '$bean.name'\nphone = '$bean.phone'\naddr = '$bean.addr'\n\n")

+         })

+

+  separator(constraints:gbc(gridwidth:REMAINDER, 

+                            fill:HORIZONTAL, 

+                            insets:[3,6,3,6]))

+  label('Output:', constraints:gbc(gridwidth:REMAINDER, 

+                                   anchor:WEST, 

+                                   insets:[3,6,3,6]))

+  scrollPane(preferredSize:[100, 100], 

+             constraints:gbc(gridwidth:REMAINDER, 

+                             fill:BOTH, 

+                             weighty:1, 

+                             insets:[3,6,6,6])) 

+  {

+   textArea(id:'output')

+  }

+ }

+}

diff --git a/groovy/src/examples/swing/RegexCoach.groovy b/groovy/src/examples/swing/RegexCoach.groovy
new file mode 100644
index 0000000..3723b27
--- /dev/null
+++ b/groovy/src/examples/swing/RegexCoach.groovy
@@ -0,0 +1,104 @@
+// Groovy Regex Coach - Copyright 2007 Jeremy Rayner
+// inspired by http://weitz.de/regex-coach/
+import java.awt.*
+import java.awt.event.*
+import java.util.regex.*
+import javax.swing.*
+import javax.swing.text.DefaultHighlighter
+import groovy.swing.SwingBuilder
+
+// define the view
+def swing = new SwingBuilder()
+def gui = swing.frame(title:'The Groovy Regex Coach', location:[20,40], size:[600,500], defaultCloseOperation:WindowConstants.EXIT_ON_CLOSE) {
+  panel(layout:new BorderLayout()) {
+    splitPane(orientation:JSplitPane.VERTICAL_SPLIT, dividerLocation:150) {
+      panel(layout:new BorderLayout()) {
+	label(constraints:BorderLayout.NORTH, text:'Regular expression:')
+	scrollPane(constraints:BorderLayout.CENTER) {textPane(id:'regexPane')}
+	label(constraints:BorderLayout.SOUTH, id:'regexStatus', text:' ')
+      }
+      panel(layout:new BorderLayout()) {
+	label(constraints:BorderLayout.NORTH, text:'Target string:')
+	scrollPane(constraints:BorderLayout.CENTER) {textPane(id:'targetPane')}
+	panel(constraints:BorderLayout.SOUTH, layout:new BorderLayout()) {
+	  label(constraints:BorderLayout.NORTH, id:'targetStatus', text:' ')
+	  panel(constraints:BorderLayout.SOUTH, layout:new FlowLayout()) {
+	    button('<<-', id:'scanLeft')
+	    button('->>', id:'scanRight')
+	  }
+	}
+      }
+    }
+  }
+}
+def highlighter = new RegexHighlighter(swing:swing)
+swing.regexPane.addKeyListener(highlighter)
+swing.targetPane.addKeyListener(highlighter)
+swing.scanLeft.addActionListener(highlighter)
+swing.scanRight.addActionListener(highlighter)
+gui.show()
+
+class RegexHighlighter extends KeyAdapter implements ActionListener {
+  def swing // reference to the view
+  int scanIndex // how many times to execute matcher.find()
+  def orange = new DefaultHighlighter.DefaultHighlightPainter(Color.ORANGE)
+  def yellow = new DefaultHighlighter.DefaultHighlightPainter(Color.YELLOW)
+  def red = new DefaultHighlighter.DefaultHighlightPainter(Color.RED)
+
+  // react to user actions
+  public void actionPerformed(ActionEvent event) {
+    if (event.actionCommand == '<<-') {scanIndex = Math.max(scanIndex - 1, 0)}
+    if (event.actionCommand == '->>') {scanIndex++}
+    doHighlights()
+  }
+  public void keyReleased(KeyEvent event) {
+    scanIndex = 0
+    doHighlights()
+  }
+
+  private resetView() {
+      swing.regexPane.highlighter.removeAllHighlights()
+      swing.targetPane.highlighter.removeAllHighlights()
+      swing.regexStatus.text = ' '
+      swing.targetStatus.text = ' '
+  }
+
+  // the main regex logic
+  private doHighlights() {
+    try {
+      resetView()
+      // note: get the text from the underlying document,
+      // otherwise carriage return/line feeds different when using the JTextPane text
+      def regex = swing.regexPane.document.getText(0,swing.regexPane.document.length)
+      def target = swing.targetPane.document.getText(0,swing.targetPane.document.length)
+
+      def matcher = (target =~ regex)
+
+      // scan past the matches before the match we want
+      int scan = 0
+      while (scan < scanIndex) {
+	matcher.find()
+	scan++
+      }
+      if (matcher.find()) {
+	// highlight any captured groups
+      	int i = 0
+	while (i++ < matcher.groupCount()) {
+	  swing.targetPane.highlighter.addHighlight(matcher.start(i), matcher.end(i), orange)
+	}
+       	// highlight whole match
+	swing.targetPane.highlighter.addHighlight(matcher.start(), matcher.end(), yellow)
+	if (regex.length() != 0) {
+	  swing.targetStatus.text = "Match #${scanIndex + 1} from ${matcher.start()} to ${matcher.end()}."
+	}
+      } else { // not found
+	scanIndex = Math.max(scan - 1, 0)
+	if (scanIndex > 0) {doHighlights()}
+	swing.targetStatus.text = "No match."
+      }
+    } catch (PatternSyntaxException e) {
+      swing.regexPane.highlighter.addHighlight(e.index, e.index + 2, red)
+      swing.regexStatus.text = e.description
+    }
+  }
+}
diff --git a/groovy/src/examples/swing/RegexCoachController.groovy b/groovy/src/examples/swing/RegexCoachController.groovy
new file mode 100644
index 0000000..aa628ef
--- /dev/null
+++ b/groovy/src/examples/swing/RegexCoachController.groovy
@@ -0,0 +1,89 @@
+// Groovy Regex Coach - Copyright 2007 Jeremy Rayner

+

+import groovy.swing.SwingBuilder

+import java.awt.Color

+import java.awt.event.ActionEvent

+import java.awt.event.ActionListener

+import java.awt.event.KeyAdapter

+import java.awt.event.KeyEvent

+import java.util.regex.PatternSyntaxException

+import javax.swing.text.DefaultHighlighter.DefaultHighlightPainter

+

+// inspired by http://weitz.de/regex-coach/

+

+// define the view

+def swing = new SwingBuilder()

+

+def gui = swing.build(RegexCoachView)

+

+def highlighter = new RegexHighliter(swing: swing)

+swing.regexPane.addKeyListener(highlighter)

+swing.targetPane.addKeyListener(highlighter)

+swing.scanLeft.addActionListener(highlighter)

+swing.scanRight.addActionListener(highlighter)

+gui.show()

+

+class RegexHighliter extends KeyAdapter implements ActionListener {

+    def swing // reference to the view

+    int scanIndex // how many times to execute matcher.find()

+    def orange = new DefaultHighlightPainter(Color.ORANGE)

+    def yellow = new DefaultHighlightPainter(Color.YELLOW)

+    def red = new DefaultHighlightPainter(Color.RED)

+

+    // react to user actions

+    public void actionPerformed(ActionEvent event) {

+        if (event.actionCommand == '<<-') {scanIndex = Math.max(scanIndex - 1, 0)}

+        if (event.actionCommand == '->>') {scanIndex++}

+        doHighlights()

+    }

+    public void keyReleased(KeyEvent event) {

+        scanIndex = 0

+        doHighlights()

+    }

+

+    private resetView() {

+        swing.regexPane.highlighter.removeAllHighlights()

+        swing.targetPane.highlighter.removeAllHighlights()

+        swing.regexStatus.text = ' '

+        swing.targetStatus.text = ' '

+    }

+

+    // the main regex logic

+    private doHighlights() {

+        try {

+            resetView()

+            // note: get the text from the underlying document,

+            // otherwise carriage return/line feeds different when using the JTextPane text

+            def regex = swing.regexPane.document.getText(0, swing.regexPane.document.length)

+            def target = swing.targetPane.document.getText(0, swing.targetPane.document.length)

+

+            def matcher = (target =~ regex)

+

+            // scan past the matches before the match we want

+            int scan = 0

+            while (scan < scanIndex) {

+                matcher.find()

+                scan++

+            }

+            if (matcher.find()) {

+                // highlight any captured groups

+                int i = 0

+                while (i++ < matcher.groupCount()) {

+                    swing.targetPane.highlighter.addHighlight(matcher.start(i), matcher.end(i), orange)

+                }

+                // highlight whole match

+                swing.targetPane.highlighter.addHighlight(matcher.start(), matcher.end(), yellow)

+                if (regex.length() != 0) {

+                    swing.targetStatus.text = "Match #${scanIndex + 1} from ${matcher.start()} to ${matcher.end()}."

+                }

+            } else {// not found

+                scanIndex = Math.max(scan - 1, 0)

+                if (scanIndex > 0) {doHighlights()}

+                swing.targetStatus.text = "No match."

+            }

+        } catch (PatternSyntaxException e) {

+            swing.regexPane.highlighter.addHighlight(e.index, e.index + 2, red)

+            swing.regexStatus.text = e.description

+        }

+    }

+}

diff --git a/groovy/src/examples/swing/RegexCoachView.groovy b/groovy/src/examples/swing/RegexCoachView.groovy
new file mode 100644
index 0000000..fad61b8
--- /dev/null
+++ b/groovy/src/examples/swing/RegexCoachView.groovy
@@ -0,0 +1,35 @@
+import static java.awt.BorderLayout.*

+import static javax.swing.JSplitPane.VERTICAL_SPLIT

+import static javax.swing.WindowConstants.EXIT_ON_CLOSE

+

+frame(title: 'The Groovy Regex Coach', location: [20, 40], size: [600, 500], defaultCloseOperation: EXIT_ON_CLOSE) {

+    panel {

+        borderLayout()

+        splitPane(orientation: VERTICAL_SPLIT, dividerLocation: 150) {

+            panel {

+                borderLayout()

+                label(constraints: NORTH, text: 'Regular expression:')

+                scrollPane(constraints: CENTER) {

+                    textPane(id: 'regexPane')

+                }

+                label(constraints: SOUTH, id: 'regexStatus', text: ' ')

+            }

+            panel {

+                borderLayout()

+                label(constraints: NORTH, text: 'Target string:')

+                scrollPane(constraints: CENTER) {

+                    textPane(id: 'targetPane')

+                }

+                panel(constraints: SOUTH) {

+                    borderLayout()

+                    label(constraints: NORTH, id: 'targetStatus', text: ' ')

+                    panel(constraints: SOUTH) {

+                        flowLayout()

+                        button('<<-', id: 'scanLeft')

+                        button('->>', id: 'scanRight')

+                    }

+                }

+            }

+        }

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/examples/swing/Widgets.groovy b/groovy/src/examples/swing/Widgets.groovy
new file mode 100644
index 0000000..bca15c9
--- /dev/null
+++ b/groovy/src/examples/swing/Widgets.groovy
@@ -0,0 +1,204 @@
+package swing
+
+import java.awt.BorderLayout
+import java.awt.Color
+import java.awt.GridBagConstraints
+import javax.swing.SwingConstants
+import javax.swing.WindowConstants
+import groovy.swing.SwingBuilder
+
+class Widgets {
+
+    def swing = new SwingBuilder()
+    def unownedDialog
+    def ownedDialog
+    
+
+    static void main(args) {
+        def demo = new Widgets()
+        demo.run()
+    }
+
+    def showUnownedDialog(event) {
+        unownedDialog.show();
+    }
+    
+    def showOwnedDialog(event) {
+        ownedDialog.show();
+    }
+
+    void run() {
+        unownedDialog = swing.dialog(
+            title:'unrooted dialog',
+            location: [200, 200],
+            pack:true,
+            defaultCloseOperation:WindowConstants.DISPOSE_ON_CLOSE
+            ) {
+                label("I am unowned, but not unwanted");
+            }
+
+        def frame = swing.frame(
+            title:'FrameTitle',
+            location:[100,100],
+            size:[800,400],
+            defaultCloseOperation:WindowConstants.EXIT_ON_CLOSE) {
+
+            menuBar {
+                menu(text:'File') {
+                    menuItem() {
+                        action(name:'New', closure:{ println("clicked on the new menu item!") })
+                    }
+                    menuItem() {
+                        action(name:'Open', closure:{ println("clicked on the open menu item!") })
+                    }
+                    separator()
+                    menuItem() {
+                        action(name:'Save', enabled:false, closure:{ println("clicked on the Save menu item!") })
+                    }
+                }
+                menu(text:'Dialogs') {
+                    menuItem() {
+                        action(name:'Owned Dialog', closure: this.&showOwnedDialog)
+                    }
+                    menuItem() {
+                        action(name:'Unowned Dialog', closure: this.&showUnownedDialog)
+                    }
+                    def deeplyOwnedDialog = swing.dialog(
+                        title:'rooted dialog #2',
+                        location: [200, 200],
+                        pack:true,
+                        defaultCloseOperation:WindowConstants.DISPOSE_ON_CLOSE
+                        ) {
+                        label("ownership is deep");
+                    }
+                    menuItem() {
+                        action(name:'Deeply Owned Dialog', closure: {deeplyOwnedDialog.show()} )
+                    }
+                }
+            }
+
+            tabbedPane() {
+
+                //colorChooser(
+                //    name:"Color Chooser",
+                //    color: 0xfeed42)
+
+                panel(name:"Formatted Text Fields") {
+                    gridLayout(columns: 2, rows: 0)
+                    label("Simple Constructor:")
+                    formattedTextField()
+                    label("Date Value")
+                    formattedTextField(value: new java.util.Date())
+                    label("Integer Value")
+                    formattedTextField(value: new java.lang.Integer(42))
+                    label("Date Format")
+                    formattedTextField(format: java.text.DateFormat.getDateInstance())
+                    label("Currency Format ")
+                    formattedTextField(format: new java.text.DecimalFormat("¤###.00;(¤###.00)"))
+                }
+
+                panel(name:"Sliders") {
+                    flowLayout()
+                    slider(minimum:-100, 
+                        maximum:100, 
+                        majorTickSpacing: 50,
+                        orientation: SwingConstants.VERTICAL, 
+                        paintLabels:true)
+                    slider(minimum:-100, 
+                        maximum:100, 
+                        orientation: SwingConstants.VERTICAL, 
+                        paintLabels:true,
+                        paintTicks:true,
+                        majorTickSpacing: 50,
+                        minorTickSpacing: 10,
+                        snapToTicks:true,
+                        paintTrack:true)
+                }
+
+                panel(name:"Spinners") {
+                    gridBagLayout()
+                    label(
+                        text:"Tempuature in London:",
+                        constraints: gbc(insets:[12, 12, 2, 2], 
+                            anchor: GridBagConstraints.EAST,
+                            gridx: 0))
+                    spinner(
+                        model:spinnerNumberModel(minimum:-10, 
+                            maximum: 40, 
+                            value:20,
+                            stepSize:5),
+                        constraints: gbc(insets:[12, 3, 2, 12], 
+                            anchor: GridBagConstraints.WEST,
+                            gridx: 1,
+                            fill: GridBagConstraints.HORIZONTAL))
+                    label(
+                        text:"Baseball Leagues:",
+                        constraints: gbc(insets:[3, 12, 2, 2], 
+                            anchor: GridBagConstraints.EAST,
+                            gridx: 0))
+                    spinner(
+                        model:spinnerListModel(
+                            list: ["Major League", "AAA", "AA", "A", "Rookie", "Semi-Pro", "Rec A", "Rec B"],
+                            value: "AA"),
+                        constraints: gbc(insets:[3, 3, 2, 12], 
+                            anchor: GridBagConstraints.WEST,
+                            gridx: 1,
+                            fill: GridBagConstraints.HORIZONTAL))
+                    label(
+                        text:"Today's Date:",
+                        constraints: gbc(insets:[3, 12, 2, 2], 
+                            anchor: GridBagConstraints.EAST,
+                            gridx: 0))
+                    spinner(
+                        model:spinnerDateModel(calendarField: Calendar.HOUR_OF_DAY),
+                        constraints: gbc(insets:[3, 3, 2, 12], 
+                            anchor: GridBagConstraints.WEST,
+                            gridx: 1,
+                            fill: GridBagConstraints.HORIZONTAL))
+                }
+
+                panel(name:"Border Layout") {
+                    borderLayout()
+                    label(text:"Border Layout", 
+                          constraints:BorderLayout.NORTH, 
+                          horizontalAlignment:SwingConstants.CENTER)
+                    label(text:"South", 
+                          constraints:BorderLayout.SOUTH, 
+                          background:Color.YELLOW,
+                          opaque:true,
+                          horizontalAlignment:SwingConstants.CENTER,
+                          toolTipText:"Tooltip on south")
+                    label(text:"West", 
+                          constraints:BorderLayout.WEST, 
+                          background:Color.ORANGE,
+                          opaque:true,
+                          horizontalAlignment:SwingConstants.CENTER,
+                          toolTipText:"Tooltip on west")
+                    label(text:"East", 
+                          constraints:BorderLayout.EAST, 
+                          background:Color.GREEN,
+                          opaque:true,
+                          horizontalAlignment:SwingConstants.CENTER,
+                          toolTipText:"Tooltip on east")
+                    label(text:"Center", 
+                          constraints:BorderLayout.CENTER, 
+                          background:Color.WHITE,
+                          opaque:true,
+                          horizontalAlignment:SwingConstants.CENTER,
+                          toolTipText:"<html>This is not the tooltip you are looking for.<br><i>*waves hand*</i>")
+                }
+            }
+
+            ownedDialog = swing.dialog(
+                title:'rooted dialog',
+                location: [200, 200],
+                pack:true,
+                defaultCloseOperation:WindowConstants.DISPOSE_ON_CLOSE
+                ) {
+                label("j00 h4v3 b33n 0wn3xed");
+            }
+        }        
+        frame.show()
+    }
+    
+}
diff --git a/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/Animal.groovy b/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/Animal.groovy
new file mode 100644
index 0000000..3cdaa17
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/Animal.groovy
@@ -0,0 +1,5 @@
+interface Animal {
+
+  String saySomething(String something);
+
+}
diff --git a/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/zoo/Fish.groovy b/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/zoo/Fish.groovy
new file mode 100644
index 0000000..494d5a8
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/zoo/Fish.groovy
@@ -0,0 +1,9 @@
+package zoo
+
+abstract class Fish implements Animal {
+
+  String saySomething(String something) {
+    return "Blubb°: " + something + "..."
+  }
+
+}
diff --git a/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Shark.groovy b/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Shark.groovy
new file mode 100644
index 0000000..afdf10c
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Shark.groovy
@@ -0,0 +1,11 @@
+package zoo.fish
+
+import zoo.Fish
+
+class Shark extends Fish {
+
+  String saySomething(String something) {
+    return "Shark bites " + something + "...ROOOAR°!"
+  }
+
+}
diff --git a/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Trout.groovy b/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Trout.groovy
new file mode 100644
index 0000000..faa8f03
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/WEB-INF/groovy/zoo/fish/Trout.groovy
@@ -0,0 +1,9 @@
+package zoo.fish
+
+class Trout extends zoo.Fish {
+
+  String saySomething(String something) {
+    return "Trout says " + something + "...blubb°!"
+  }
+
+}
diff --git a/groovy/src/examples/webapps/groovlet-examples/WEB-INF/web.xml b/groovy/src/examples/webapps/groovlet-examples/WEB-INF/web.xml
new file mode 100644
index 0000000..118626a
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/WEB-INF/web.xml
@@ -0,0 +1,69 @@
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_3.xsd"
+    version="2.3">
+
+  <servlet>
+    <servlet-name>Groovlet</servlet-name>
+    <servlet-class>groovy.servlet.GroovyServlet</servlet-class>
+    <init-param>
+      <param-name>verbose</param-name>
+      <param-value>true</param-value>
+    </init-param>
+    <init-param>
+      <param-name>resource.name.regex</param-name>
+      <param-value>none</param-value>
+    </init-param>
+    <init-param>
+      <param-name>resource.name.replacement</param-name>
+      <param-value>none</param-value>
+    </init-param>
+  </servlet>
+    
+  <servlet>
+    <servlet-name>Template|GSP</servlet-name>
+    <servlet-class>groovy.servlet.TemplateServlet</servlet-class>
+  </servlet>
+  
+  <servlet>
+    <servlet-name>Template|GString</servlet-name>
+    <servlet-class>groovy.servlet.TemplateServlet</servlet-class>
+    <init-param>
+      <param-name>template.engine</param-name>
+      <param-value>groovy.text.GStringTemplateEngine</param-value>
+    </init-param>
+  </servlet>
+  
+  <servlet>
+    <servlet-name>Template|XML</servlet-name>
+    <servlet-class>groovy.servlet.TemplateServlet</servlet-class>
+    <init-param>
+      <param-name>template.engine</param-name>
+      <param-value>groovy.text.XmlTemplateEngine</param-value>
+    </init-param>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>Template|GSP</servlet-name>
+    <url-pattern>*.html</url-pattern>
+  </servlet-mapping>
+  
+  <servlet-mapping>
+    <servlet-name>Template|XML</servlet-name>
+    <url-pattern>*.xhtml</url-pattern>
+  </servlet-mapping>
+
+  <servlet-mapping>
+    <servlet-name>Groovlet</servlet-name>
+    <url-pattern>*.groovy</url-pattern>
+  </servlet-mapping>
+
+  <welcome-file-list>
+    <welcome-file>index.xhtml</welcome-file>
+    <welcome-file>index.html</welcome-file>
+    <welcome-file>index.jsp</welcome-file>
+    <welcome-file>index.jspx</welcome-file>
+    <welcome-file>index.groovy</welcome-file>
+  </welcome-file-list>
+
+</web-app>
diff --git a/groovy/src/examples/webapps/groovlet-examples/codehaus-style.css b/groovy/src/examples/webapps/groovlet-examples/codehaus-style.css
new file mode 100644
index 0000000..a5cf0dd
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/codehaus-style.css
@@ -0,0 +1,609 @@
+
+body {
+  font-family: Verdana, Helvetica, Arial, sans-serif;
+  font-size: small;
+  margin: 0;
+}
+
+div {
+  /* line-height: 1.5em; */
+}
+
+a {
+  color: #008800;
+  text-decoration: none;
+  font-weight: bold;
+}
+
+.navLink a {
+  font-weight: normal;
+}
+
+.navLink {
+  margin-left: 5px;
+}
+
+.navLink:first-line {
+  margin-left: -5px;
+}
+
+
+a:link.selfref, a:visited.selfref {
+}
+
+a:link, a:visited {
+}
+
+a:active, a:hover {
+  text-decoration: underline;
+}
+
+a.plain:active, a.plain:hover {
+  text-decoration: none;
+}
+
+.sectionTitle a {
+  text-decoration: underline;
+}
+.subsectionTitle a {
+  text-decoration: underline;
+}
+
+span.highlight {
+  font-weight: bold;
+  color: #990000;
+}
+
+#layout {
+  margin: 0px;
+  padding: 0px;
+}
+
+#banner {
+  padding: 8px;
+}
+
+#breadcrumbs {
+  border-top: 1px solid #009900;
+  border-bottom: 1px solid #009900;
+  padding-left: 12px;
+  padding-right: 12px;
+  padding-top: 2px;
+  padding-bottom: 2px;
+  font-size: x-small;
+  background-color: #dddddd;
+}
+
+#breadcrumbs td {
+  font-size: x-small;
+}
+
+#breadcrumbs a {
+  font-weight: bold;
+}
+
+#layout {
+  /*border-top: 1px solid #009900;*/
+  padding: 0px;
+  margin: 0px;
+
+}
+
+.navSection {
+  background-color: #ffffff;
+  border: 1px solid #999999;
+  border-top: none;
+  padding: 0px;
+  margin-bottom: 8px;
+  font-size: small;
+}
+
+.navSection a {
+  font-weight: normal;
+}
+
+.navSectionHead {
+  border-top: 1px solid #999999;
+  border-bottom: 1px solid #999999;
+  color: #555555;
+  padding: 4px;
+  margin-left: 0px;
+  margin-right: 0px;
+  background-color: #eeeeee;
+  font-weight: bold;
+  font-size: x-small;
+}
+
+.navLink {
+  padding-top: 2px;
+  padding-bottom: 2px;
+  padding-left: 14px;
+  font-size: small;
+}
+
+.section {
+  padding-bottom: 16px;
+}
+
+* + h1 {
+  margin-top: 1.5em;
+}
+
+* + h2 {
+  margin-top: 1.5em;
+}
+
+.sectionTitle, h1 {
+  padding: 4px;
+  border: 1px solid #aaaaaa;
+  color: #007700;
+  font-size: larger;
+  background-color: #eee;
+  color: #007700;
+  font-weight: bold;
+  margin-bottom: .5em;
+  /* margin-top: 1em; */
+}
+
+.subsection {
+  padding-left: 20px;
+  margin-top: 10px;
+  margin-bottom: 10px;
+}
+
+body:first-child {
+  padding-top: 0px;
+}
+
+.subsectionTitle, h2 {
+  padding-left: .2em;
+  /* margin-top: 1em; */
+  margin-bottom: .5em;
+  border-bottom: 1px solid #999;
+  border-left: 1px solid #999;
+  background-color: white;
+  font-weight: bold;
+  font-size: larger;
+}
+
+.sectionTitle a {
+  font-weight: normal;
+}
+
+.subsectionTitle a {
+  font-weight: normal;
+}
+
+.subsubsection {
+  padding-left: 30px;
+}
+
+.subsubsectionTitle, h3, .blogheading {
+  font-weight: bold;
+  border-bottom: 1px solid #bbb;
+  padding: .1em;
+  color: #555;
+  margin-bottom: .5em;
+}
+
+h4 {
+  margin-bottom: .5em;
+  padding: .1em;
+  border: 1px solid #ddd;
+
+}
+
+ins.inserted {
+  text-decoration: none;
+  font-weight: bold;
+}
+
+#leftColumn {
+  width: 12em;
+}
+
+#leftColumn h3 {
+  background-color: #ddd;
+  border: 1px solid #ccc;
+  border-bottom: 1px solid #070;
+  margin-bottom: .5em;
+}
+
+#leftColumn a {
+  font-weight: inherit;
+}
+
+#breadcrumbs a {
+  font-weight: inherit;
+}
+
+
+p {
+  margin: 0px;
+  margin-bottom: .5em;
+  line-height: 1.2em;
+  padding-right: 20px;
+  padding-left: 0px;
+}
+
+blockquote p {
+  padding-left: 0px;
+  padding-right: 0px;
+}
+
+ul {
+  margin-top: 1em;
+  margin-bottom: 1em;
+  padding-left: 1.5em;
+}
+
+ul ul {
+  margin: 0px;
+  padding-left: 1em;
+}
+
+li {
+  margin: 0px;
+  margin-right: 15%;
+  line-height: 1.2em;
+}
+
+#leftColumn {
+  border-right: 1px solid #cccccc;
+  background-color: #eeeeee;
+  padding: 12px;
+  font-size: small;
+}
+
+#leftColumn a {
+  font-size: smaller;
+}
+
+#leftColumn tt a {
+  font-size: inherit;
+}
+
+#leftColumn h3 a {
+  font-size: inherit;
+}
+
+tt {
+  font-size: larger;
+}
+
+
+#extraColumn {
+  padding: 12px;
+}
+
+#navBox {
+}
+
+#rightColumn {
+  padding: 12px;
+  border-right: 1px solid #cccccc;
+  font-size: small;
+  padding-left: 16px;
+  padding-right: 16px;
+}
+
+#contentBox {
+
+}
+
+table.bodyTable, table.wikitable {
+  margin: 10px;
+  border-collapse: collapse;
+  border-spacing: 0pt;
+  background-color: #eeeeee;
+}
+
+#Content table.grid {
+  border: 1px solid #bbbbbb;
+}
+
+table.grid {
+  padding: 0px;
+  border-collapse: collapse;
+  border-spacing: 0pt;
+  margin-left: 1em;
+  margin-right: 1em;
+}
+
+table.grid th {
+  background-color: #eeeeee;
+  font-size: smaller;
+  padding: 4px;
+  border: 1px solid #bbbbbb;
+}
+
+table.grid td {
+  font-size: x-small;
+  border: 1px solid #bbbbbb;
+  padding: 3px;
+}
+
+table.bodyTable th, table.bodyTable td, table.wikitable th, table.wikitable td {
+  border: 1px solid #999999;
+  font-size: smaller;
+  padding: 4px;
+}
+
+
+table.bodyTable th, table.wikitable th {
+  text-align: left;
+  background-color: #dddddd;
+  border: 2px solid #999999;
+  padding: 4px;
+}
+
+.nobr
+  white-space: nowrap;
+}
+
+table.bodyTable td {
+  padding: 4px;
+}
+
+/*
+table.bodyTable th, table.wikitable th {
+  border-bottom: 2px solid #999999;
+}
+
+table.bodyTable tr.a {
+  background-color: #dedede;
+}
+
+table.bodyTable tr.b {
+  background-color: #efefef;
+}
+*/
+
+.source, .code {
+  padding: 12px;
+  margin: 1em;
+  border: 1px solid #007700;
+  border-left: 2px solid #007700;
+  border-right: 2px solid #007700;
+  color: #555555;
+}
+
+pre {
+  padding: 12px;
+  font-size: larger;
+}
+
+.java-keyword {
+  color: #009900;
+}
+
+.java-object {
+  color: #000099;
+}
+
+.java-quote {
+  color: #990000;
+}
+
+
+.source, .code pre {
+  margin: 0px;
+  margin-left: 8px;
+  padding: 0px;
+}
+
+#footer {
+  padding-left: 4px;
+  border-top: 1px solid #009900;
+  color: #888888;
+  font-size: x-small;
+}
+
+blockquote {
+  border-top: 1px solid #bbbbbb;
+  border-bottom: 1px solid #bbbbbb;
+  border-left: 3px solid #bbbbbb;
+  border-right: 3px solid #bbbbbb;
+  padding: 12px;
+  margin-left: 3em;
+  margin-right: 3em;
+  color: #666666;
+  background-color: white;
+  line-height: 1.5em;
+}
+
+input[type="text"] {
+  margin: 0px;
+  border: 1px solid #999999;
+  background-color: #dddddd;
+}
+
+input.required {
+  margin: 0px;
+  border: 1px solid #990000;
+}
+
+input {
+  border: 1px solid #999999;
+}
+
+textarea {
+  border: 1px solid #999999;
+}
+
+textarea.required {
+  border: 1px solid #990000;
+}
+
+label {
+  font-size: smaller;
+}
+
+label.required {
+  color: #990000;
+}
+
+.searchResults {
+  color: black;
+}
+
+.searchResults b {
+  color: #007700;
+}
+
+
+.linecomment { color: #bbbbbbb; }
+.blockcomment { color: #bbbbbbb; }
+.prepro { color: #0000BB; }
+.select {}
+.quote { color: #770000; }
+.category1 { color: #007700; }
+.category2 { color: #0000BB; }
+.category3 { color: #0000BB; }
+
+#page_title {
+  border-bottom: 1px solid black;
+  font-weight: bold;
+  font-size: x-large;
+  margin-bottom: .5em;
+}
+
+.greenbar {
+  background-color: green;
+}
+
+.redbar {
+  background-color: red;
+}
+
+tr.testpassed td {
+  padding: 0px;
+  padding-left: 1px;
+  padding-right: 1px;
+  margin: 0px;
+}
+
+tr td.noformatting {
+  border: none;
+  padding: 0px;
+  padding-left: 4px;
+  padding-right: 4px;
+  margin: 0px;
+}
+
+.greybox {
+  font-style: italic;
+  font-weight: bold;
+  margin-top: .5em;
+  margin-bottom: .5em;
+  background-color: #ddd;
+  border: 1px solid #bbb;
+  padding: .3em;
+}
+
+.panelContent {
+  border: 1px solid #999;
+  padding: 1em;
+  margin: 1em;
+}
+
+.header_name {
+	font-size: smaller;
+	padding: 2px;
+	padding-right: 1ex;
+	border-right: 1px solid #555;
+	background-color: #ccc;
+}
+
+.header_value {
+	font-size: smaller;
+	background-color: #ddd;
+	padding: 2px;
+}
+
+.header_fields {
+	width: 100%;
+	border: 1px solid #999;
+	background-color: #fff;
+}
+
+.email_body {
+	margin: 2ex;
+	padding: 1ex;
+	padding-left: 2ex;
+	padding-right: 2ex;
+	border: 1px solid #999;
+	font-size: smaller;
+}
+
+.email_body pre {
+	padding: 0px;
+	margin: 0px;
+}
+
+.email_body blockquote {
+	padding: 0px;
+	margin: 0px;
+	border: 1px solid #ccc;
+}
+
+.msg_navblock {
+	margin-bottom: 2ex;
+	border: 1px solid #999;
+	background-color: #fff;
+}
+
+.msg_navblock th {
+	border: 1px solid #ccc;
+	font-size: smaller;
+}
+
+.msg_navblock td {
+	border: 1px solid #ccc;
+	font-size: smaller;
+}
+
+.single_entry {
+	border: 1px solid #aaa;
+	padding: .5ex;
+	padding-left: 1ex;
+	border-left: 1px solid #090;
+	margin: 2px;
+	background-color: #eee;
+}
+
+.root_entry {
+	border: 1px solid #aaa;
+	padding: .5ex;
+	padding-left: 1ex;
+	border-left: 1px solid #090;
+	margin-top: 8px;
+	margin-bottom: 8px;
+	background-color: #eee;
+}
+
+.root_entry .sub_entry {
+	padding: .5ex;
+	padding-left: 1ex;
+	margin: 2px;
+	margin-left: 1ex;
+	border-left: 1px solid #999;
+}
+
+.list_entry {
+	border: 1px solid #aaa;
+	padding: 1ex;
+	margin-top: 1ex;
+	margin-bottom: 1ex;
+}
+
+.list_entry td {
+	border: 1px solid #999;
+}
+
+.project_entry {
+	border: 1px solid #aaa;
+	padding: 1ex;
+	margin-top: 1ex;
+	margin-bottom: 1ex;
+}
diff --git a/groovy/src/examples/webapps/groovlet-examples/hello/hello.groovy b/groovy/src/examples/webapps/groovlet-examples/hello/hello.groovy
new file mode 100644
index 0000000..74e97cd
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/hello/hello.groovy
@@ -0,0 +1,33 @@
+println """
+<html>
+    <head>
+        <title>Groovy Servlet Example - hello</title>
+    </head>
+    <body>
+    <a href="../"><img src="../images/return.gif" width="24" height="24" border="0"></a><a href="../">Return</a>
+    <p>
+"""
+
+session = request.getSession(true);
+
+if (session.counter == null) {
+  session.counter = 1
+}
+
+
+println """Hello, ${request.remoteHost}! ${new java.util.Date()}"""
+
+println """
+<dl>
+ <dt><b>requestURI</b></dt><dd>${request.requestURI}</dd>
+ <dt><b>servletPath</b></dt><dd>${request.servletPath}</dd>
+ <dt><b>session.counter</b></dt><dd>${session.counter}</dd>
+</dl>
+"""
+
+println """
+    </body>
+</html>
+"""
+
+session.counter = session.counter + 1
diff --git a/groovy/src/examples/webapps/groovlet-examples/images/code.gif b/groovy/src/examples/webapps/groovlet-examples/images/code.gif
new file mode 100644
index 0000000..93af2cd
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/images/code.gif
Binary files differ
diff --git a/groovy/src/examples/webapps/groovlet-examples/images/execute.gif b/groovy/src/examples/webapps/groovlet-examples/images/execute.gif
new file mode 100644
index 0000000..f64d70f
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/images/execute.gif
Binary files differ
diff --git a/groovy/src/examples/webapps/groovlet-examples/images/groovy.png b/groovy/src/examples/webapps/groovlet-examples/images/groovy.png
new file mode 100644
index 0000000..54af4c1
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/images/groovy.png
Binary files differ
diff --git a/groovy/src/examples/webapps/groovlet-examples/images/return.gif b/groovy/src/examples/webapps/groovlet-examples/images/return.gif
new file mode 100644
index 0000000..af4f68f
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/images/return.gif
Binary files differ
diff --git a/groovy/src/examples/webapps/groovlet-examples/index.groovy b/groovy/src/examples/webapps/groovlet-examples/index.groovy
new file mode 100644
index 0000000..534201b
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/index.groovy
@@ -0,0 +1,89 @@
+println """
+
+<!-- Groovy Groovlet Examples. -->
+
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="Groovy Developers">
+   <title>Groovy - Groovlet Examples</title>
+   <link rel="stylesheet" href="codehaus-style.css" type="text/css">
+</head>
+
+<body bgcolor="#FFFFFF">
+
+<a href="http://groovy.codehaus.org"><img src="images/groovy.png" border="0"></a>
+
+<h1>Groovlet examples showing GroovyServlet in action</h1>
+<p>
+These examples will only work when viewed via an http URL. They will not work
+if you are viewing these pages via a <tt>file://...</tt> URL. You need to
+deploy the <tt>war</tt> archive into a servlet container like Tomcat, Jetty or
+any other will do as well.
+</p>
+
+<p>
+To navigate your way through the examples, the following icons will help:
+
+<table border=0>
+ <tr>
+  <td width="30"><img src="images/execute.gif" ></td>
+  <td>Executes the example</td>
+</tr>
+<tr>
+  <td width="30"><img src="images/code.gif"></td>
+  <td>Look at the source code for the example</td>
+</tr>
+<tr>
+  <td width="30"><img src="images/return.gif"></td>
+  <td>Return to this screen</td>
+</tr>
+</table>
+</p>
+
+<p>Tip: To see the cookie interactions with your browser, try turning on
+the "notify when setting a cookie" option in your browser preferences.
+This will let you see when a session is created and give some feedback
+when looking at the cookie demo.
+</p>
+
+<h2>Table of content</h2>
+
+"""
+
+println """
+
+<table BORDER=0 CELLSPACING=5 WIDTH="85%" >
+
+<!-- Begin Groovlet -->
+<tr VALIGN=TOP>
+<td>Hello World</td>
+
+<td VALIGN=TOP WIDTH="30%"><img SRC="images/execute.gif" HSPACE=4 BORDER=0 align=TOP><a href="hello/hello.groovy">Execute</a></td>
+
+<td WIDTH="30%"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP>Source</td>
+</tr>
+<!-- End Groovlet -->
+
+
+<!-- Begin Groovlet -->
+<tr VALIGN=TOP>
+<td>Zoo</td>
+
+<td VALIGN=TOP WIDTH="30%"><img SRC="images/execute.gif" HSPACE=4 BORDER=0 align=TOP><a href="zoo/zoo.groovy">Execute</a></td>
+
+<td WIDTH="30%"><img SRC="images/code.gif" HSPACE=4 BORDER=0 height=24 width=24 align=TOP>Source</td>
+</tr>
+<!-- End Groovlet -->
+</table>
+
+<h2>Note</h2>
+
+<p>The source code for these examples does not contain all of the
+source code that is actually in the example, only the important sections
+of code. Code not important to understand the example has been removed
+for clarity.
+</body>
+</html>
+"""
\ No newline at end of file
diff --git a/groovy/src/examples/webapps/groovlet-examples/readme.txt b/groovy/src/examples/webapps/groovlet-examples/readme.txt
new file mode 100644
index 0000000..a041280
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/readme.txt
@@ -0,0 +1,13 @@
+
+GroovyServlet examples
+
+[ / ]
+Only contains the "index.groovy", "codehaus-style.css" and "readme.txt" files.
+
+[ /hello ]
+Contains a single welcome file named "hello.groovy" showing a simple groovlet.
+
+[ /zoo ]
+The zoo demonstrates a groovy setup of scripts, that are partly <i>hidden</i>
+beneath the "WEB-INF/groovy" directory. Note the different package names in the
+script files.
diff --git a/groovy/src/examples/webapps/groovlet-examples/xml/index.xhtml b/groovy/src/examples/webapps/groovlet-examples/xml/index.xhtml
new file mode 100644
index 0000000..6fc4e89
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/xml/index.xhtml
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+<!-- DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" -->
+
+<html xmlns:gsp="http://groovy.codehaus.org/2005/gsp">
+
+  <head>
+    <link rel="stylesheet" href="../codehaus-style.css" type="text/css" />
+  </head>
+
+  <body>
+
+  Name = ${request.getParameter("name")} <br/>
+  Name = <gsp:expression>params.get("name")</gsp:expression>
+
+  <hr color="#12334" noshade="noshade" />
+
+  BEGIN
+
+    <br/>
+
+  <gsp:scriptlet>
+   <![CDATA[
+    3.times {
+   ]]>
+  </gsp:scriptlet>
+* Hello World! *
+  <gsp:scriptlet>
+   <![CDATA[  } // 3.times ]]>
+  </gsp:scriptlet>  
+
+    <br/>
+
+    END
+
+  </body>
+
+</html>
diff --git a/groovy/src/examples/webapps/groovlet-examples/zoo/HommingbergerGepardenforelle.groovy b/groovy/src/examples/webapps/groovlet-examples/zoo/HommingbergerGepardenforelle.groovy
new file mode 100644
index 0000000..760e55f
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/zoo/HommingbergerGepardenforelle.groovy
@@ -0,0 +1,9 @@
+package zoo
+
+class HommingbergerGepardenforelle extends zoo.fish.Trout {
+
+  String saySomething(String something) {
+    return something;
+  }
+  
+}
\ No newline at end of file
diff --git a/groovy/src/examples/webapps/groovlet-examples/zoo/visit.groovy b/groovy/src/examples/webapps/groovlet-examples/zoo/visit.groovy
new file mode 100644
index 0000000..21abe87
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/zoo/visit.groovy
@@ -0,0 +1,19 @@
+Animal shark = new zoo.fish.Shark()
+Animal trout = new zoo.fish.Trout()
+Animal forelle = new zoo.HommingbergerGepardenforelle()
+
+println """
+<html>
+    <head>
+        <title>Groovy Servlet Example - Visiting the zoo</title>
+    </head>
+    <body>
+     <p>Shark<br> 
+     ${shark.saySomething("\"Where is the trout?\"")}
+     <p>Trout<br> 
+     ${trout.saySomething("Here is the trout!")}
+     <p>Forelle<br> 
+     ${forelle.saySomething("\"<a href=\"http://www.hommingberger-forelle.de\">There is no spoon.</a>\"")}
+    </body>
+</html>
+"""
\ No newline at end of file
diff --git a/groovy/src/examples/webapps/groovlet-examples/zoo/zoo.groovy b/groovy/src/examples/webapps/groovlet-examples/zoo/zoo.groovy
new file mode 100644
index 0000000..27118db
--- /dev/null
+++ b/groovy/src/examples/webapps/groovlet-examples/zoo/zoo.groovy
@@ -0,0 +1,30 @@
+println """
+<html>
+    <head>
+        <title>Groovy Servlet Example - Visiting the zoo</title>
+    </head>
+    <body>
+    <a href="../"><img src="../images/return.gif" width="24" height="24" border="0"></a><a href="../">Return</a>
+    <p>
+"""
+
+Animal shark = new zoo.fish.Shark()
+Animal trout = new zoo.fish.Trout()
+Animal forelle = new zoo.HommingbergerGepardenforelle()
+
+println """
+     <p>Shark<br>
+     ${shark.saySomething("\"Where is the trout?\"")}
+
+     <p>Trout<br>
+     ${trout.saySomething("Here is the trout!")}
+
+     <p>Forelle<br>
+     ${forelle.saySomething("\"<a href=\"http://www.hommingberger-forelle.de\">There is no spoon.</a>\"")}
+     <!-- http://en.wikipedia.org/wiki/Nigritude_ultramarine -->
+"""
+
+println """
+    </body>
+</html>
+"""
diff --git a/groovy/src/examples/webapps/gsp-examples/readme.txt b/groovy/src/examples/webapps/gsp-examples/readme.txt
new file mode 100644
index 0000000..e00b01e
--- /dev/null
+++ b/groovy/src/examples/webapps/gsp-examples/readme.txt
@@ -0,0 +1,2 @@
+TODO First, implement http://jira.codehaus.org/browse/GROOVY-839
+TODO Second, setup WEB-INF and examples gsp files.
\ No newline at end of file
diff --git a/groovy/src/examples/webapps/template-examples/3.times.HelloWorld.html b/groovy/src/examples/webapps/template-examples/3.times.HelloWorld.html
new file mode 100644
index 0000000..39eec70
--- /dev/null
+++ b/groovy/src/examples/webapps/template-examples/3.times.HelloWorld.html
@@ -0,0 +1,13 @@
+<html>
+  <body>
+
+    <% 3.times { %>
+      Hello World!
+    <% } %>
+
+    <br>
+
+    session id = ${session.id}
+
+  </body>
+</html>
diff --git a/groovy/src/examples/webapps/template-examples/WEB-INF/lib/groovy-all-xyz.jar.placeholder b/groovy/src/examples/webapps/template-examples/WEB-INF/lib/groovy-all-xyz.jar.placeholder
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/groovy/src/examples/webapps/template-examples/WEB-INF/lib/groovy-all-xyz.jar.placeholder
diff --git a/groovy/src/examples/webapps/template-examples/WEB-INF/web.xml b/groovy/src/examples/webapps/template-examples/WEB-INF/web.xml
new file mode 100644
index 0000000..bfa7eab
--- /dev/null
+++ b/groovy/src/examples/webapps/template-examples/WEB-INF/web.xml
@@ -0,0 +1,26 @@
+<web-app xmlns="http://java.sun.com/xml/ns/j2ee"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
+    version="2.4">
+
+<servlet>
+    <servlet-name>template</servlet-name>
+    <servlet-class>groovy.servlet.TemplateServlet</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>template</servlet-name>
+    <url-pattern>*.htm</url-pattern>
+  </servlet-mapping>
+  
+  <servlet-mapping>
+    <servlet-name>template</servlet-name>
+    <url-pattern>*.html</url-pattern>
+  </servlet-mapping>
+
+  <servlet-mapping>
+    <servlet-name>template</servlet-name>
+    <url-pattern>*.template</url-pattern>
+  </servlet-mapping>
+
+</web-app>
diff --git a/groovy/src/examples/webapps/template-examples/javasystemproperties.htm b/groovy/src/examples/webapps/template-examples/javasystemproperties.htm
new file mode 100644
index 0000000..e80d84c
--- /dev/null
+++ b/groovy/src/examples/webapps/template-examples/javasystemproperties.htm
@@ -0,0 +1,22 @@
+<html>
+<head>
+<title>System properties</title>
+</head>
+<body>
+
+<h1>System properties</h1>
+<% keys = System.getProperties().keySet();
+   if (keys.isEmpty()) {
+     out.println('No keys.');
+   }
+   else {
+  %>
+<dl>
+    <% for (key in keys) { %>
+    <dt> <b> ${key} </b> </dt> 
+    <dd> <tt> ${System.getProperty(key)} </tt> </dd>
+    <% } %>
+</dl>
+<% } %>
+</body>
+</html>
diff --git a/groovy/src/examples/webapps/template-examples/readme.txt b/groovy/src/examples/webapps/template-examples/readme.txt
new file mode 100644
index 0000000..fb647a0
--- /dev/null
+++ b/groovy/src/examples/webapps/template-examples/readme.txt
@@ -0,0 +1,13 @@
+
+TemplateServlet examples
+
+[ /readme.txt ]
+This readme file. For clarity sake, it does not contain a welcome file and
+therefore the servlet container should list all files and subdirectories listed
+below.
+
+[ /3.times.HelloWorld.html ]
+Prints three times "Hello World!" and the session id.
+
+[ /javasystemproperties.htm ]
+Lists Java runtime system properties.
diff --git a/groovy/src/latex/ref/README.txt b/groovy/src/latex/ref/README.txt
new file mode 100644
index 0000000..a4158b1
--- /dev/null
+++ b/groovy/src/latex/ref/README.txt
@@ -0,0 +1,16 @@
+# purpose:
+#  to create a PDF file from latex source
+
+latex groovy-reference-card.tex
+dvips -Ppdf -t landscape groovy-reference-card.dvi
+ps2pdf groovy-reference-card.ps
+
+# or
+
+pdflatex groovy-reference-card.tex
+
+
+# p.s. best for debugging use...
+#   latex $1.tex; xdvi $1.dvi
+# or
+#  http://www.uoregon.edu/~koch/texshop/texshop.html
diff --git a/groovy/src/latex/ref/groovy-reference-card.tex b/groovy/src/latex/ref/groovy-reference-card.tex
new file mode 100644
index 0000000..db1e195
--- /dev/null
+++ b/groovy/src/latex/ref/groovy-reference-card.tex
@@ -0,0 +1,526 @@
+\documentclass[10pt, landscape]{article}
+\usepackage{multicol}
+\usepackage{graphics}
+
+% turn off header and footer
+\pagestyle{empty}
+
+\setlength{\textheight}{7.5 in}
+\setlength{\textwidth}{11.2 in}
+%\setlength{\hoffset}{-2 in}
+%\setlength{\voffset}{-1 in}
+%\setlength{\footskip}{12 pt}
+\setlength{\oddsidemargin}{-0.74 in}
+\setlength{\evensidemargin}{0.5 in}
+\setlength{\topmargin}{-0.75 in}
+\setlength{\headheight}{0 in}
+\setlength{\headsep}{0 in}
+
+
+\makeatletter
+\renewcommand{\section}{\@startsection{section}{1}{0mm}%
+                                {-1ex plus -.5ex minus -.2ex}%
+                                {0.5ex plus .2ex}%x
+                                {\normalfont\large\bfseries}}
+\renewcommand{\subsection}{\@startsection{subsection}{2}{0mm}%
+                                {-1explus -.5ex minus -.2ex}%
+                                {0.5ex plus .2ex}%
+                                {\normalfont\normalsize\bfseries}}
+\renewcommand\subsubsection{\@startsection{subsubsection}{3}{0mm}%
+                                {-1ex plus -.5ex minus -.2ex}%
+                                {1ex plus .2ex}%
+                                {\normalfont\small\bfseries}}
+\makeatother
+
+% Don't print section numbers
+\setcounter{secnumdepth}{0}
+
+
+\setlength{\columnsep}{0.5 in}
+%\setlength{\columnseprule}{0.4 pt}
+\setlength{\parindent}{0pt}
+\setlength{\parskip}{0pt plus 0.5ex}
+
+
+% -------------------------
+
+\begin{document}
+\raggedright
+\footnotesize
+
+
+
+% -- tabular, good for html output
+\newcommand{\keywordSummaryHeader}[1]{\begin{tabular}{ll} #1 \end{tabular}}
+\newcommand{\keywordSummary}[2]{{\bf #1} & #2 \\}
+
+\newcommand{\operatorSummaryHeader}[1]{\begin{tabular}{ll} #1 \end{tabular}}
+\newcommand{\operatorSummary}[2]{ #1 & {\bf #2} \\}
+%\newcommand{\methodDetailHeader}[1]{\begin{tabular}{rll} #1 \end{tabular}}
+%\newcommand{\methodDetail}[3]{#1 & #2 & #3 \\}
+
+% -- tabbing, good for pdf output
+%\newcommand{\keywordSummaryHeader}[1]{\begin{tabbing} jez \= jez-repeated-for-tab-settings-only \kill #1 \end{tabbing}}
+%\newcommand{\keywordSummary}[2]{#1 \\ \> #2 \\}
+
+\newcommand{\methodDetailHeader}[1]{\begin{tabbing} jezje \= jez-repeated-for-tab-settings-only \kill #1 \end{tabbing}}
+\newcommand{\methodDetail}[4]{#1\>{\bf #2}#3\\{\scriptsize #4}\\}
+
+\newcommand{\methodSummaryHeader}[1]{\begin{tabbing} jezj \= jez-repeated-for-tab-settings-only \kill #1 \end{tabbing}}
+\newcommand{\methodSummary}[4]{#1\>{\bf #2}#3\\}
+
+\newcommand{\toolDetailHeader}[1]{\begin{tabbing} je \= jez-repeated-for-tab-settings-only \kill #1 \end{tabbing}}
+\newcommand{\toolDetail}[4]{#1\>{\bf #2}#3\\{\scriptsize #4}\\}
+
+
+
+
+\newcommand{\head}[1]{{\large\textbf{#1}}\\}
+
+\begin{multicols}{3}
+\setlength{\premulticols}{1pt}
+\setlength{\postmulticols}{1pt}
+\setlength{\multicolsep}{1pt}
+\setlength{\columnsep}{2pt}
+
+
+\begin{tabular}{ll}
+\scalebox{0.35}{\includegraphics{one-groovy-logo.mps}}  &   {\large\textbf{Groovy}}      \\
+                                                        &   {\large\textbf{Reference}}   \\
+                                                        &   {\large\textbf{Summary}}
+\end{tabular}
+
+GROOVY-1.0
+
+{\em Fourth Edition\/} (May 2007)
+
+\vskip 0.05 in
+Italics are used, where possible, to show differences to Java
+\vskip 0.15 in
+
+\section{Keywords}
+\subsection{Grammar}
+\keywordSummaryHeader{
+\keywordSummary{{\em as}        }{   import {\em type\/} as {\em id\/}}
+\keywordSummary{                }{   {\em expr\/} as {\em type\/}}
+\keywordSummary{assert          }{   assert {\em expr expr?\/}}
+\keywordSummary{break           }{   break}
+\keywordSummary{case            }{   switch {\em expr\/} case {\em expr stmt*\/}}
+\keywordSummary{catch           }{   try {\em stmt*\/} catch {\em type id stmt*\/}}
+\keywordSummary{class           }{   {\em mod*\/} class {\em id\/}}
+\keywordSummary{continue        }{   continue}
+\keywordSummary{{\em def}       }{   def {\em methodDeclaration\/}}
+\keywordSummary{                }{   def {\em variableDeclaration\/}}
+\keywordSummary{default         }{   switch {\em expr\/} case default{\em stmt*\/} }
+\keywordSummary{else            }{   if {\em expr stmt*\/} else if {\em expr stmt*\/}}
+% todo - enum
+\keywordSummary{extends         }{   {\em mod*\/} class {\em id\/} extends {\em type\/}}
+% todo - syntax supports "generic" extends, but has no effect in groovy 1.0
+\keywordSummary{finally         }{   try {\em stmt*} finally {\em stmt*}}
+%\keywordSummary{for             }{   for {\em expr*};{\em expr};{\em expr} {\em stmt*\/}}
+\keywordSummary{{\em for}       }{   for {\em id\/} in {\em id stmt*\/}}
+\keywordSummary{if              }{   if {\em expr stmt*\/} else if {\em expr stmt*\/}}
+\keywordSummary{{\em in}        }{   for {\em id\/} in {\em id stmt*\/}}
+\keywordSummary{                }{   if {\em id\/} in {\em collection stmt*\/}}
+\keywordSummary{implements      }{   {\em mod*\/} class {\em id\/} implements {\em type*}}
+\keywordSummary{import          }{   import {\em type\/}}
+\keywordSummary{instanceof      }{   {\em expr\/} instanceof {\em type}}
+\keywordSummary{interface       }{   {\em mod*\/} interface {\em id\/}}
+%\keywordSummary{{\em mixin}     }{   {\em mod*\/} mixin {\em id\/}}
+\keywordSummary{new             }{   new {\em type\/} }
+\keywordSummary{package         }{   package {\em id\/}}
+\keywordSummary{return          }{   return {\em expr?/} }
+% not sure if 'super' and 'this' are worth including here?
+\keywordSummary{switch          }{   switch {\em expr\/} case {\em expr stmt*\/} }
+\keywordSummary{throw           }{   throw {\em expr\/} }
+\keywordSummary{throws          }{   {\em methodDeclaration} throws {\em type\/} }
+\keywordSummary{try             }{   try {\em stmt*\/} catch {\em type id stmt*\/}}
+\keywordSummary{while           }{   while {\em expr\/} {\em stmt*\/}}
+% quiet on statics, static imports etc (if they want to use statics, make em look! bwha ha ha)
+}
+
+
+\subsection{Modifiers}
+\keywordSummaryHeader{
+\keywordSummary{abstract        }{   abstract class {\em id\/}}
+\keywordSummary{Annotations     }{   @Annotation}
+\keywordSummary{Annotations     }{   @Annotation(x = 3, y = 5)}
+\keywordSummary{final           }{   final int {\em id\/} = 42}
+%todo - native
+\keywordSummary{private         }{   private String mySecret}
+\keywordSummary{protected       }{   protected List foo}
+\keywordSummary{public          }{   public Date newYear}
+\keywordSummary{static          }{   static int {\em id\/}}
+\keywordSummary{strictfp        }{   strictfp float {\em id\/}}
+\keywordSummary{synchronized    }{   synchronized myMethod()}
+\keywordSummary{transient       }{   transient long distance}
+\keywordSummary{volatile        }{   volatile int diamond}
+}
+
+\subsection{Lists and Maps}
+\keywordSummaryHeader{
+\keywordSummary{                }{   {\em type\/}[] {\em id\/} = new {\em type\/}[{\em size\/}]}
+\keywordSummary{                }{   List people = [fred, barney] }
+\keywordSummary{                }{   Map wilma = [friend:betty, age:unknown] }
+}
+\subsection{Operators}
+\operatorSummaryHeader{
+\operatorSummary{ {\em id\/}                =                                       {\em expr\/} }{ assign}
+\operatorSummary{ {\em expr\/}              \&                                      {\em expr\/} }{ bitwise-and}
+\operatorSummary{ {\em id\/}                \&=                                     {\em expr\/} }{ bitwise-and assign}
+\operatorSummary{ {\em id\/} =              ~                                       {\em expr\/} }{ bitwise-not} %todo - doesn't render
+\operatorSummary{ {\em expr\/}              \textbar\/                              {\em expr\/} }{ bitwise-or}
+\operatorSummary{ {\em id\/}                \textbar\/=                             {\em expr\/} }{ bitwise-or assign}
+\operatorSummary{ {\em expr\/}              \textgreater\textgreater\textgreater\/  {\em expr\/} }{ bitwise-shift-right}
+\operatorSummary{ {\em id\/}                \textgreater\textgreater\textgreater=\/ {\em expr\/} }{ bitwise-shift-right assign}
+%\operatorSummary{ {\em expr\/}              \wedge                                  {\em expr\/} }{ bitwise-exclusive-or}
+%\operatorSummary{ {\em expr\/}              \wedge\/=                                  {\em expr\/} }{ bitwise-exclusive-or assign}
+\operatorSummary{ {\em expr\/}              \textless=\textgreater\/                {\em expr\/} }{ {\em compare to\/}}
+\operatorSummary{                           $--$                                    {\em id\/}   }{ pre-decrement}
+\operatorSummary{ {\em expr\/}              $/$                                     {\em expr\/} }{ division}
+\operatorSummary{ {\em id\/}                $/=$                                    {\em expr\/} }{ division assign}
+\operatorSummary{ {\em expr\/}              ==                                      {\em expr\/} }{ equal}
+\operatorSummary{ {\em expr\/}              $>=$                                    {\em expr\/} }{ greater or equal}
+\operatorSummary{ {\em expr\/}              $>$                                     {\em expr\/} }{ greater than}
+\operatorSummary{                           $++$                                    {\em id\/}   }{ pre-increment}
+\operatorSummary{ {\em expr\/}              \&\&                                    {\em expr\/} }{ logical-and}
+\operatorSummary{ {\em expr\/}              $<=$                                    {\em expr\/} }{ less or equal}
+\operatorSummary{                           !                                       {\em expr\/} }{ logical-not}
+\operatorSummary{ {\em expr\/}              \textbar\textbar\/                      {\em expr\/} }{ logical-or}
+\operatorSummary{ {\em expr\/}              \textless\/                             {\em expr\/} }{ less}
+\operatorSummary{ {\em expr\/}              .\&                                     {\em expr\/} }{ {\em member-pointer\/}}
+\operatorSummary{ {\em expr\/}              -                                       {\em expr\/} }{ minus}
+\operatorSummary{ {\em id\/}                -=                                      {\em expr\/} }{ minus assign}
+\operatorSummary{ {\em expr\/}              \%                                      {\em expr\/} }{ modulus}
+\operatorSummary{ {\em id\/}                \%=                                     {\em expr\/} }{ modulus assign}
+\operatorSummary{ {\em expr\/}              !=                                      {\em expr\/} }{ not equal}
+\operatorSummary{ {\em expr\/}              ?.                                      {\em expr\/} }{ {\em optional dot\/}}
+\operatorSummary{ {\em expr\/}              +                                       {\em expr\/} }{ plus}
+\operatorSummary{ {\em id\/}                +=                                      {\em expr\/} }{ plus assign}
+\operatorSummary{ {\em id\/}                $++$                                                 }{ post-decrement}
+\operatorSummary{ {\em id\/}                $++$                                                 }{ post-increment}
+}
+\subsection{Operators2}
+\operatorSummaryHeader{
+\operatorSummary{ {\em expr\/}              ?                        {\em expr\/} : {\em expr\/} }{ ternary if/else}
+\operatorSummary{ {\em expr\/}              ..\textless                             {\em expr\/} }{ {\em range exclusive\/}}
+\operatorSummary{ {\em expr\/}              ..                                      {\em expr\/} }{ {\em range inclusive\/}}
+\operatorSummary{                           /{\em regex\/}/                                      }{ {\em regex literal\/}}
+\operatorSummary{ {\em id\/}                =~                                    /{\em regex\/}/}{ {\em regex find\/}} %todo - tilde doesn't render
+\operatorSummary{ {\em id\/}                ==~                                   /{\em regex\/}/}{ {\em regex match\/}} %todo - tilde doesn't render
+\operatorSummary{ {\em expr\/}              .@                                     {\em expr\/} }{ {\em select slot\/}}
+\operatorSummary{ {\em expr\/}              \textless\textless\/                   {\em expr\/} }{ shift-left}
+\operatorSummary{ {\em expr\/}              \textless\textless=\/                  {\em expr\/} }{ shift-left-assign}
+\operatorSummary{                           *                                      {\em cltn\/} }{ {\em spread arg\/}}
+\operatorSummary{ {\em cltn\/}              *.                                     {\em expr\/} }{ {\em spread dot\/}}
+\operatorSummary{                           *:                                     {\em map \/} }{ {\em spread-map arg\/}}
+\operatorSummary{ {\em expr\/}              \textgreater\textgreater\/             {\em expr\/} }{ shift-right}
+\operatorSummary{ {\em expr\/}              \textgreater\textgreater=\/            {\em expr\/} }{ shift-right-assign}
+\operatorSummary{ {\em expr\/}              *                                      {\em expr\/} }{ multiply}
+\operatorSummary{ {\em id\/}                *=                                     {\em expr\/} }{ multiply assign}
+\operatorSummary{ {\em expr\/}              **                                     {\em expr\/} }{ to-the-power-of}
+\operatorSummary{ {\em id\/}                **=                                    {\em expr\/} }{ to-the-power-of assign}
+\operatorSummary{                           `{\em string\/}'                                    }{ {\em string literal\/}}
+\operatorSummary{                           ``{\em string\/}"                                   }{ string literal}
+\operatorSummary{                           ` ` `{\em string\/}' ' '                            }{ {\em multi-line-string literal\/}}
+\operatorSummary{                           ``````{\em string\/}"""                             }{ {\em multi-line-string literal\/}} 
+\operatorSummary{                           $-$                                    {\em id\/}   }{ unary-minus}
+\operatorSummary{                           $+$                                    {\em id\/}   }{ unary-plus}
+\operatorSummary{ {\em type\/}              ...                                    {\em id\/}   }{ variable parameters}
+
+%todo - perhaps put index operators here, i.e. foo[1]
+
+}
+% not supported in groovy yet
+% "public @interface Foo{}"
+% "public @interface Foo{int bar() default 123}"
+
+
+% \subsection{Types}
+% \keywordSummaryHeader{
+% \keywordSummary{boolean         }{   true {\em or\/} false}
+% \keywordSummary{byte            }{   -128 to 127}
+% \keywordSummary{char            }{   u0000 to uFFFF }
+% \keywordSummary{double          }{   $\pm 4.9E^{-324}$ to $\pm 1.8E^{+308}$}
+% \keywordSummary{float           }{   $\pm 1.4E^{-45}$ to $\pm 3.4E^{+38}$}
+% \keywordSummary{int             }{   -2,147,483,648 to 2,147,483,647}
+% \keywordSummary{long            }{   -9,223,372,036,854,775,808 }
+% \keywordSummary{                }{   to 9,223,372,036,854,775,807}
+% \keywordSummary{short           }{   -32,768 to 32,767}
+% \keywordSummary{void            }{   }
+% todo - exponents e.g. 1.2e-10
+% todo - hex digits e.g. 0xCaFe
+% todo - big decimal/int literals e.g. 9.8g and 9g
+% todo - float and double literals e.g. 34.4f and 53.5d
+% todo - long literals e.g. 12l
+% }
+
+\vskip 0.5 in
+
+\section{Groovy JDK}
+% based upon DefaultGroovyMethods r1.114
+\subsection{Collections and properties}
+{\scriptsize {\em Note: cltn in this sense {\em can\/} include lists, sets, matchers, strings, charSeqs and arrays\/}}
+
+\methodDetailHeader{
+% a.getAt(b) and a.putAt(b,c) are equiv to a[b] and a[b] = c respectiv. 
+%\methodDetail{ cltn. }{ getAt}{(index\textbar indices\textbar range) }{ obtains objects at specified {\em indicies\/} or in {\em range\/}}
+%\methodDetail{ cltn. }{ getAt}{(property)                         }{ obtains {\em property\/} from each object in collection}
+%\methodDetail{  obj. }{ putAt}{(idx, value)                       }{ put {\em value\/} at position {\em idx\/} } 
+%\methodDetail{  obj. }{ putAt}{(propertyName, value)              }{ put {\em value\/} in the {\em propertyName\/} }
+\methodDetail{  cltn }{[}{index\textbar indices\textbar range\textbar property {\bf ]}}{ obtains objects at specified location}
+\methodDetail{   obj }{[}{index\textbar property{\bf ] =} value    }{ put {\em value\/} at location } 
+\methodDetail{  cltn }{ \textless\textless}{ obj                  }{ append obj to collection}
+\methodSummary{  cltn }{ $+$}{ obj                                   }{ append obj to collection}
+%\methodDetail{  cltn }{ $+$}{ cltn                                  }{ append collection to collection}
+\methodSummary{  list }{ $-$}{ cltn                                  }{ remove items from list}
+\methodDetail{  cltn }{ $*$}{ num                                   }{ repeat items in collection a number of times}
+\methodDetail{       }{  }{                                       }{ }
+% no count() or getAt(range) for matchers!
+\methodDetail{  obj. }{ getProperties}{()                         }{ obtain Map of properties on {\em obj\/}}
+\methodDetail{  obj. }{ getMetaPropertyValues}{()                 }{ obtain List of meta properties on {\em obj\/}}
+\methodDetail{ cltn. }{ count}{(obj) *                            }{ counts number of occurances of {\em obj\/} in collection}
+\methodSummary{  map. }{ get}{(key, defaultValue) *                }{ try to get {\em key \/} from map, otherwise put {\em defaultValue\/} in map and return }
+\methodSummary{ cltn. }{ size}{() *                                }{ returns size of an array or string }
+\methodDetail{       }{   }{                                      }{ }
+\methodDetail{ cltn. }{ collect}{() \{closure\} *                 }{ new collection of {\em closure \/} transformed items }
+\methodDetail{  obj. }{ each}{() \{closure\} *                    }{ iterate through object applying {\em closure\/} }
+\methodDetail{  obj. }{ eachWithIndex}{() \{closure\}             }{ iterate through object with a counter applying {\em closure\/} }
+\methodDetail{  obj. }{ find}{() \{closure\} *                    }{ find first item picked by {\em closure \/} condition }
+\methodDetail{  obj. }{ findAll}{() \{closure\} *                 }{ returns all items picked by {\em closure\/} condition }
+\methodDetail{  obj. }{ findIndexOf}{() \{closure\}               }{ return first index that matches condition {\em closure\/} }
+\methodSummary{  obj. }{ grep}{(regex\textbar range\textbar etc..) *               }{ returns all matching items }
+\methodDetail{ cltn. }{ inject}{(value) \{closure\}               }{ returns closure( closure( closure(value,item0) ,item1) ,item2) ... }
+\methodDetail{ cltn. }{ max}{([comparator]) \{closure\} *         }{ returns the maximum value found in the collection }
+\methodDetail{ cltn. }{ min}{([comparator]) \{closure\} *         }{ returns the minimum value found in the collection }
+\methodDetail{ list. }{ reverseEach}{() \{closure\}               }{ iterate backwards through list applying {\em closure\/}}
+\methodDetail{ cltn. }{ sort}{([comparator]) *                    }{ sorts collection into a list, optionally using a {\em comparator\/}}
+\methodDetail{ cltn. }{ sort}{() \{closure\} *                    }{ sorts collection into a list using closure as comparator}
+\methodDetail{       }{     }{                                    }{ }
+\methodSummary{ cltn. }{ asImmutable}{()                           }{ create an immutable collection }
+\methodSummary{ cltn. }{ asSynchronized}{()                        }{ create a synchronized collection }
+\methodSummary{ list. }{ flatten}{()                               }{ flattens list }
+\methodDetail{ list. }{ intersect}{(cltn)                         }{ returns intersection of list and collection }
+\methodDetail{ cltn. }{ join}{(separator) *                       }{ concatenate all the elements of {\em cltn\/} into a string }
+\methodDetail{ list. }{ pop}{() *                                   }{ remove and return last item from list }
+%todo: what happened to push() :-)
+\methodSummary{ cltn. }{ reverse}{()                               }{ reverses order of collection or string }
+\methodDetail{  map. }{ subMap}{(keys)                            }{ returns a map of the given keys }
+\methodSummary{ cltn. }{ toList}{()                                }{ turns any collection into a list }
+}
+
+\vskip 0.75 in
+
+\subsection{Strings}
+\methodDetailHeader{
+\methodDetail{  str }{ $++$}{                                      }{ increment the number at end of string}
+\methodDetail{  str }{ $--$}{                                      }{ reduce the number at end of string}
+\methodSummary{  str }{ $+$}{ obj                                  }{ concatenate {\em str\/} and {\em obj\/} together}
+\methodDetail{  str }{ $-$}{ obj                                   }{ remove the first {\em obj\/} from {\em str\/}}
+%\methodDetailHeader{  num }{ + str                                   }{ concatenate str to the end of num }
+\methodSummary{  str }{ \textless\textless}{ value                }{ }
+%\methodDetail{  strBuff }{ \textless\textless}{ value            }{ }
+%center and centre :-) !!!!
+\methodDetail{ str. }{ padRight}{(size,[padding])                }{ left justifies string padded out to {\em size\/}} 
+\methodDetail{ str. }{ center}{(size, [padding])                 }{ centers a string padded out to {\em size\/}}
+% name change for padLeft?
+\methodDetail{ str. }{ padLeft}{(size,[padding])                 }{ right justifies string padded out to {\em size\/}} 
+\methodDetail{ str. }{ contains}{(str2) *                        }{ true if {\em str} contains {\em str2}}
+\methodDetail{ str. }{ eachMatch}{(regex) \{closure\} *          }{ apply {\em closure\/} to each match of the specified {\em regex\/} }
+%leftshift
+\methodSummary{ str. }{ toCharacter}{()                           }{ }
+\methodSummary{ str. }{ toList}{()                                }{ }
+\methodSummary{ str. }{ toLong}{()                                }{ }
+\methodSummary{ str. }{ toURL}{()                                 }{ }
+\methodSummary{ str. }{ tokenize}{([token]) *                     }{ }
+}
+
+
+\subsection{Input/output}
+%todo: unify method names
+{\scriptsize {\em Note: url in this sense {\em can\/} include urls, files, streams and readers\/}}
+\methodDetailHeader{
+\methodDetail{   dir. }{ eachFile}{() \{closure\}                 }{ apply {\em closure\/} to each file in {\em dir\/}}
+\methodDetail{   dir. }{ eachFileRecurse}{() \{closure\}          }{ apply {\em closure\/} to each file in {\em dir\/} recursively}
+\methodDetail{ }{ }{ }{ }
+\methodSummary{   url. }{ eachByte}{() \{closure\}                 }{ apply {\em closure\/} to each byte in {\em file\/}}
+\methodDetail{    url. }{ eachLine}{() \{closure\}                 }{ apply {\em closure\/} to each line of input}
+%\methodDetail{  file. }{ newInputStream}{()                       }{ obtain Stream to read from {\em file\/}}
+%\methodDetail{  file. }{ newReader}{([charset])                   }{ obtain Reader from {\em file}}
+\methodSummary{  file. }{ readBytes}{()                            }{ read bytes from {\em file\/}}
+\methodDetail{    in. }{ readLine}{()                             }{ read a single, whole line from {\em in\/}}
+\methodDetail{  file. }{ readLines}{()                            }{ obtain List of lines from {\em file}}
+\methodDetail{   url. }{ getText}{([charset])                     }{ fetch all available text from resource }
+\methodDetail{  file. }{ splitEachLine}{(regEx) \{closure\}       }{ read in each line of {\em file\/}, apply {\em closure\/} to data delimited by {\em regEx\/}}
+%\methodDetail{  file. }{ withInputStream}{() \{closure\}          }{ apply {\em closure\/} to a new InputStream on {\em file\/}, then close}
+\methodDetail{   url. }{ withReader}{() \{closure\}               }{ apply {\em closure\/} to {\em in\/}, then close {\em in\/}}
+%\methodDetail{    in. }{ withStream}{() \{closure\}               }{ apply {\em closure\/} to {\em in\/}, then close {\em in\/}}
+\methodDetail{ }{ }{ }{ }
+\methodDetail{    out }{ \textless\textless}{ obj                 }{ append {\em obj\/} to stream, process or socket}
+\methodSummary{  file. }{ append}{(text, [charset])                }{ append {\em text\/} at end of {\em file\/} with {\em charset\/} encoding}
+%\methodSummary{  file. }{ asWritable}{([charset]) *                }{ wrap {\em file\/} as Writable }
+\methodSummary{ bytes. }{ encodeBase64}{()                         }{ wraps {\em file\/} in Writable with contents encoded as Base64}
+\methodSummary{   str. }{ decodeBase64}{()                         }{ unwraps bytes from Base64 encoded {\em str\/}}
+\methodDetail{     in. }{ filterLine}{([out]) \{closure\}            }{ read from {\em in\/} and write each line to {\em out\/} only if {\em closure\/}}
+%\methodDetail{  file. }{ newOutputStream}{()                      }{ obtain Stream to write to {\em file\/}}
+%\methodDetail{  file. }{ newPrintWriter}{([charset])              }{ obtain PrintWriter to {\em file\/}}
+%\methodDetail{  file. }{ newWriter}{([charset],[append])          }{ obtain Writer to {\em file\/}}
+% todo: transformChar(out) could be more in line with eachByte?
+\methodSummary{   in. }{ transformChar}{(out) \{closure\}           }{ read {\em in\/} and apply {\em closure\/} to each character being written {\em out\/}}  
+\methodDetail{   in. }{ transformLine}{(out) \{closure\}          }{ read {\em in\/} and apply {\em closure\/} to each line being written {\em out\/}}  
+\methodSummary{  file. }{ withOutputStream}{() \{closure\}         }{ apply {\em closure\/} to a new OutputStream on {\em file\/}, then close}
+\methodSummary{  file. }{ withPrintWriter}{() \{closure\}          }{ apply {\em closure\/} to a new PrintWriter on {\em file\/}, then close}
+\methodSummary{   out. }{ withStream}{() \{closure\}               }{ apply {\em closure\/} to {\em out\/}, then close {\em out\/}}
+\methodSummary{   skt. }{ withStreams}{() \{closure\}              }{ apply {\em closure\/} to [in,out], then close them}
+\methodSummary{   out. }{ withWriter}{([charset]) \{closure\}      }{ apply {\em closure\/} to {\em out\/} using {\em charset\/}, then close {\em out\/}}
+\methodDetail{  file. }{ withWriterAppend}{(charset) \{closure\}  }{ apply {\em closure\/} to a new appending Writer on {\em file\/}, then close}
+\methodSummary{  file. }{ write}{(text, [charset]) *               }{ write {\em text\/} to {\em file\/} using {\em charset\/}}
+\methodSummary{   out. }{ writeLine}{(line)                        }{ write {\em line\/} and append a newline}
+}
+
+\subsection{Misc}
+\methodDetailHeader{
+\methodSummary{   date }{ $++$}{                                       }{ add one day to the date }
+\methodSummary{   date }{ $--$}{                                       }{ subtract one day from the date }
+\methodSummary{   date }{ $+$}{ days                                   }{ add {\em days\/} to the date }
+\methodSummary{   date }{ $-$}{ days                                   }{ subtract {\em days\/} from the date }
+\methodDetail{   obj. }{ dump}{() *                                }{ returns a detailed dump string of {\em obj\/}}
+\methodDetail{   obj. }{ inspect}{() *                             }{ returns the groovy expression used to create this instance of {\em obj}}
+\methodSummary{   obj. }{ invokeMethod}{(method, args) *             }{ invokes {\em method\/} on {\em obj\/} }
+\methodSummary{   obj. }{ print}{(obj) *                             }{ print an object }
+\methodSummary{   obj. }{ print}{(out) *                             }{ print this object to {\em out} }
+\methodSummary{   obj. }{ println}{(object) *                        }{ print an object and then terminate line }
+\methodSummary{   obj. }{ println}{(out) *                           }{ print this object to {\em out} and then terminate line }
+\methodDetail{   num. }{ step}{(endNum, stepNum) \{closure\}        }{ iterates {\em closure\/} starting at this number, stepping up to {\em endNum} }
+\methodDetail{   num. }{ times}{() \{closure\} *                    }{ iterates {\em closure\/} num times }
+%todo: upTo() instead of upto()
+\methodDetail{   num. }{ upto}{(endNum) \{closure\}                 }{ iterates {\em closure\/} starting at this number, up to {\em endNum} }
+\methodDetail{   obj. }{ use}{(categoryClass) \{closure\} *         }{ attach {\em closure\/} to specified class }
+\methodDetail{   obj. }{ use}{(categoryClassList) \{closure\} *     }{ attach {\em closure\/} to specified classes }
+\methodSummary{    ps. }{ waitForOrKill}{(milliSecs)                 }{ wait for process to finish, or stop after specified {\em milliSecs} }
+\methodSummary{    ps. }{ getText}{()                                }{ }
+%Static methods shown with \em
+\methodSummary{    Thd.}{ {\em start}}{() \{closure\}                      }{ }
+\methodSummary{    Thd.}{ {\em startDaemon}}{() \{closure\}                }{ }
+\methodSummary{    Mtr.}{ {\em getLastMatcher}}{()                         }{ }
+}
+
+
+\section{Groovy Developers Kit}
+\subsection{Groovy SQL}
+%START
+% sql = Sql.newInstance(
+%END
+\methodSummaryHeader{
+%\methodSummary{      }{ newInstance}{(url,[props],[driver]) }
+\methodSummary{      }{ Sql}{(datasource\textbar connection\textbar sql)}{ }
+\methodSummary{      }{ newInstance}{(url,[user],[pass],[driver]) }{ }
+\methodSummary{ sql. }{ call}{(str,[params]) }{ }
+\methodSummary{ sql. }{ eachRow}{(str,[params]) \{closure\}}{ }
+\methodSummary{ sql. }{ execute}{(str,[params]) }{ }
+\methodSummary{ sql. }{ executeUpdate}{(str,[params]) }{ }
+\methodSummary{ sql. }{ close}{() }{ }
+}
+
+\section{Snippets}
+\begin{tabbing} jezjezjezjezjez \= jez-repeated-for-tab-settings-only \kill 
+{\bf Closures}\>
+{\scriptsize implicit/explicit}\\
+\verb+ { println it }+\\
+\verb+ { x, y -> println(x * y) }+\\
+{\bf Dynamic Members}\>
+{\scriptsize }\\
+\verb+ foo.bar.mooky."$randomProperty"+\\
+{\bf Construct and call setters}\>
+{\scriptsize }\\
+\verb+ new Foo(bar:123, mooky:'red')+\\
+{\bf Builder}\>
+{\scriptsize Markup, Node, StreamingMarkup, DOM, }\\
+{\scriptsize Ant, JavaDoc, Swt, JFace, Swing}\\
+\verb+farm = new NodeBuilder().farm(){animal(type:'pig')}+\\
+{\bf GPath}  \>
+{\scriptsize walks the tree}\\
+\verb+farm.animal.collect{it['@type']}.contains('pig')+\\
+\end{tabbing}
+%\section{Sample Groovy Builder}
+%{\scriptsize Builders are provided to construct these tree structures: {\em Markup, Node, StreamingMarkup, DOM, Ant, JavaDoc, Swt, JFace, Swing}}
+%\begin{verbatim}
+%builder = new NodeBuilder()
+%
+%farm = builder.farm(type:'livestock', acres:30) {
+%                 animal(type:'pig')
+%                 animal(type:'sheep')
+%                 tractor(wheelbase:90)
+%}
+%
+%assert farm.animal.collect{it['@type']}.contains('pig')
+%
+%\end{verbatim}
+
+%\section{Sample Groovy code}
+%\begin{verbatim}
+%package com.example
+%
+%import org.example.Cow
+%
+%/**
+% * This is a sample of Groovy
+% */
+%class HighlandCow extends Cow {
+%  
+%  void mooAtPeople() {
+%    sql = Sql.newInstance("jdbc:foo:bar")
+%    sql.eachRow("select * from PERSON") {
+%      println("Och-Aye ${it.firstname}")
+%    }
+%  }
+%}        
+%\end{verbatim}
+
+\section{Tools}
+\subsection{Ant tasks}
+\begin{verbatim}
+<taskdef
+    name="groovy" 
+    classname="org.codehaus.groovy.ant.Groovy"/>
+<groovy>ant.mkdir(dir:"myDir")</groovy>
+
+<taskdef
+    name="groovyc" 
+    classname="org.codehaus.groovy.ant.Groovyc"/>
+<groovyc 
+    destdir="${build.classes.dir}" 
+    srcdir="${src.dir}"/>
+
+<taskdef
+    name="groovydoc"
+    classname="org.codehaus.groovy.ant.Groovydoc"/>
+<groovydoc
+    sourcepath="${src.dir}" 
+    destdir="${javadoc.dir}" 
+    packagenames="${javadoc.packages}"/>
+\end{verbatim}
+\subsection{Command Line Tools}                                      
+\toolDetailHeader{
+\toolDetail{ \textgreater }{ groovy        }{ [options] Telson.groovy [args] }{ interpret and execute specified groovy script }
+\toolDetail{ \textgreater }{ groovyc       }{ [options] Darv.groovy          }{ compiles specified groovy script }
+\toolDetail{ \textgreater }{ groovysh      }{                                }{ begins an interactive groovy session }
+\toolDetail{ \textgreater }{ groovyConsole }{                                }{ begins a GUI based groovy session }
+\toolDetail{ \textgreater }{ java2groovy   }{ Astra.java                     }{ rough source code translation of Java into Groovy}
+
+%\toolDetail{ \textgreater }{ grok   }{                  }{ groovy javadoc }
+}
+
+\rule{0.3\linewidth}{0.25pt}
+\scriptsize
+
+Copyright \copyright\ 2004, 2007 Jeremy Rayner 
+\textless\/groovy@ross-rayner.com\textgreater\/\/http://javanicus.com/blog2
+
+$Revision$
+$Date$
+
+
+
+\end{multicols}
+\end{document}
diff --git a/groovy/src/latex/ref/one-groovy-logo.eps b/groovy/src/latex/ref/one-groovy-logo.eps
new file mode 100644
index 0000000..b96a010
--- /dev/null
+++ b/groovy/src/latex/ref/one-groovy-logo.eps
Binary files differ
diff --git a/groovy/src/latex/ref/one-groovy-logo.mps b/groovy/src/latex/ref/one-groovy-logo.mps
new file mode 100644
index 0000000..570bb60
--- /dev/null
+++ b/groovy/src/latex/ref/one-groovy-logo.mps
@@ -0,0 +1,1666 @@
+%!PS-Adobe-3.0 EPSF-3.0
+%%BoundingBox: 197 340 415 450 
+%%Creator: MetaPost
+%%CreationDate: 2005.04.17:1127
+%%Pages: 1
+%%EndProlog
+%%Page: 1 1
+newpath 372.793 340.699 moveto
+305.645 366.988 lineto
+238.5 340.707 lineto
+265.012 382.602 lineto
+197 408.258 lineto
+280.531 407.863 lineto
+305.641 450 lineto
+330.754 407.859 lineto
+414.289 408.246 lineto
+346.277 382.598 lineto
+372.793 340.699 lineto
+ closepath fill
+ 0.35832 0.70496 0.749 setrgbcolor
+newpath 363.707 348.195 moveto
+305.703 370.902 lineto
+247.699 348.199 lineto
+270.602 384.391 lineto
+211.848 406.551 lineto
+284.008 406.211 lineto
+305.699 442.609 lineto
+327.395 406.211 lineto
+399.559 406.543 lineto
+340.805 384.387 lineto
+363.707 348.195 lineto
+ closepath fill
+ 0 setgray
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath fill
+ 0 9.7534 dtransform truncate idtransform setlinewidth pop [] 0 setdash
+ 1 setlinejoin 10 setmiterlimit
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath stroke
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath fill
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath stroke
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath fill
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath fill
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath stroke
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath stroke
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath fill
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath fill
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath stroke
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath stroke
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath fill
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath stroke
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 353.43 362.887 349.023 362.887 curveto
+348.492 362.887 347.914 363.043 347.656 363.391 curveto
+349.516 364.586 350.68 365.812 351.508 367.586 curveto
+352.504 369.719 352.492 371.711 352.359 373.551 curveto
+352.711 373.344 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath fill
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 353.43 362.887 349.023 362.887 curveto
+348.492 362.887 347.914 363.043 347.656 363.391 curveto
+349.516 364.586 350.68 365.812 351.508 367.586 curveto
+352.504 369.719 352.492 371.711 352.359 373.551 curveto
+352.711 373.344 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath stroke
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath fill
+ 0 0.8128 dtransform truncate idtransform setlinewidth pop 0 setlinejoin
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath stroke
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath fill
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath stroke
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath fill
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath fill
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath stroke
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath stroke
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath fill
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath fill
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath stroke
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath stroke
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath fill
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath stroke
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 365.262 363.73 360.859 363.73 curveto
+360.32 363.73 359.746 363.883 359.488 364.234 curveto
+361.348 365.426 362.512 366.648 363.344 368.426 curveto
+364.336 370.559 364.32 372.551 364.191 374.387 curveto
+364.543 374.184 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath fill
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 365.262 363.73 360.859 363.73 curveto
+360.32 363.73 359.746 363.883 359.488 364.234 curveto
+361.348 365.426 362.512 366.648 363.344 368.426 curveto
+364.336 370.559 364.32 372.551 364.191 374.387 curveto
+364.543 374.184 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath stroke
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath fill
+ 0 9.7534 dtransform truncate idtransform setlinewidth pop 1 setlinejoin
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath stroke
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath fill
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath stroke
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath fill
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath fill
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath stroke
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath stroke
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath fill
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath fill
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath stroke
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath stroke
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath fill
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath stroke
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 365.262 363.73 360.859 363.73 curveto
+360.32 363.73 359.746 363.883 359.488 364.234 curveto
+361.348 365.426 362.512 366.648 363.344 368.426 curveto
+364.336 370.559 364.32 372.551 364.191 374.387 curveto
+364.543 374.184 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath fill
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 365.262 363.73 360.859 363.73 curveto
+360.32 363.73 359.746 363.883 359.488 364.234 curveto
+361.348 365.426 362.512 366.648 363.344 368.426 curveto
+364.336 370.559 364.32 372.551 364.191 374.387 curveto
+364.543 374.184 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath stroke
+ 1 setgray
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath fill
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath fill
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath fill
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath fill
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath fill
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath fill
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath fill
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+365.691 359.168 351.629 362.602 347.227 362.602 curveto
+346.691 362.602 346.113 362.754 345.859 363.105 curveto
+347.715 364.301 348.879 365.523 349.711 367.301 curveto
+350.703 369.43 350.691 371.426 350.559 373.262 curveto
+350.914 373.055 352.043 372.539 352.508 372.539 curveto
+354.414 372.539 365.035 367.027 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath fill
+ 0 setgray
+newpath 372.793 340.699 moveto
+305.645 366.988 lineto
+238.5 340.707 lineto
+265.012 382.602 lineto
+197 408.258 lineto
+280.531 407.863 lineto
+305.641 450 lineto
+330.754 407.859 lineto
+414.289 408.246 lineto
+346.277 382.598 lineto
+372.793 340.699 lineto
+ closepath fill
+ 0.35832 0.70496 0.749 setrgbcolor
+newpath 363.707 348.195 moveto
+305.703 370.902 lineto
+247.699 348.199 lineto
+270.602 384.391 lineto
+211.848 406.551 lineto
+284.008 406.211 lineto
+305.699 442.609 lineto
+327.395 406.211 lineto
+399.559 406.543 lineto
+340.805 384.387 lineto
+363.707 348.195 lineto
+ closepath fill
+ 0 setgray
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath fill
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath stroke
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath fill
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath stroke
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath fill
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath fill
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath stroke
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath stroke
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath fill
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath fill
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath stroke
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath stroke
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath fill
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath stroke
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 353.43 362.887 349.023 362.887 curveto
+348.492 362.887 347.914 363.043 347.656 363.391 curveto
+349.516 364.586 350.68 365.812 351.508 367.586 curveto
+352.504 369.719 352.492 371.711 352.359 373.551 curveto
+352.711 373.344 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath fill
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 353.43 362.887 349.023 362.887 curveto
+348.492 362.887 347.914 363.043 347.656 363.391 curveto
+349.516 364.586 350.68 365.812 351.508 367.586 curveto
+352.504 369.719 352.492 371.711 352.359 373.551 curveto
+352.711 373.344 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath stroke
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath fill
+ 0 0.8128 dtransform truncate idtransform setlinewidth pop 0 setlinejoin
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath stroke
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath fill
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath stroke
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath fill
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath fill
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath stroke
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath stroke
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath fill
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath fill
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath stroke
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath stroke
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath fill
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath stroke
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 365.262 363.73 360.859 363.73 curveto
+360.32 363.73 359.746 363.883 359.488 364.234 curveto
+361.348 365.426 362.512 366.648 363.344 368.426 curveto
+364.336 370.559 364.32 372.551 364.191 374.387 curveto
+364.543 374.184 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath fill
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 365.262 363.73 360.859 363.73 curveto
+360.32 363.73 359.746 363.883 359.488 364.234 curveto
+361.348 365.426 362.512 366.648 363.344 368.426 curveto
+364.336 370.559 364.32 372.551 364.191 374.387 curveto
+364.543 374.184 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath stroke
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath fill
+ 0 9.7534 dtransform truncate idtransform setlinewidth pop 1 setlinejoin
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath stroke
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath fill
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath stroke
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath fill
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath fill
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath stroke
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath stroke
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath fill
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath fill
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath stroke
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath stroke
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath fill
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath stroke
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 365.262 363.73 360.859 363.73 curveto
+360.32 363.73 359.746 363.883 359.488 364.234 curveto
+361.348 365.426 362.512 366.648 363.344 368.426 curveto
+364.336 370.559 364.32 372.551 364.191 374.387 curveto
+364.543 374.184 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath fill
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+370.301 367.719 365.262 363.73 360.859 363.73 curveto
+360.32 363.73 359.746 363.883 359.488 364.234 curveto
+361.348 365.426 362.512 366.648 363.344 368.426 curveto
+364.336 370.559 364.32 372.551 364.191 374.387 curveto
+364.543 374.184 365.672 373.664 366.141 373.664 curveto
+368.047 373.664 369.684 375.523 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath stroke
+ 1 setgray
+newpath 261.176 428.469 moveto
+266.969 428.469 263.594 413.848 261.531 409.438 curveto
+259.77 405.664 255.797 400.219 251.086 400.219 curveto
+245.441 400.219 245.609 406.605 247.855 411.418 curveto
+249.074 414.02 250.945 416.941 253.52 419.086 curveto
+252.863 416.848 255.094 415.316 254.191 413.387 curveto
+253.574 412.066 252.301 410.629 251.637 409.211 curveto
+250.566 406.918 250.973 402.852 253.562 402.852 curveto
+255.879 402.852 257.938 405.75 258.785 407.555 curveto
+260.512 411.254 264.836 427.043 260.004 427.043 curveto
+254.438 427.043 244.844 413.344 242.836 409.047 curveto
+239.25 401.375 241.852 391.93 250.02 391.93 curveto
+255.539 391.93 260.078 397.289 262.27 401.98 curveto
+263.578 404.773 264.289 407.555 264.57 410.176 curveto
+265.641 410.375 lineto
+265.879 403.156 273.438 391.207 270.219 384.32 curveto
+266.102 375.508 253.785 375.586 246.754 372.461 curveto
+245.273 375.953 243.273 379.199 240.453 381.68 curveto
+246.727 383.809 262.137 387.387 265.492 394.559 curveto
+266.898 397.57 265.941 402.566 264.312 404.258 curveto
+264.035 403.195 263.652 402.109 263.145 401.02 curveto
+260.273 394.883 254.129 390.121 247.793 390.121 curveto
+238.977 390.121 235.238 396.52 239.387 405.398 curveto
+241.879 410.73 254.598 428.469 261.176 428.469 curveto
+ closepath fill
+newpath 276.242 407.305 moveto
+276.594 406.434 277.609 401.512 278.379 401.512 curveto
+280.039 401.512 283.039 406.078 283.848 407.262 curveto
+285.324 406.285 284.504 404.234 287.242 404.234 curveto
+288.48 404.234 289.105 406.93 290.395 406.973 curveto
+290.441 405.883 290.508 404.898 289.922 403.641 curveto
+289.012 401.699 286.719 399.066 284.461 399.066 curveto
+282.844 399.066 280.594 401.031 280.152 401.031 curveto
+279.672 401.031 278.789 400.805 278.508 400.203 curveto
+277.332 397.691 280.477 389.297 283.625 389.297 curveto
+285.309 389.297 287.316 390.543 289.078 391.312 curveto
+288.621 388.289 288.559 385.738 289.164 383.133 curveto
+287.559 382.109 285.68 381.676 284.172 381.676 curveto
+276.43 381.676 280.699 399.859 273.559 403.734 curveto
+276.242 407.305 lineto
+ closepath fill
+newpath 306 408.762 moveto
+311.852 408.762 314.16 398.133 311.535 392.516 curveto
+309.5 388.16 305.887 385 301.262 385 curveto
+293.09 385 294.113 396.246 297.301 403.066 curveto
+298.18 404.945 301.184 409.039 303.531 409.039 curveto
+304.395 409.039 305.113 408.762 306 408.762 curveto
+ closepath fill
+newpath 305.816 407.77 moveto
+304.898 407.77 304.086 407.309 303.66 406.395 curveto
+302.762 404.477 307.234 402.535 306.594 401.164 curveto
+306.188 400.301 304.652 399.492 303.875 399.492 curveto
+299.996 399.492 300.031 404.586 302.34 407.906 curveto
+300.906 406.98 299.961 405.57 299.352 404.258 curveto
+297.078 399.398 298.883 392.711 304.066 392.711 curveto
+306.934 392.711 310.23 395.312 311.512 398.055 curveto
+313.094 401.43 309.059 407.77 305.816 407.77 curveto
+ closepath fill
+newpath 332.59 391.262 moveto
+331.062 387.992 328.211 386.195 325.004 386.195 curveto
+314.824 386.195 312.805 395.57 317.215 405.012 curveto
+318.258 407.238 319.969 409.703 322.578 409.703 curveto
+330.758 409.703 336.453 399.531 332.59 391.262 curveto
+ closepath fill
+newpath 324.469 408.52 moveto
+323.715 408.52 323.059 408.246 322.695 407.477 curveto
+321.906 405.781 326.211 404.27 326.219 402.516 curveto
+325.77 401.98 324.508 401.379 323.961 401.379 curveto
+320.859 401.379 318.516 405.625 321.164 408.781 curveto
+320.09 408.094 319.449 407.062 319 406.09 curveto
+316.312 400.344 320.141 394.535 325.719 394.535 curveto
+328.043 394.535 331.035 395.84 332.242 398.422 curveto
+333.949 402.074 327.867 408.52 324.469 408.52 curveto
+ closepath fill
+newpath 343.121 408.574 moveto
+351.891 408.574 356.352 392.316 352.43 383.93 curveto
+351.68 382.324 350.285 380.723 348.441 380.723 curveto
+338.418 380.723 340.977 400.848 335.332 406.562 curveto
+335.852 407.117 336.344 407.695 336.797 408.277 curveto
+340.609 403.758 338.715 391.867 346.859 391.867 curveto
+348.734 391.867 350.156 393.523 350.91 395.145 curveto
+352.934 399.473 349.191 406.984 345.098 406.984 curveto
+345.055 406.984 344.465 406.961 344.48 406.719 curveto
+344.629 403.957 346.781 405.68 344.617 400.98 curveto
+342.402 401.426 340.059 405.016 341.266 407.605 curveto
+341.625 408.363 342.516 408.574 343.121 408.574 curveto
+ closepath fill
+newpath 364.938 408.758 moveto
+365.762 408.68 367.004 408.754 367.711 408.535 curveto
+367.848 408.496 367.746 408.328 367.766 408.195 curveto
+367.984 406.965 368.32 405.801 368.617 404.613 curveto
+369.758 399.988 370.445 395.238 371.758 390.652 curveto
+373.184 385.66 374.672 380.035 372.945 373.77 curveto
+372.777 373.156 372.551 372.535 372.258 371.91 curveto
+365.691 359.168 351.629 362.602 347.227 362.602 curveto
+346.691 362.602 346.113 362.754 345.859 363.105 curveto
+347.715 364.301 348.879 365.523 349.711 367.301 curveto
+350.703 369.43 350.691 371.426 350.559 373.262 curveto
+350.914 373.055 352.043 372.539 352.508 372.539 curveto
+354.414 372.539 365.035 367.027 370.445 377.16 curveto
+371.852 380.164 371.613 383.23 371.367 385.789 curveto
+371.246 387.066 371.074 388.328 370.84 389.559 curveto
+370.48 391.332 lineto
+370.438 391.531 lineto
+370.391 391.723 lineto
+370.605 391.723 370.414 391.348 370.422 391.156 curveto
+370.555 388.059 370.5 383.738 368.852 380.211 curveto
+368.086 378.574 366.832 376.887 364.91 376.887 curveto
+356.488 376.887 360.723 401.016 353.973 406.75 curveto
+354.348 407.375 354.68 408.016 354.961 408.645 curveto
+358.953 403.773 359.164 390.34 365.047 390.34 curveto
+366.473 390.34 367.945 391.691 368.555 392.988 curveto
+371.168 398.578 366.824 405.086 364.938 408.758 curveto
+ closepath fill
+showpage
+%%EOF
diff --git a/groovy/src/latex/spec/colophon.tex b/groovy/src/latex/spec/colophon.tex
new file mode 100644
index 0000000..4abd051
--- /dev/null
+++ b/groovy/src/latex/spec/colophon.tex
@@ -0,0 +1,11 @@
+\chapter{Colophon}
+
+This manual was produced without the aid of Microsoft products.
+It was authored on a Linux laptop using XEmacs\index{XEmacs}, \LaTeX\index{Latex@\LaTeX},
+\texttt{makeindex}, \texttt{xdvi}, \texttt{dia}, and \texttt{epstopdf}.
+
+\rule{0pt}{17em}
+
+\begin{flushright}
+Phish\index{Phish|textbf} rocks.
+\end{flushright}
diff --git a/groovy/src/latex/spec/expressions.tex b/groovy/src/latex/spec/expressions.tex
new file mode 100644
index 0000000..f212c2e
--- /dev/null
+++ b/groovy/src/latex/spec/expressions.tex
@@ -0,0 +1,2 @@
+\chapter{Expressions}
+
diff --git a/groovy/src/latex/spec/groovy-lang-spec.tex b/groovy/src/latex/spec/groovy-lang-spec.tex
new file mode 100644
index 0000000..d0a4f2f
--- /dev/null
+++ b/groovy/src/latex/spec/groovy-lang-spec.tex
@@ -0,0 +1,162 @@
+
+%%
+%%
+%% the groovy language spec
+%%
+%%
+
+\documentclass[10pt,letterpaper]{book}
+
+
+%%
+%% Package Imports
+%%
+
+\usepackage{alltt}
+%\usepackage{cite}
+%\usepackage{plain}
+\usepackage{graphicx}
+\usepackage{hyperref}
+\usepackage{varioref}
+\usepackage{tabularx}
+%\usepackage{array}
+%\usepackage{color}
+\usepackage{makeidx}
+%\usepackage{showidx}
+
+%%
+%% Extra Commands and Evironments
+%%
+
+\newenvironment{codelisting}%
+	{\bigskip\begin{minipage}{250pt}\footnotesize\begin{alltt}}%
+	{\end{alltt}\end{minipage}\bigskip}
+
+\newenvironment{basenote}[1]
+	{
+		\begin{flushright}
+		\vline \hspace{1em}
+		\begin{minipage}{0.7\linewidth}
+		\hspace{-1em}\textsf{\textbf{#1}}\\  
+		\rule{0em}{1.4em}
+		\sl
+	} {
+		\end{minipage}
+		\end{flushright}
+	}
+
+\newenvironment{indnote}[1]
+	{
+		\begin{flushright}
+		\vline \hspace{1em}
+		\begin{minipage}{0.9\linewidth}
+		\hspace{-1em}\textsf{\textbf{#1's note}}\\  
+		\rule{0em}{1.4em}
+		\sl
+	} {
+		\end{minipage}
+		\end{flushright}
+	}
+
+\newenvironment{note}
+	{
+		\begin{basenote}{Note}
+	} {
+		\end{basenote}
+	}
+
+\newenvironment{ednote}
+	{
+		\begin{basenote}{Editor's Note}
+	} {
+		\end{basenote}
+	}
+
+\newenvironment{implnote}
+	{
+		\begin{basenote}{Implementor's Note}
+	} {
+		\end{basenote}
+	}
+
+\newcommand{\diagram}[2]{
+  \bigskip
+  \begin{figure}[h]
+    \begin{center}
+      \includegraphics[scale=0.5]{#1}
+    \end{center}
+    \caption{#2}
+  \end{figure} 
+  \medskip
+}
+
+\newcommand{\indexfile}[1]{\texttt{#1}\index{#1@\texttt{#1} file}}
+\newcommand{\indexclass}[1]{\texttt{#1}\index{#1@\texttt{#1} class}}
+\newcommand{\indexclassex}[1]{\index{#1@\texttt{#1}!example}}
+\newcommand{\indexmethod}[2]{\texttt{#2}\index{#1@\texttt{#1} class!#2@\texttt{#2} method}}
+
+\newcommand{\indexkeyword}[1]{\texttt{#1}\index{#1@\texttt{#1} keyword}}
+\newcommand{\indexoperator}[1]{\texttt{#1}\index{#1@\texttt{#1} operator}}
+
+\newcommand{\file}[1]{\texttt{#1} file}
+\newcommand{\class}[1]{\texttt{#1}}
+\newcommand{\method}[1]{\texttt{#1}}
+
+\raggedbottom
+
+\clubpenalty = 10000
+\widowpenalty = 10000 
+\makeindex
+
+\begin{document}
+
+\let\footnoterule\hrule
+
+\makeatletter
+\renewcommand{\@makefntext}[1]%
+	{\noindent\makebox[1.8em][r]{\@makefnmark}#1}
+\makeatother
+
+\title{
+	Groovy Language Specification
+}
+
+\author{
+	\textsf{The Codehaus}\\
+	\textsf{www.codehaus.org}\\
+}
+
+
+\frontmatter
+
+\maketitle
+
+\tableofcontents
+
+\listoffigures 
+
+\include{preface}
+
+\mainmatter
+
+\include{keywords}
+\include{operators}
+\include{expressions}
+\include{statements}
+
+\index{Betty|see{betty}}
+\index{betty|see{Betty}}
+
+\appendix
+
+\include{faq}
+\include{license}
+
+\backmatter
+
+\include{colophon}
+
+\small
+\printindex
+
+\end{document}
diff --git a/groovy/src/latex/spec/keywords.tex b/groovy/src/latex/spec/keywords.tex
new file mode 100644
index 0000000..7922452
--- /dev/null
+++ b/groovy/src/latex/spec/keywords.tex
@@ -0,0 +1,3 @@
+\chapter{Keywords}
+
+\section{\indexkeyword{class}}
diff --git a/groovy/src/latex/spec/operators.tex b/groovy/src/latex/spec/operators.tex
new file mode 100644
index 0000000..0eff220
--- /dev/null
+++ b/groovy/src/latex/spec/operators.tex
@@ -0,0 +1,7 @@
+\chapter{Operators}
+
+\section{\indexoperator{.} -- property access}
+\section{\indexoperator{[]} -- collection member access}
+\section{\indexoperator{()} -- method invocation}
+\section{\indexoperator{==} -- identity equality testing}
+\section{\indexoperator{!=} -- identity equality testing}
diff --git a/groovy/src/latex/spec/preface.tex b/groovy/src/latex/spec/preface.tex
new file mode 100644
index 0000000..0f5d1c1
--- /dev/null
+++ b/groovy/src/latex/spec/preface.tex
@@ -0,0 +1,3 @@
+\chapter{Preface}
+
+Yadda foo.
diff --git a/groovy/src/latex/spec/statements.tex b/groovy/src/latex/spec/statements.tex
new file mode 100644
index 0000000..69fac31
--- /dev/null
+++ b/groovy/src/latex/spec/statements.tex
@@ -0,0 +1,2 @@
+\chapter{Statements}
+
diff --git a/groovy/src/main/groovy/inspect/Inspector.java b/groovy/src/main/groovy/inspect/Inspector.java
new file mode 100644
index 0000000..bf382fd
--- /dev/null
+++ b/groovy/src/main/groovy/inspect/Inspector.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.inspect;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.MetaClass;
+import groovy.lang.MetaMethod;
+import groovy.lang.PropertyValue;
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/**
+ * The Inspector provides a unified access to an object's
+ * information that can be determined by introspection.
+ *
+ * @author Dierk Koenig
+ */
+public class Inspector {
+    protected Object objectUnderInspection;
+
+    // Indexes to retrieve Class Property information
+    public static final int CLASS_PACKAGE_IDX       = 0;
+    public static final int CLASS_CLASS_IDX         = 1;
+    public static final int CLASS_INTERFACE_IDX     = 2;
+    public static final int CLASS_SUPERCLASS_IDX    = 3;
+    public static final int CLASS_OTHER_IDX         = 4;
+
+    // Indexes to retrieve field and method information
+    public static final int MEMBER_ORIGIN_IDX = 0;
+    public static final int MEMBER_MODIFIER_IDX = 1;
+    public static final int MEMBER_DECLARER_IDX = 2;
+    public static final int MEMBER_TYPE_IDX = 3;
+    public static final int MEMBER_NAME_IDX = 4;
+    public static final int MEMBER_PARAMS_IDX = 5;
+    public static final int MEMBER_VALUE_IDX = 5;
+    public static final int MEMBER_EXCEPTIONS_IDX = 6;
+
+    public static final String NOT_APPLICABLE = "n/a";
+    public static final String GROOVY = "GROOVY";
+    public static final String JAVA = "JAVA";
+
+    /**
+     * @param objectUnderInspection must not be null
+     */
+    public Inspector(Object objectUnderInspection) {
+        if (null == objectUnderInspection){
+            throw new IllegalArgumentException("argument must not be null");
+        }
+        this.objectUnderInspection = objectUnderInspection;
+    }
+
+    /**
+     * Get the Class Properties of the object under inspection.
+     * @return String array to be indexed by the CLASS_xxx_IDX constants
+     */
+    public String[] getClassProps() {
+        String[] result = new String[CLASS_OTHER_IDX+1];
+        Package pack = getClassUnderInspection().getPackage();
+        result[CLASS_PACKAGE_IDX] = "package "+ ((pack == null) ? NOT_APPLICABLE : pack.getName());
+        String modifiers = Modifier.toString(getClassUnderInspection().getModifiers());
+        result[CLASS_CLASS_IDX] = modifiers + " class "+ shortName(getClassUnderInspection());
+        result[CLASS_INTERFACE_IDX] = "implements ";
+        Class[] interfaces = getClassUnderInspection().getInterfaces();
+        for (int i = 0; i < interfaces.length; i++) {
+            result[CLASS_INTERFACE_IDX] += shortName(interfaces[i])+ " ";
+        }
+        result[CLASS_SUPERCLASS_IDX] = "extends " + shortName(getClassUnderInspection().getSuperclass());
+        result[CLASS_OTHER_IDX] = "is Primitive: "+getClassUnderInspection().isPrimitive()
+                  +", is Array: "   +getClassUnderInspection().isArray()
+                  +", is Groovy: "  + isGroovy();
+        return result;
+    }
+
+    public boolean isGroovy() {
+        return GroovyObject.class.isAssignableFrom(getClassUnderInspection());
+    }
+
+    /**
+     * Gets the object being inspected.
+     */
+    public Object getObject() {
+    	return objectUnderInspection;
+    }
+
+    /**
+     * Get info about usual Java instance and class Methods as well as Constructors.
+     * @return  Array of StringArrays that can be indexed with the MEMBER_xxx_IDX constants
+     */
+    public Object[] getMethods(){
+        Method[] methods = getClassUnderInspection().getMethods();
+        Constructor[] ctors = getClassUnderInspection().getConstructors();
+        Object[] result = new Object[methods.length + ctors.length];
+        int resultIndex = 0;
+        for (; resultIndex < methods.length; resultIndex++) {
+            Method method = methods[resultIndex];
+            result[resultIndex] = methodInfo(method);
+        }
+        for (int i = 0; i < ctors.length; i++, resultIndex++) {
+            Constructor ctor = ctors[i];
+            result[resultIndex] = methodInfo(ctor);
+        }
+        return result;
+    }
+     /**
+     * Get info about instance and class Methods that are dynamically added through Groovy.
+     * @return  Array of StringArrays that can be indexed with the MEMBER_xxx_IDX constants
+     */
+    public Object[] getMetaMethods(){
+        MetaClass metaClass = InvokerHelper.getMetaClass(objectUnderInspection);
+        List metaMethods = metaClass.getMetaMethods();
+        Object[] result = new Object[metaMethods.size()];
+        int i=0;
+        for (Iterator iter = metaMethods.iterator(); iter.hasNext(); i++) {
+            MetaMethod metaMethod = (MetaMethod) iter.next();
+            result[i] = methodInfo(metaMethod);
+        }
+        return result;
+    }
+
+    /**
+     * Get info about usual Java public fields incl. constants.
+     * @return  Array of StringArrays that can be indexed with the MEMBER_xxx_IDX constants
+     */
+    public Object[] getPublicFields(){
+        Field[] fields = getClassUnderInspection().getFields();
+        Object[] result = new Object[fields.length];
+        for (int i = 0; i < fields.length; i++) {
+            Field field = fields[i];
+            result[i] = fieldInfo(field);
+        }
+        return result;
+    }
+    /**
+     * Get info about Properties (Java and Groovy alike).
+     * @return  Array of StringArrays that can be indexed with the MEMBER_xxx_IDX constants
+     */
+    public Object[] getPropertyInfo(){
+        List props = DefaultGroovyMethods.getMetaPropertyValues(objectUnderInspection);
+        Object[] result = new Object[props.size()];
+        int i=0;
+        for (Iterator iter = props.iterator(); iter.hasNext(); i++) {
+            PropertyValue pv = (PropertyValue) iter.next();
+            result[i] = fieldInfo(pv);
+        }
+        return result;
+    }
+
+    protected String[] fieldInfo(Field field) {
+        String[] result = new String[MEMBER_VALUE_IDX+1];
+        result[MEMBER_ORIGIN_IDX] = JAVA;
+        result[MEMBER_MODIFIER_IDX] = Modifier.toString(field.getModifiers());
+        result[MEMBER_DECLARER_IDX] = shortName(field.getDeclaringClass());
+        result[MEMBER_TYPE_IDX] = shortName(field.getType());
+        result[MEMBER_NAME_IDX] = field.getName();
+        try {
+            result[MEMBER_VALUE_IDX] = InvokerHelper.inspect(field.get(objectUnderInspection));
+        } catch (IllegalAccessException e) {
+            result[MEMBER_VALUE_IDX] = NOT_APPLICABLE;
+        }
+        return withoutNulls(result);
+    }
+    protected String[] fieldInfo(PropertyValue pv) {
+        String[] result = new String[MEMBER_VALUE_IDX+1];
+        result[MEMBER_ORIGIN_IDX] = GROOVY;
+        result[MEMBER_MODIFIER_IDX] = "public";
+        result[MEMBER_DECLARER_IDX] = NOT_APPLICABLE;
+        result[MEMBER_TYPE_IDX] = shortName(pv.getType());
+        result[MEMBER_NAME_IDX] = pv.getName();
+        try {
+            result[MEMBER_VALUE_IDX] = InvokerHelper.inspect(pv.getValue());
+        } catch (Exception e) {
+            result[MEMBER_VALUE_IDX] = NOT_APPLICABLE;
+        }
+        return withoutNulls(result);
+    }
+
+    protected Class getClassUnderInspection() {
+        return objectUnderInspection.getClass();
+    }
+
+    public static String shortName(Class clazz){
+        if (null == clazz) return NOT_APPLICABLE;
+        String className = clazz.getName();
+        if (null == clazz.getPackage()) return className;
+        String packageName = clazz.getPackage().getName();
+        int offset = packageName.length();
+        if (offset > 0) offset++;
+        className = className.substring(offset);
+        return className;
+    }
+
+    protected String[] methodInfo(Method method){
+        String[] result = new String[MEMBER_EXCEPTIONS_IDX+1];
+	    int mod = method.getModifiers();
+        result[MEMBER_ORIGIN_IDX] = JAVA;
+        result[MEMBER_DECLARER_IDX] = shortName(method.getDeclaringClass());
+        result[MEMBER_MODIFIER_IDX] = Modifier.toString(mod);
+        result[MEMBER_NAME_IDX] = method.getName();
+        result[MEMBER_TYPE_IDX] = shortName(method.getReturnType());
+	    Class[] params = method.getParameterTypes();
+        StringBuffer sb = new StringBuffer();
+	    for (int j = 0; j < params.length; j++) {
+		    sb.append(shortName(params[j]));
+		    if (j < (params.length - 1)) sb.append(", ");
+	    }
+        result[MEMBER_PARAMS_IDX] = sb.toString();
+	    sb.setLength(0);
+	    Class[] exceptions = method.getExceptionTypes();
+		for (int k = 0; k < exceptions.length; k++) {
+		    sb.append(shortName(exceptions[k]));
+		    if (k < (exceptions.length - 1)) sb.append(", ");
+	    }
+        result[MEMBER_EXCEPTIONS_IDX] = sb.toString();
+	    return withoutNulls(result);
+    }
+
+    protected String[] methodInfo(Constructor ctor){
+        String[] result = new String[MEMBER_EXCEPTIONS_IDX+1];
+	    int mod = ctor.getModifiers();
+        result[MEMBER_ORIGIN_IDX] = JAVA;
+        result[MEMBER_MODIFIER_IDX] = Modifier.toString(mod);
+        result[MEMBER_DECLARER_IDX] = shortName(ctor.getDeclaringClass());
+        result[MEMBER_TYPE_IDX] = shortName(ctor.getDeclaringClass());
+        result[MEMBER_NAME_IDX] = ctor.getName();
+	    Class[] params = ctor.getParameterTypes();
+        StringBuffer sb = new StringBuffer();
+	    for (int j = 0; j < params.length; j++) {
+		    sb.append(shortName(params[j]));
+		    if (j < (params.length - 1)) sb.append(", ");
+	    }
+        result[MEMBER_PARAMS_IDX] = sb.toString();
+	    sb.setLength(0);
+	    Class[] exceptions = ctor.getExceptionTypes();
+		for (int k = 0; k < exceptions.length; k++) {
+		    sb.append(shortName(exceptions[k]));
+		    if (k < (exceptions.length - 1)) sb.append(", ");
+	    }
+        result[MEMBER_EXCEPTIONS_IDX] = sb.toString();
+	    return withoutNulls(result);
+    }
+    protected String[] methodInfo(MetaMethod method){
+        String[] result = new String[MEMBER_EXCEPTIONS_IDX+1];
+	    int mod = method.getModifiers();
+        result[MEMBER_ORIGIN_IDX] = GROOVY;
+        result[MEMBER_MODIFIER_IDX] = Modifier.toString(mod);
+        result[MEMBER_DECLARER_IDX] = shortName(method.getDeclaringClass().getCachedClass());
+        result[MEMBER_TYPE_IDX] = shortName(method.getReturnType());
+        result[MEMBER_NAME_IDX] = method.getName();
+	    CachedClass[] params = method.getParameterTypes();
+        StringBuffer sb = new StringBuffer();
+	    for (int j = 0; j < params.length; j++) {
+            sb.append(shortName(params[j].getCachedClass()));
+		    if (j < (params.length - 1)) sb.append(", ");
+	    }
+        result[MEMBER_PARAMS_IDX] = sb.toString();
+        result[MEMBER_EXCEPTIONS_IDX] = NOT_APPLICABLE; // no exception info for Groovy MetaMethods
+        return withoutNulls(result);
+    }
+
+    protected String[] withoutNulls(String[] toNormalize){
+        for (int i = 0; i < toNormalize.length; i++) {
+            String s = toNormalize[i];
+            if (null == s) toNormalize[i] = NOT_APPLICABLE;
+        }
+        return toNormalize;
+    }
+
+    public static void print(Object[] memberInfo) {
+        print(System.out, memberInfo);
+    }
+
+    static void print(final PrintStream out, Object[] memberInfo) {
+        for (int i = 0; i < memberInfo.length; i++) {
+            String[] metaMethod = (String[]) memberInfo[i];
+            out.print(i+":\t");
+            for (int j = 0; j < metaMethod.length; j++) {
+                String s = metaMethod[j];
+                out.print(s+" ");
+            }
+            out.println("");
+        }
+    }
+
+    public static Collection sort(List memberInfo) {
+        Collections.sort(memberInfo, new MemberComparator());
+        return memberInfo;
+    }
+
+    public static class MemberComparator implements Comparator {
+        public int compare(Object a, Object b) {
+            String[] aStr = (String[]) a;
+            String[] bStr = (String[]) b;
+            int result = aStr[Inspector.MEMBER_NAME_IDX].compareTo(bStr[Inspector.MEMBER_NAME_IDX]);
+            if (0 != result) return result;
+            result = aStr[Inspector.MEMBER_TYPE_IDX].compareTo(bStr[Inspector.MEMBER_TYPE_IDX]);
+            if (0 != result) return result;
+            result = aStr[Inspector.MEMBER_PARAMS_IDX].compareTo(bStr[Inspector.MEMBER_PARAMS_IDX]);
+            if (0 != result) return result;
+            result = aStr[Inspector.MEMBER_DECLARER_IDX].compareTo(bStr[Inspector.MEMBER_DECLARER_IDX]);
+            if (0 != result) return result;
+            result = aStr[Inspector.MEMBER_MODIFIER_IDX].compareTo(bStr[Inspector.MEMBER_MODIFIER_IDX]);
+            if (0 != result) return result;
+            result = aStr[Inspector.MEMBER_ORIGIN_IDX].compareTo(bStr[Inspector.MEMBER_ORIGIN_IDX]);
+            return result;
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/inspect/package.html b/groovy/src/main/groovy/inspect/package.html
new file mode 100644
index 0000000..f11744b
--- /dev/null
+++ b/groovy/src/main/groovy/inspect/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.inspect.*</title>
+  </head>
+  <body>
+    <p>Classes for inspecting object properties through introspection.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/inspect/swingui/ObjectBrowser.groovy b/groovy/src/main/groovy/inspect/swingui/ObjectBrowser.groovy
new file mode 100644
index 0000000..c14905d
--- /dev/null
+++ b/groovy/src/main/groovy/inspect/swingui/ObjectBrowser.groovy
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.inspect.swingui
+
+import java.awt.*
+import javax.swing.*
+import groovy.swing.SwingBuilder
+import groovy.inspect.Inspector
+
+/**
+A little GUI to show some of the Inspector capabilities.
+Starting this script opens the ObjectBrowser on "some String".
+Use it in groovysh or groovyConsole to inspect your object of
+interest with
+<code>
+ObjectBrowser.inspect(myObject)
+</code>.
+
+@author Dierk Koenig
+**/
+class ObjectBrowser {
+    
+    def inspector
+    def swing, frame, fieldTable, methodTable, itemTable, mapTable
+
+    static void main(args) {
+        inspect("some String")
+    }
+    static void inspect(objectUnderInspection){
+        def browser = new ObjectBrowser()
+        browser.inspector = new Inspector(objectUnderInspection)
+        browser.run()
+    }
+    
+    void run() {
+        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
+        swing = new SwingBuilder()
+        
+        frame = swing.frame(title:'Groovy Object Browser', location:[200,200],
+                size:[800,600], pack: true, show:true,
+                iconImage: swing.imageIcon(groovy.ui.Console.ICON_PATH).image,
+                defaultCloseOperation:WindowConstants.DISPOSE_ON_CLOSE) {
+                
+            menuBar {
+                menu(text:'Help') {
+                    menuItem() {action(name:'About', closure: this.&showAbout)}
+                }
+            }
+            panel() {
+                borderLayout()
+                panel(  name:"Class Info",
+                        border: emptyBorder([5,10,5,10]),
+                        constraints:BorderLayout.NORTH) {
+                    flowLayout(alignment:FlowLayout.LEFT)
+                    def props = inspector.classProps
+                    def classLabel = '<html>' + props.join('<br>')
+                    label(classLabel)
+                }
+                tabbedPane(constraints:BorderLayout.CENTER){
+                    if (inspector.object instanceof Collection) {
+	                    scrollPane(name: ' Collection data ') {
+	                        itemTable = table() {
+	                        	int i = 0
+	                            def data = inspector.object.collect { val -> [i++, val] }
+	                            tableModel(list:data) {
+	                                closureColumn(header:'Index', read:{it[0]})
+	                                closureColumn(header:'Value', read:{it[1]})
+	                            }
+	                        }
+	                    }
+                    }
+                    if (inspector.object instanceof Map) {
+	                    scrollPane(name: ' Map data ') {
+	                        itemTable = table() {
+	                        	int i = 0
+	                            def data = inspector.object.collect { key, val -> [i++, key, val] }
+	                            tableModel(list:data) {
+	                                closureColumn(header:'Index', read:{it[0]})
+	                                closureColumn(header:'Key',   read:{it[1]})
+	                                closureColumn(header:'Value', read:{it[2]})
+	                            }
+	                        }
+	                    }
+                    }
+                    scrollPane(name: ' Public Fields and Properties ') {
+                        fieldTable = table() {
+                            def data = Inspector.sort(inspector.publicFields.toList())
+                            data.addAll(Inspector.sort(inspector.propertyInfo.toList()))
+                            tableModel(list:data) {
+                                closureColumn(header:'Name',        read:{it[Inspector.MEMBER_NAME_IDX]})
+                                closureColumn(header:'Value',       read:{it[Inspector.MEMBER_VALUE_IDX]})
+                                closureColumn(header:'Type',        read:{it[Inspector.MEMBER_TYPE_IDX]})
+                                closureColumn(header:'Origin',      read:{it[Inspector.MEMBER_ORIGIN_IDX]})
+                                closureColumn(header:'Modifier',    read:{it[Inspector.MEMBER_MODIFIER_IDX]})
+                                closureColumn(header:'Declarer',    read:{it[Inspector.MEMBER_DECLARER_IDX]})
+                            }
+                        }
+                    }
+                    scrollPane(name:' (Meta) Methods ' ) {
+                        methodTable = table() {
+                            def data = Inspector.sort(inspector.methods.toList())
+                            data.addAll(Inspector.sort(inspector.metaMethods.toList()))
+
+                            tableModel(list:data) {
+                                closureColumn(header:'Name',        read:{it[Inspector.MEMBER_NAME_IDX]})
+                                closureColumn(header:'Params',      read:{it[Inspector.MEMBER_PARAMS_IDX]})
+                                closureColumn(header:'Type',        read:{it[Inspector.MEMBER_TYPE_IDX]})
+                                closureColumn(header:'Origin',      read:{it[Inspector.MEMBER_ORIGIN_IDX]})
+                                closureColumn(header:'Modifier',    read:{it[Inspector.MEMBER_MODIFIER_IDX]})
+                                closureColumn(header:'Declarer',    read:{it[Inspector.MEMBER_DECLARER_IDX]})
+                                closureColumn(header:'Exceptions',  read:{it[Inspector.MEMBER_EXCEPTIONS_IDX]})
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        
+        // Add a bit of formatting
+        addSorter(itemTable)
+        addSorter(mapTable)
+        addSorter(fieldTable)
+        addSorter(methodTable)
+        
+        frame.toFront()
+    }
+    
+    void addSorter(table) {
+    	if (table != null) {
+	    	def sorter = new TableSorter(table.model)
+    		table.model = sorter
+    		sorter.addMouseListenerToHeaderInTable(table)
+    	}
+    }
+    
+    void showAbout(EventObject evt) {
+         def pane = swing.optionPane()
+         // work around GROOVY-1048
+         pane.setMessage('An interactive GUI to explore object capabilities.')
+         def dialog = pane.createDialog(frame, 'About Groovy Object Browser')
+         dialog.show()
+    }
+}
diff --git a/groovy/src/main/groovy/inspect/swingui/TableMap.java b/groovy/src/main/groovy/inspect/swingui/TableMap.java
new file mode 100644
index 0000000..4c84a4a
--- /dev/null
+++ b/groovy/src/main/groovy/inspect/swingui/TableMap.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package groovy.inspect.swingui;
+
+/**
+ * In a chain of data manipulators some behaviour is common. TableMap
+ * provides most of this behavour and can be subclassed by filters
+ * that only need to override a handful of specific methods. TableMap
+ * implements TableModel by routing all requests to its model, and
+ * TableModelListener by routing all events to its listeners. Inserting
+ * a TableMap which has not been subclassed into a chain of table filters
+ * should have no effect.
+ *
+ * @version 1.11 01/23/03
+ * @author Philip Milne */
+
+import javax.swing.table.*;
+import javax.swing.event.TableModelListener;
+import javax.swing.event.TableModelEvent;
+
+public class TableMap extends AbstractTableModel implements TableModelListener
+{
+    protected TableModel model;
+
+    public TableModel  getModel() {
+        return model;
+    }
+
+    public void  setModel(TableModel model) {
+        this.model = model;
+        model.addTableModelListener(this);
+    }
+
+    // By default, Implement TableModel by forwarding all messages
+    // to the model.
+
+    public Object getValueAt(int aRow, int aColumn) {
+        return model.getValueAt(aRow, aColumn);
+    }
+
+    public void setValueAt(Object aValue, int aRow, int aColumn) {
+        model.setValueAt(aValue, aRow, aColumn);
+    }
+
+    public int getRowCount() {
+        return (model == null) ? 0 : model.getRowCount();
+    }
+
+    public int getColumnCount() {
+        return (model == null) ? 0 : model.getColumnCount();
+    }
+
+    public String getColumnName(int aColumn) {
+        return model.getColumnName(aColumn);
+    }
+
+    public Class getColumnClass(int aColumn) {
+        return model.getColumnClass(aColumn);
+    }
+
+    public boolean isCellEditable(int row, int column) {
+         return model.isCellEditable(row, column);
+    }
+//
+// Implementation of the TableModelListener interface,
+//
+
+    // By default forward all events to all the listeners.
+    public void tableChanged(TableModelEvent e) {
+        fireTableChanged(e);
+    }
+}
+
diff --git a/groovy/src/main/groovy/inspect/swingui/TableSorter.java b/groovy/src/main/groovy/inspect/swingui/TableSorter.java
new file mode 100644
index 0000000..9e4f127
--- /dev/null
+++ b/groovy/src/main/groovy/inspect/swingui/TableSorter.java
@@ -0,0 +1,331 @@
+/*
+ * $Id$
+ *
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.inspect.swingui;
+
+/**
+ * A sorter for TableModels. The sorter has a model (conforming to TableModel)
+ * and itself implements TableModel. TableSorter does not store or copy
+ * the data in the TableModel, instead it maintains an array of
+ * integers which it keeps the same size as the number of rows in its
+ * model. When the model changes it notifies the sorter that something
+ * has changed eg. "rowsAdded" so that its internal array of integers
+ * can be reallocated. As requests are made of the sorter (like
+ * getValueAt(row, col) it redirects them to its model via the mapping
+ * array. That way the TableSorter appears to hold another copy of the table
+ * with the rows in a different order. The sorting algorthm used is stable
+ * which means that it does not move around rows when its comparison
+ * function returns 0 to denote that they are equivalent.
+ *
+ * @version 1.12 01/23/03
+ * @author Philip Milne
+ * @author Minimal adjustments by Dierk Koenig, June 2005
+ */
+
+import javax.swing.*;
+import javax.swing.event.TableModelEvent;
+import javax.swing.table.JTableHeader;
+import javax.swing.table.TableColumnModel;
+import javax.swing.table.TableModel;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.Date;
+import java.util.Vector;
+
+public class TableSorter extends TableMap {
+    int indexes[];
+    Vector sortingColumns = new Vector();
+    boolean ascending = true;
+    int lastSortedColumn = -1;
+
+    public TableSorter() {
+        indexes = new int[0]; // For consistency.
+    }
+
+    public TableSorter(TableModel model) {
+        setModel(model);
+    }
+
+    public void setModel(TableModel model) {
+        super.setModel(model);
+        reallocateIndexes();
+    }
+
+    public int compareRowsByColumn(int row1, int row2, int column) {
+        Class type = model.getColumnClass(column);
+        TableModel data = model;
+
+        // Check for nulls
+
+        Object o1 = data.getValueAt(row1, column);
+        Object o2 = data.getValueAt(row2, column);
+
+        // If both values are null return 0
+        if (o1 == null && o2 == null) {
+            return 0;
+        } else if (o1 == null) { // Define null less than everything.
+            return -1;
+        } else if (o2 == null) {
+            return 1;
+        }
+
+/* We copy all returned values from the getValue call in case
+an optimised model is reusing one object to return many values.
+The Number subclasses in the JDK are immutable and so will not be used in
+this way but other subclasses of Number might want to do this to save
+space and avoid unnecessary heap allocation.
+*/
+        if (type.getSuperclass() == java.lang.Number.class) {
+            return compareNumbers(data, row1, column, row2);
+        }
+
+        if (type == java.util.Date.class) {
+            return compareDates(data, row1, column, row2);
+        }
+
+        if (type == String.class) {
+            return compareStrings(data, row1, column, row2);
+        }
+
+        if (type == Boolean.class) {
+            return compareBooleans(data, row1, column, row2);
+        }
+        return compareObjects(data, row1, column, row2);
+    }
+
+    private int compareObjects(TableModel data, int row1, int column, int row2) {
+        Object v1 = data.getValueAt(row1, column);
+        String s1 = v1.toString();
+        Object v2 = data.getValueAt(row2, column);
+        String s2 = v2.toString();
+        int result = s1.compareTo(s2);
+
+        if (result < 0)
+            return -1;
+        if (result > 0)
+            return 1;
+        return 0;
+    }
+
+    private int compareBooleans(TableModel data, int row1, int column, int row2) {
+        Boolean bool1 = (Boolean) data.getValueAt(row1, column);
+        boolean b1 = bool1.booleanValue();
+        Boolean bool2 = (Boolean) data.getValueAt(row2, column);
+        boolean b2 = bool2.booleanValue();
+
+        if (b1 == b2)
+            return 0;
+        if (b1) // Define false < true
+            return 1;
+        return -1;
+    }
+
+    private int compareStrings(TableModel data, int row1, int column, int row2) {
+        String s1 = (String) data.getValueAt(row1, column);
+        String s2 = (String) data.getValueAt(row2, column);
+        int result = s1.compareTo(s2);
+
+        if (result < 0)
+            return -1;
+        if (result > 0)
+            return 1;
+        return 0;
+    }
+
+    private int compareDates(TableModel data, int row1, int column, int row2) {
+        Date d1 = (Date) data.getValueAt(row1, column);
+        long n1 = d1.getTime();
+        Date d2 = (Date) data.getValueAt(row2, column);
+        long n2 = d2.getTime();
+
+        if (n1 < n2)
+            return -1;
+        if (n1 > n2)
+            return 1;
+        return 0;
+    }
+
+    private int compareNumbers(TableModel data, int row1, int column, int row2) {
+        Number n1 = (Number) data.getValueAt(row1, column);
+        double d1 = n1.doubleValue();
+        Number n2 = (Number) data.getValueAt(row2, column);
+        double d2 = n2.doubleValue();
+
+        if (d1 < d2)
+            return -1;
+        if (d1 > d2)
+            return 1;
+        return 0;
+    }
+
+    public int compare(int row1, int row2) {
+        for (int level = 0; level < sortingColumns.size(); level++) {
+            Integer column = (Integer) sortingColumns.elementAt(level);
+            int result = compareRowsByColumn(row1, row2, column.intValue());
+            if (result != 0)
+                return ascending ? result : -result;
+        }
+        return 0;
+    }
+
+    public void reallocateIndexes() {
+        int rowCount = model.getRowCount();
+
+        // Set up a new array of indexes with the right number of elements
+        // for the new data model.
+        indexes = new int[rowCount];
+
+        // Initialise with the identity mapping.
+        for (int row = 0; row < rowCount; row++)
+            indexes[row] = row;
+    }
+
+    public void tableChanged(TableModelEvent e) {
+        System.out.println("Sorter: tableChanged");
+        reallocateIndexes();
+
+        super.tableChanged(e);
+    }
+
+    public void checkModel() {
+        if (indexes.length != model.getRowCount()) {
+            System.err.println("Sorter not informed of a change in model.");
+        }
+    }
+
+    public void sort(Object sender) {
+        checkModel();
+        shuttlesort((int[]) indexes.clone(), indexes, 0, indexes.length);
+    }
+
+    public void n2sort() {
+        for (int i = 0; i < getRowCount(); i++) {
+            for (int j = i + 1; j < getRowCount(); j++) {
+                if (compare(indexes[i], indexes[j]) == -1) {
+                    swap(i, j);
+                }
+            }
+        }
+    }
+
+    // This is a home-grown implementation which we have not had time
+    // to research - it may perform poorly in some circumstances. It
+    // requires twice the space of an in-place algorithm and makes
+    // NlogN assigments shuttling the values between the two
+    // arrays. The number of compares appears to vary between N-1 and
+    // NlogN depending on the initial order but the main reason for
+    // using it here is that, unlike qsort, it is stable.
+    public void shuttlesort(int from[], int to[], int low, int high) {
+        if (high - low < 2) {
+            return;
+        }
+        int middle = (low + high) / 2;
+        shuttlesort(to, from, low, middle);
+        shuttlesort(to, from, middle, high);
+
+        int p = low;
+        int q = middle;
+
+        /* This is an optional short-cut; at each recursive call,
+        check to see if the elements in this subset are already
+        ordered.  If so, no further comparisons are needed; the
+        sub-array can just be copied.  The array must be copied rather
+        than assigned otherwise sister calls in the recursion might
+        get out of sinc.  When the number of elements is three they
+        are partitioned so that the first set, [low, mid), has one
+        element and and the second, [mid, high), has two. We skip the
+        optimisation when the number of elements is three or less as
+        the first compare in the normal merge will produce the same
+        sequence of steps. This optimisation seems to be worthwhile
+        for partially ordered lists but some analysis is needed to
+        find out how the performance drops to Nlog(N) as the initial
+        order diminishes - it may drop very quickly.  */
+
+        if (high - low >= 4 && compare(from[middle - 1], from[middle]) <= 0) {
+            System.arraycopy(from, low, to, low, high - low);
+            return;
+        }
+
+        // A normal merge.
+
+        for (int i = low; i < high; i++) {
+            if (q >= high || (p < middle && compare(from[p], from[q]) <= 0)) {
+                to[i] = from[p++];
+            } else {
+                to[i] = from[q++];
+            }
+        }
+    }
+
+    public void swap(int i, int j) {
+        int tmp = indexes[i];
+        indexes[i] = indexes[j];
+        indexes[j] = tmp;
+    }
+
+    // The mapping only affects the contents of the data rows.
+    // Pass all requests to these rows through the mapping array: "indexes".
+
+    public Object getValueAt(int aRow, int aColumn) {
+        checkModel();
+        return model.getValueAt(indexes[aRow], aColumn);
+    }
+
+    public void setValueAt(Object aValue, int aRow, int aColumn) {
+        checkModel();
+        model.setValueAt(aValue, indexes[aRow], aColumn);
+    }
+
+    public void sortByColumn(int column) {
+        sortByColumn(column, true);
+    }
+
+    public void sortByColumn(int column, boolean ascending) {
+        this.ascending = ascending;
+        sortingColumns.removeAllElements();
+        sortingColumns.addElement(new Integer(column));
+        sort(this);
+        super.tableChanged(new TableModelEvent(this));
+    }
+
+    // There is no-where else to put this.
+    // Add a mouse listener to the Table to trigger a table sort
+    // when a column heading is clicked in the JTable.
+    public void addMouseListenerToHeaderInTable(JTable table) {
+        final TableSorter sorter = this;
+        final JTable tableView = table;
+        tableView.setColumnSelectionAllowed(false);
+        MouseAdapter listMouseListener = new MouseAdapter() {
+            public void mouseClicked(MouseEvent e) {
+                TableColumnModel columnModel = tableView.getColumnModel();
+                int viewColumn = columnModel.getColumnIndexAtX(e.getX());
+                int column = tableView.convertColumnIndexToModel(viewColumn);
+                if (e.getClickCount() == 1 && column != -1) {
+                    if (lastSortedColumn == column) ascending = !ascending;
+                    sorter.sortByColumn(column, ascending);
+                    lastSortedColumn = column;
+                }
+            }
+        };
+        JTableHeader th = tableView.getTableHeader();
+        th.addMouseListener(listMouseListener);
+    }
+
+
+}
+
diff --git a/groovy/src/main/groovy/inspect/swingui/package.html b/groovy/src/main/groovy/inspect/swingui/package.html
new file mode 100644
index 0000000..941a96e
--- /dev/null
+++ b/groovy/src/main/groovy/inspect/swingui/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.inspect.swingui.*</title>
+  </head>
+  <body>
+    <p>Classes associated with the Swing GUI for inspecting objects.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/io/PlatformLineWriter.java b/groovy/src/main/groovy/io/PlatformLineWriter.java
new file mode 100644
index 0000000..d0e4c48
--- /dev/null
+++ b/groovy/src/main/groovy/io/PlatformLineWriter.java
@@ -0,0 +1,46 @@
+package groovy.io;
+
+import java.io.BufferedWriter;
+import java.io.Writer;
+import java.io.IOException;
+
+/**
+ * A buffered writer that gobbles any \r characters
+ * and replaces every \n with a platform specific newline.
+ * In many places Groovy normalises streams to only have \n
+ * characters but when creating files that must be used
+ * by other platform-aware tools, you sometimes want the
+ * newlines to match what the platform expects.
+ *
+ * @author Paul King
+ */
+public class PlatformLineWriter extends Writer {
+    private BufferedWriter writer;
+
+    public PlatformLineWriter(Writer out) {
+        writer = new BufferedWriter(out);
+    }
+
+    public PlatformLineWriter(Writer out, int sz) {
+        writer = new BufferedWriter(out, sz);
+    }
+
+    public void write(char cbuf[], int off, int len) throws IOException {
+        for (; len > 0; len--) {
+            char c = cbuf[off++];
+            if (c == '\n') {
+                writer.newLine();
+            } else if (c != '\r') {
+                writer.write(c);
+            }
+        }
+    }
+
+    public void flush() throws IOException {
+        writer.flush();
+    }
+
+    public void close() throws IOException {
+        writer.close();
+    }
+}
diff --git a/groovy/src/main/groovy/io/package.html b/groovy/src/main/groovy/io/package.html
new file mode 100644
index 0000000..0270f37
--- /dev/null
+++ b/groovy/src/main/groovy/io/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.io.*</title>
+  </head>
+  <body>
+    <p>Classes for Groovier Input/Output.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/lang/AdaptingMetaClass.java b/groovy/src/main/groovy/lang/AdaptingMetaClass.java
new file mode 100644
index 0000000..2fec543
--- /dev/null
+++ b/groovy/src/main/groovy/lang/AdaptingMetaClass.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+/**
+ * An interface for MetaClass instances that "adapt" other MetaClass instances such as a proxy or
+ * delegating MetaClass
+ *
+ * @author Graeme Rocher
+ * @since 1.1
+ *
+ *        <p/>
+ *        Created: Apr 16, 2007
+ *        Time: 9:35:42 PM
+ */
+public interface AdaptingMetaClass extends MetaClass {
+
+    /**
+     * Returns the MetaClass that this adapter adapts
+     *
+     * @return The MetaClass instance
+     */
+    MetaClass getAdaptee();
+
+    /**
+     * Sets the MetaClass adapted by this MetaClass
+     *
+     * @param metaClass The MetaClass to adapt
+     */
+    void setAdaptee(MetaClass metaClass);
+}
diff --git a/groovy/src/main/groovy/lang/BenchmarkInterceptor.java b/groovy/src/main/groovy/lang/BenchmarkInterceptor.java
new file mode 100644
index 0000000..71ca26c
--- /dev/null
+++ b/groovy/src/main/groovy/lang/BenchmarkInterceptor.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import java.util.*;
+
+/**
+ * Interceptor that registers the timestamp of each method call
+ * before and after invocation.
+ */
+public class BenchmarkInterceptor implements Interceptor {
+
+    protected Map calls = new HashMap(); // keys to list of invocation times and before and after
+
+
+    public Map getCalls() {
+        return calls;
+    }
+    public void reset() {
+        calls = new HashMap();
+    }
+
+    public Object beforeInvoke(Object object, String methodName, Object[] arguments) {
+        if (!calls.containsKey(methodName)) calls.put(methodName, new LinkedList());
+        ((List) calls.get(methodName)).add(new Long(System.currentTimeMillis()));
+
+        return null;
+    }
+
+    public Object afterInvoke(Object object, String methodName, Object[] arguments, Object result) {
+        ((List) calls.get(methodName)).add(new Long(System.currentTimeMillis()));
+        return result;
+    }
+
+    public boolean doInvoke() {
+        return true;
+    }
+
+    /**
+     * @return a list of lines, each item is [methodname, numberOfCalls, accumulatedTime]
+     */
+    public List statistic() {
+        List result = new LinkedList();
+        for (Iterator iter = calls.keySet().iterator(); iter.hasNext();) {
+            Object[] line = new Object[3];
+            result.add(line);
+            line[0] = iter.next();
+            List times = (List) calls.get(line[0]);
+            line[1] = new Integer(times.size() / 2);
+            int accTime = 0;
+            for (Iterator it = times.iterator(); it.hasNext();) {
+                Long start = (Long) it.next();
+                Long end = (Long) it.next();
+                accTime += end.longValue() - start.longValue();
+            }
+            line[2] = new Long(accTime);
+        }
+        return result;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/Binding.java b/groovy/src/main/groovy/lang/Binding.java
new file mode 100644
index 0000000..c95458b
--- /dev/null
+++ b/groovy/src/main/groovy/lang/Binding.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents the variable bindings of a script which can be altered
+ * from outside the script object or created outside of a script and passed
+ * into it.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class Binding extends GroovyObjectSupport {
+    private final Map variables;
+    
+    public Binding() {
+        variables = new HashMap();
+    }
+    
+    public Binding(Map variables) {
+        this.variables = variables;
+    }
+    
+    /**
+     * A helper constructor used in main(String[]) method calls
+     * 
+     * @param args are the command line arguments from a main()
+     */
+    public Binding(String[] args) {
+        this();
+        setVariable("args", args);
+    }
+    
+    /**
+     * @param name the name of the variable to lookup
+     * @return the variable value
+     */
+    public Object getVariable(String name) {
+        Object result = variables.get(name);
+        
+        if (result == null && !variables.containsKey(name)) {
+            throw new MissingPropertyException(name, Binding.class);
+        }
+        
+        return result;
+    }
+    
+    /**
+     * Sets the value of the given variable
+     * @param name the name of the variable to set
+     * @param value the new value for the given variable
+     */
+    public void setVariable(String name, Object value) {
+        variables.put(name, value);
+    }
+    
+    public Map getVariables() {
+        return variables;
+    }
+
+    /**
+     * Overloaded to make variables appear as bean properties or via the subscript operator
+     */
+    public Object getProperty(String property) {
+        /** @todo we should check if we have the property with the metaClass instead of try/catch  */
+        try {
+            return super.getProperty(property);
+        }
+        catch (MissingPropertyException e) {
+            return getVariable(property);
+        }
+    }
+
+    /**
+     * Overloaded to make variables appear as bean properties or via the subscript operator
+     */
+    public void setProperty(String property, Object newValue) {
+        /** @todo we should check if we have the property with the metaClass instead of try/catch  */
+        try {
+            super.setProperty(property, newValue);
+        }
+        catch (MissingPropertyException e) {
+            setVariable(property, newValue);
+        }
+    }
+
+}
diff --git a/groovy/src/main/groovy/lang/Buildable.java b/groovy/src/main/groovy/lang/Buildable.java
new file mode 100644
index 0000000..32a415d
--- /dev/null
+++ b/groovy/src/main/groovy/lang/Buildable.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+
+public interface Buildable {
+	void build(GroovyObject builder);
+}
diff --git a/groovy/src/main/groovy/lang/Closure.java b/groovy/src/main/groovy/lang/Closure.java
new file mode 100644
index 0000000..00283a4
--- /dev/null
+++ b/groovy/src/main/groovy/lang/Closure.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.reflection.CachedMethod;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.runtime.CurriedClosure;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+
+/**
+ * Represents any closure object in Groovy.
+ * <p/>
+ * Groovy allows instances of Closures to be called in a
+ * short form. For example:
+ * <pre>
+ *   def a = 1
+ *   def c = {a}
+ *   assert c() == 1
+ * </pre>
+ * To be able to use a Closure in this way with your own
+ * subclass, you need to provide a doCall method with any
+ * signature you want to. This ensures that 
+ * {@link #getMaximumNumberOfParameters()} and 
+ * {@link #getParameterTypes()} will work too without any 
+ * additional code. If no doCall method is provided a
+ * closure must be used in its long form like
+ * <pre>
+ *   def a = 1
+ *   def c = {a}
+ *   assert c.call() == 1
+ * </pre>
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:tug@wilson.co.uk">John Wilson</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @author Graeme Rocher
+ *
+ * @version $Revision$
+ */
+public abstract class Closure extends GroovyObjectSupport implements Cloneable, Runnable {
+
+    /**
+     * With this resolveStrategy set the closure will attempt to resolve property references to the
+     * owner first
+     */
+    public static final int OWNER_FIRST = 0;
+    /**
+     * With this resolveStrategy set the closure will attempt to resolve property references to the
+     * delegate first
+     */
+    public static final int DELEGATE_FIRST = 1;
+    /**
+     * With this resolveStrategy set the closure will resolve property references to the owner only
+     * and not call the delegate at all
+     */
+    public static final int OWNER_ONLY = 2;
+    /**
+     * With this resolveStrategy set the closure will resolve property references to the delegate
+     * only and entirely bypass the owner
+     */
+    public static final int DELEGATE_ONLY = 3;
+    /**
+     * With this resolveStrategy set the closure will resolve property references to itself and go
+     * through the usual MetaClass look-up process. This allows the developer to override getProperty
+     * using ExpandoMetaClass of the closure itself
+     */
+    public static final int TO_SELF = 4;
+
+    private Object delegate;
+    private final Object owner;
+    protected Class[] parameterTypes;
+    protected int maximumNumberOfParameters;
+    private final Object thisObject;
+    private int resolveStrategy = OWNER_FIRST;
+
+
+    private int directive;
+    public static final int DONE = 1, SKIP = 2;
+    private static final Object[] EMPTY_OBJECT_ARRAY = {};
+
+    public Closure(Object owner, Object thisObject) {
+        this.owner = owner;
+        this.delegate = owner;
+        this.thisObject = thisObject;
+
+        final Class clazz = this.getClass();
+        final CachedMethod[] methods = ReflectionCache.getCachedClass(clazz).getMethods();
+
+        // set it to -1 for starters so parameterTypes will always get a type
+        maximumNumberOfParameters = -1;
+        for (int j = 0; j < methods.length; j++) {
+            if ("doCall".equals(methods[j].getName())) {
+                final Class[] pt = methods[j].getNativeParameterTypes();
+                if (pt.length > maximumNumberOfParameters) {
+                    parameterTypes = pt;
+                    maximumNumberOfParameters = parameterTypes.length;
+                }
+            }
+        }
+        // this line should be useless, but well, just in case
+        maximumNumberOfParameters = Math.max(maximumNumberOfParameters,0);
+    }
+    
+    public Closure(Object owner) {
+        this(owner,null);
+    }
+
+    /**
+     * Sets the strategy which the closure uses to resolve property references. The default is Closure.OWNER_FIRST
+     *
+     * @param resolveStrategy The resolve strategy to set
+     *
+     * @see groovy.lang.Closure#DELEGATE_FIRST
+     * @see groovy.lang.Closure#DELEGATE_ONLY
+     * @see groovy.lang.Closure#OWNER_FIRST
+     * @see groovy.lang.Closure#OWNER_ONLY
+     * @see groovy.lang.Closure#TO_SELF
+     */
+    public void setResolveStrategy(int resolveStrategy) {
+        this.resolveStrategy = resolveStrategy;
+    }
+
+    /**
+     * Gets the strategy which the closure users to resolve methods and properties
+     *
+     * @return The resolve strategy
+     * 
+     * @see groovy.lang.Closure#DELEGATE_FIRST
+     * @see groovy.lang.Closure#DELEGATE_ONLY
+     * @see groovy.lang.Closure#OWNER_FIRST
+     * @see groovy.lang.Closure#OWNER_ONLY
+     * @see groovy.lang.Closure#TO_SELF
+     */
+    public int getResolveStrategy() {
+        return resolveStrategy;
+    }
+
+    public Object getThisObject(){
+        return thisObject;
+    }
+
+    public Object getProperty(final String property) {
+        if ("delegate".equals(property)) {
+            return getDelegate();
+        } else if ("owner".equals(property)) {
+            return getOwner();
+        } else if ("maximumNumberOfParameters".equals(property)) {
+            return new Integer(getMaximumNumberOfParameters());
+        } else if ("parameterTypes".equals(property)) {
+            return getParameterTypes();
+        } else if ("metaClass".equals(property)) {
+            return getMetaClass();
+        } else if ("class".equals(property)) {
+            return getClass();
+        } else if ("directive".equals(property)) {
+            return new Integer(getDirective());
+        } else {
+            switch(resolveStrategy) {
+                case DELEGATE_FIRST:
+                    return getPropertyDelegateFirst(property);
+                case DELEGATE_ONLY:
+                    return InvokerHelper.getProperty(this.delegate, property);
+                case OWNER_ONLY:
+                    return InvokerHelper.getProperty(this.owner, property);
+                case TO_SELF:
+                    return super.getProperty(property);
+                default:
+                    return getPropertyOwnerFirst(property);
+            }
+        }
+    }
+
+    private Object getPropertyDelegateFirst(String property) {
+        if(delegate == null) return getPropertyOwnerFirst(property);
+        return getPropertyTryThese(property, this.delegate, this.owner);
+
+    }
+
+    private Object getPropertyTryThese(String property, Object firstTry, Object secondTry) {
+        try {
+            // lets try getting the property on the owner
+            return InvokerHelper.getProperty(firstTry, property);
+        } catch (MissingPropertyException e1) {
+            if (secondTry != null && firstTry != this && firstTry != secondTry) {
+                try {
+                    // lets try getting the property on the delegate
+                    return InvokerHelper.getProperty(secondTry, property);
+                } catch (GroovyRuntimeException e2) {
+                    // ignore, we'll throw e1
+                }
+            }
+
+            throw e1;
+        }
+    }
+
+    private Object getPropertyOwnerFirst(String property) {
+        return getPropertyTryThese(property, this.owner, this.delegate);
+    }
+
+    public void setProperty(String property, Object newValue) {
+        if ("delegate".equals(property)) {
+            setDelegate(newValue);
+        } else if ("metaClass".equals(property)) {
+            setMetaClass((MetaClass) newValue);
+        } else if ("resolveStrategy".equals(property)) {
+            setResolveStrategy(((Number)newValue).intValue());
+        }
+        else {
+            switch(resolveStrategy) {
+                case DELEGATE_FIRST:
+                    setPropertyDelegateFirst(property, newValue);
+                break;
+                case DELEGATE_ONLY:
+                    InvokerHelper.setProperty(this.delegate, property, newValue);
+                break;
+                case OWNER_ONLY:
+                    InvokerHelper.setProperty(this.owner, property, newValue);
+                break;
+                case TO_SELF:
+                    super.setProperty(property, newValue);
+                break;
+                default:
+                    setPropertyOwnerFirst(property, newValue);
+            }
+        }
+    }
+
+    private void setPropertyDelegateFirst(String property, Object newValue) {
+        if(delegate == null) setPropertyOwnerFirst(property, newValue); 
+        else
+            setPropertyTryThese(property, newValue, this.delegate, this.owner);
+    }
+
+    private void setPropertyTryThese(String property, Object newValue, Object firstTry, Object secondTry) {
+        try {
+            // lets try setting the property on the owner
+            InvokerHelper.setProperty(firstTry, property, newValue);
+        } catch (GroovyRuntimeException e1) {
+            if (firstTry != null && firstTry != this && firstTry != secondTry) {
+                try {
+                    // lets try setting the property on the delegate
+                    InvokerHelper.setProperty(secondTry, property, newValue);
+                    return;
+                } catch (GroovyRuntimeException e2) {
+                    // ignore, we'll throw e1
+                }
+            }
+
+            throw e1;
+        }
+    }
+
+    private void setPropertyOwnerFirst(String property, Object newValue) {
+        setPropertyTryThese(property, newValue, this.owner, this.delegate);
+    }
+
+    public boolean isCase(Object candidate){
+        return DefaultTypeTransformation.castToBoolean(call(candidate));
+    }
+
+    /**
+     * Invokes the closure without any parameters, returning any value if applicable.
+     *
+     * @return the value if applicable or null if there is no return statement in the closure
+     */
+    public Object call() {
+        final Object[] NOARGS = EMPTY_OBJECT_ARRAY;
+        return call(NOARGS);
+    }
+    
+    public Object call(Object[] args) {
+        try {
+            return getMetaClass().invokeMethod(this,"doCall",args);
+        } catch (Exception e) {
+            return throwRuntimeException(e);
+        }
+    }
+    
+    /**
+     * Invokes the closure, returning any value if applicable.
+     *
+     * @param arguments could be a single value or a List of values
+     * @return the value if applicable or null if there is no return statement in the closure
+     */
+    public Object call(final Object arguments) {
+        return call(new Object[]{arguments});
+    }
+    
+    protected static Object throwRuntimeException(Throwable throwable) {
+        if (throwable instanceof RuntimeException) {
+            throw (RuntimeException) throwable;
+        } else {
+            throw new GroovyRuntimeException(throwable.getMessage(), throwable);
+        }
+    }
+
+    /**
+     * @return the owner Object to which method calls will go which is
+     *         typically the outer class when the closure is constructed
+     */
+    public Object getOwner() {
+        return this.owner;
+    }
+
+    /**
+     * @return the delegate Object to which method calls will go which is
+     *         typically the outer class when the closure is constructed
+     */
+    public Object getDelegate() {
+        return this.delegate;
+    }
+
+    /**
+     * Allows the delegate to be changed such as when performing markup building
+     *
+     * @param delegate the new delegate
+     */
+    public void setDelegate(Object delegate) {
+        this.delegate = delegate;
+    }
+    
+    /**
+     * @return the parameter types of the longest doCall method
+     * of this closure
+     */
+    public Class[] getParameterTypes() {
+        return this.parameterTypes;
+    }
+
+    /**
+     * @return the maximum number of parameters a doCall methos
+     * of this closure can take
+     */
+    public int getMaximumNumberOfParameters() {
+        return this.maximumNumberOfParameters;
+    }
+
+    /**
+     * @return a version of this closure which implements Writable
+     */
+    public Closure asWritable() {
+        return new WritableClosure();
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Runnable#run()
+     */
+    public void run() {
+        call();
+    }
+
+    /**
+     * Support for closure currying
+     *
+     * @param arguments the arguments to bind
+     * @return the new closure with its arguments bound
+     */
+    public Closure curry(final Object arguments[]) {
+        return new CurriedClosure(this,arguments);
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#clone()
+     */
+    public Object clone() {
+        try {
+            return super.clone();
+        } catch (final CloneNotSupportedException e) {
+            return null;
+        }
+    }
+    
+    /**
+     * Implementation note: 
+     *   This has to be an inner class!
+     * 
+     * Reason: 
+     *   Closure.this.call will call the outer call method, but
+     * with the inner class as executing object. This means any
+     * invokeMethod or getProperty call will be called on this 
+     * inner class instead of the outer!
+     */
+    private class WritableClosure extends Closure implements Writable {
+        public WritableClosure() {
+            super(Closure.this);
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.Writable#writeTo(java.io.Writer)
+         */
+        public Writer writeTo(Writer out) throws IOException {
+            Closure.this.call(new Object[]{out});
+
+            return out;
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.GroovyObject#invokeMethod(java.lang.String, java.lang.Object)
+         */
+        public Object invokeMethod(String method, Object arguments) {
+            if ("clone".equals(method)) {
+                return clone();
+            } else if ("curry".equals(method)) {
+                return curry((Object[]) arguments);
+            } else if ("asWritable".equals(method)) {
+                return asWritable();
+            } else {
+                return Closure.this.invokeMethod(method, arguments);
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.GroovyObject#getProperty(java.lang.String)
+         */
+        public Object getProperty(String property) {
+            return Closure.this.getProperty(property);
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.GroovyObject#setProperty(java.lang.String, java.lang.Object)
+         */
+        public void setProperty(String property, Object newValue) {
+            Closure.this.setProperty(property, newValue);
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.Closure#call()
+         */
+        public Object call() {
+            return ((Closure) getOwner()).call();
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.Closure#call(java.lang.Object)
+         */
+        public Object call(Object arguments) {
+            return ((Closure) getOwner()).call(arguments);
+        }
+        
+        public Object call(Object[] args) {
+            return ((Closure) getOwner()).call(args);
+        }
+
+        public Object doCall(Object[] args) {
+            return call(args);
+        }
+        
+        /* (non-Javadoc)
+         * @see groovy.lang.Closure#getDelegate()
+         */
+        public Object getDelegate() {
+            return Closure.this.getDelegate();
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.Closure#setDelegate(java.lang.Object)
+         */
+        public void setDelegate(Object delegate) {
+            Closure.this.setDelegate(delegate);
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.Closure#getParameterTypes()
+         */
+        public Class[] getParameterTypes() {
+            return Closure.this.getParameterTypes();
+        }
+        
+        /* (non-Javadoc)
+         * @see groovy.lang.Closure#getParameterTypes()
+         */
+        public int getMaximumNumberOfParameters() {
+            return Closure.this.getMaximumNumberOfParameters();
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.Closure#asWritable()
+         */
+        public Closure asWritable() {
+            return this;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Runnable#run()
+         */
+        public void run() {
+            Closure.this.run();
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#clone()
+         */
+        public Object clone() {
+            return ((Closure) Closure.this.clone()).asWritable();
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        public int hashCode() {
+            return Closure.this.hashCode();
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        public boolean equals(Object arg0) {
+            return Closure.this.equals(arg0);
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#toString()
+         */
+        public String toString() {
+            final StringWriter writer = new StringWriter();
+
+            try {
+                writeTo(writer);
+            } catch (IOException e) {
+                return null;
+            }
+
+            return writer.toString();
+        }
+        
+        public Closure curry(final Object arguments[]) {
+            return (new CurriedClosure(this,arguments)).asWritable();
+        }
+
+        public void setResolveStrategy(int resolveStrategy) {
+            Closure.this.setResolveStrategy(resolveStrategy);
+        }
+        
+        public int getResolveStrategy() {
+            return Closure.this.getResolveStrategy();
+        }
+    }
+
+    /**
+     * @return Returns the directive.
+     */
+    public int getDirective() {
+        return directive;
+    }
+
+    /**
+     * @param directive The directive to set.
+     */
+    public void setDirective(int directive) {
+        this.directive = directive;
+    }
+
+}
diff --git a/groovy/src/main/groovy/lang/ClosureException.java b/groovy/src/main/groovy/lang/ClosureException.java
new file mode 100644
index 0000000..8a0bb65
--- /dev/null
+++ b/groovy/src/main/groovy/lang/ClosureException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+/**
+ * An exception thrown by a closure invocation
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ClosureException extends RuntimeException {
+
+    private final Closure closure;
+    
+    public ClosureException(Closure closure, Throwable cause) {
+        super("Exception thrown by call to closure: " + closure + " reaason: " + cause, cause);
+        this.closure = closure;
+    }
+
+    public Closure getClosure() {
+        return closure;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/ClosureInvokingMethod.java b/groovy/src/main/groovy/lang/ClosureInvokingMethod.java
new file mode 100644
index 0000000..2102cb8
--- /dev/null
+++ b/groovy/src/main/groovy/lang/ClosureInvokingMethod.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+/**
+ * An interface for MetaMethods that invoke closures to implements. Used by ExpandoMetaClass
+ *
+ * @see groovy.lang.ExpandoMetaClass
+ * 
+ * @author Graeme Rocher
+ * @since 1.1
+ */
+public interface ClosureInvokingMethod {
+
+	/**
+	 * Returns the original closure that this method invokes
+	 * @return The closure
+	 */
+	Closure getClosure();
+
+	/**
+	 * Is it a static method?
+	 * @return True if it is
+	 */
+	boolean isStatic();
+
+	/**
+	 * The method name
+	 * @return The method name
+	 */
+	String getName();
+}
diff --git a/groovy/src/main/groovy/lang/DelegatingMetaClass.java b/groovy/src/main/groovy/lang/DelegatingMetaClass.java
new file mode 100644
index 0000000..63b43d2
--- /dev/null
+++ b/groovy/src/main/groovy/lang/DelegatingMetaClass.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class DelegatingMetaClass implements MetaClass, MutableMetaClass, GroovyObject {
+    protected MetaClass delegate;
+    
+    public DelegatingMetaClass(final MetaClass delegate) {
+        this.delegate = delegate;
+    }
+   
+    public DelegatingMetaClass(final Class theClass) {
+        this(GroovySystem.getMetaClassRegistry().getMetaClass(theClass));
+    }
+
+    public boolean isModified() {
+        return this.delegate instanceof MutableMetaClass && ((MutableMetaClass) this.delegate).isModified();
+    }
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#addNewInstanceMethod(java.lang.reflect.Method)
+     */
+    public void addNewInstanceMethod(Method method) {
+        if(delegate instanceof MutableMetaClass)
+            ((MutableMetaClass)delegate).addNewInstanceMethod(method);
+    }
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#addNewStaticMethod(java.lang.reflect.Method)
+     */
+    public void addNewStaticMethod(Method method) {
+        if(delegate instanceof MutableMetaClass)
+            ((MutableMetaClass)delegate).addNewStaticMethod(method);
+    }
+
+    public void addMetaMethod(MetaMethod metaMethod) {
+        if(delegate instanceof MutableMetaClass)
+            ((MutableMetaClass)delegate).addMetaMethod(metaMethod);
+    }
+
+    public void addMetaBeanProperty(MetaBeanProperty metaBeanProperty) {
+        if(delegate instanceof MutableMetaClass)
+            ((MutableMetaClass)delegate).addMetaBeanProperty(metaBeanProperty);
+    }
+
+    /* (non-Javadoc)
+    * @see groovy.lang.MetaClass#initialize()
+    */
+    public void initialize() {
+        delegate.initialize();
+    }
+
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#getAttribute(java.lang.Object, java.lang.String)
+     */
+    public Object getAttribute(Object object, String attribute) {
+        return delegate.getAttribute(object, attribute);
+    }
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#getClassNode()
+     */
+    public ClassNode getClassNode() {
+         return delegate.getClassNode();
+    }
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#getMetaMethods()
+     */
+    public List getMetaMethods() {
+        return delegate.getMetaMethods();
+    }
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#getMethods()
+     */
+    public List getMethods() {
+        return delegate.getMethods();
+    }
+
+    public List respondsTo(Object obj, String name, Object[] argTypes) {
+        return delegate.respondsTo(obj, name, argTypes);
+    }
+
+    public List respondsTo(Object obj, String name) {
+        return delegate.respondsTo(obj, name);
+    }
+
+    public MetaProperty hasProperty(Object obj, String name) {
+        return delegate.hasProperty(obj, name);
+    }
+
+    /* (non-Javadoc)
+    * @see groovy.lang.MetaClass#getProperties()
+    */
+    public List getProperties() {
+        return delegate.getProperties();
+    }
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#getProperty(java.lang.Object, java.lang.String)
+     */
+    public Object getProperty(Object object, String property) {
+        return delegate.getProperty(object, property);
+    }
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#invokeConstructor(java.lang.Object[])
+     */
+    public Object invokeConstructor(Object[] arguments) {
+        return delegate.invokeConstructor(arguments);
+    }
+
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#invokeMethod(java.lang.Object, java.lang.String, java.lang.Object)
+     */
+    public Object invokeMethod(Object object, String methodName, Object arguments) {
+        return delegate.invokeMethod(object, methodName, arguments);
+    }
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#invokeMethod(java.lang.Object, java.lang.String, java.lang.Object[])
+     */
+    public Object invokeMethod(Object object, String methodName, Object[] arguments) {
+        return delegate.invokeMethod(object, methodName, arguments);
+    }
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#invokeStaticMethod(java.lang.Object, java.lang.String, java.lang.Object[])
+     */
+    public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
+        return delegate.invokeStaticMethod(object, methodName, arguments);
+    }
+
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#setAttribute(java.lang.Object, java.lang.String, java.lang.Object)
+     */
+    public void setAttribute(Object object, String attribute, Object newValue) {
+        delegate.setAttribute(object, attribute, newValue);
+    }
+    /* (non-Javadoc)
+     * @see groovy.lang.MetaClass#setProperty(java.lang.Object, java.lang.String, java.lang.Object)
+     */
+    public void setProperty(Object object, String property, Object newValue) {
+        delegate.setProperty(object, property, newValue);
+    }
+    /* (non-Javadoc)
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        return delegate.equals(obj);
+    }
+    /* (non-Javadoc)
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        return delegate.hashCode();
+    }
+
+    public String toString() { 
+        return super.toString() + "[" + delegate.toString()+ "]";
+    }
+
+    /**
+     * @deprecated
+     */
+    public MetaMethod pickMethod(String methodName, Class[] arguments) {
+        return delegate.pickMethod(methodName,arguments);
+    }
+
+    public Object getAttribute(Class sender, Object receiver, String messageName, boolean useSuper) {
+        return this.delegate.getAttribute(sender, receiver, messageName, useSuper);
+    }
+
+    public Object getProperty(Class sender, Object receiver, String messageName, boolean useSuper, boolean fromInsideClass) {
+        return this.delegate.getProperty(sender, receiver, messageName, useSuper, fromInsideClass);
+    }
+
+    public MetaProperty getMetaProperty(String name) {
+        return this.delegate.getMetaProperty(name);
+    }
+
+    public MetaMethod getStaticMetaMethod(String name, Object[] args) {
+        return this.delegate.getStaticMetaMethod(name, args);
+    }
+
+    public MetaMethod getStaticMetaMethod(String name, Class[] argTypes) {
+        return this.delegate.getStaticMetaMethod(name, argTypes);
+    }
+
+    public MetaMethod getMetaMethod(String name, Object[] args) {
+        return this.delegate.getMetaMethod(name, args);
+    }
+
+    public MetaMethod getMetaMethod(String name, Class[] argTypes) {
+        return this.delegate.getMetaMethod(name, argTypes);
+    }
+
+    public Class getTheClass() {
+        return this.delegate.getTheClass();
+    }
+
+    public Object invokeMethod(Class sender, Object receiver, String methodName, Object[] arguments, boolean isCallToSuper, boolean fromInsideClass) {
+        return this.delegate.invokeMethod(sender, receiver, methodName, arguments, isCallToSuper, fromInsideClass);
+    }
+
+    public Object invokeMissingMethod(Object instance, String methodName, Object[] arguments) {
+        return this.delegate.invokeMissingMethod(instance, methodName, arguments);
+    }
+
+    public Object invokeMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
+        return this.delegate.invokeMissingProperty(instance, propertyName, optionalValue, isGetter);
+    }
+
+    public boolean isGroovyObject() {
+        return GroovyObject.class.isAssignableFrom(this.delegate.getTheClass());
+    }
+
+    public void setAttribute(Class sender, Object receiver, String messageName, Object messageValue, boolean useSuper, boolean fromInsideClass) {
+        this.delegate.setAttribute(sender, receiver, messageName, messageValue, useSuper, fromInsideClass);
+    }
+
+    public void setProperty(Class sender, Object receiver, String messageName, Object messageValue, boolean useSuper, boolean fromInsideClass) {
+        this.delegate.setProperty(sender, receiver, messageName, messageValue, useSuper, fromInsideClass);
+    }
+
+    public int selectConstructorAndTransformArguments(int numberOfCosntructors, Object[] arguments) {
+        return this.delegate.selectConstructorAndTransformArguments(numberOfCosntructors, arguments);
+    }
+
+	public void setAdaptee(MetaClass adaptee) {
+		this.delegate = adaptee; 
+	}
+
+    public MetaClass getAdaptee() {
+        return this.delegate; 
+    }
+
+    public Object invokeMethod(String name, Object args) {
+      try {
+        return getMetaClass().invokeMethod(this, name, args);
+      }
+      catch (MissingMethodException e) {
+        if (delegate instanceof GroovyObject)
+          return ((GroovyObject)delegate).invokeMethod(name, args);
+        else
+          throw e;
+      }
+    }
+
+    public Object getProperty(String property) {
+      try {
+        return getMetaClass().getProperty(this, property);
+      }
+      catch (MissingPropertyException e) {
+        if (delegate instanceof GroovyObject)
+          return ((GroovyObject)delegate).getProperty(property);
+        else
+          throw e;
+      }
+    }
+
+    public void setProperty(String property, Object newValue) {
+        try {
+          getMetaClass().setProperty(this, property, newValue);
+        }
+        catch (MissingPropertyException e) {
+          if (delegate instanceof GroovyObject)
+            ((GroovyObject)delegate).setProperty(property, newValue);
+          else
+            throw e;
+        }
+    }
+
+    public MetaClass getMetaClass() {
+        return InvokerHelper.getMetaClass(this);
+    }
+
+    public void setMetaClass(MetaClass metaClass) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/groovy/src/main/groovy/lang/DeprecationException.java b/groovy/src/main/groovy/lang/DeprecationException.java
new file mode 100644
index 0000000..61be5d6
--- /dev/null
+++ b/groovy/src/main/groovy/lang/DeprecationException.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+/**
+ * Use this exception to mark a method implementation as being deprecated.
+ *
+ * Use the message to indicate the recommended way of calling the desired functionality.
+ * Make throwing this exception the only line in the method implementation, i.e. unlike
+ * the JavaDoc deprecated feature there is no relay to the new implementation but an early
+ * and deliberate halt of execution ("fail early").
+ *
+ * This exception is supposed to be used in the SNAPSHOT releases only. Before release, all
+ * references to this exception should be resolved and the according methods removed.
+ *
+ * @author Dierk Koenig
+ */
+public class DeprecationException extends RuntimeException {
+
+    public DeprecationException(String message) {
+        super(message);
+    }
+
+    public DeprecationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/groovy/src/main/groovy/lang/EmptyRange.java b/groovy/src/main/groovy/lang/EmptyRange.java
new file mode 100644
index 0000000..a2f9413
--- /dev/null
+++ b/groovy/src/main/groovy/lang/EmptyRange.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.util.*;
+
+/**
+ * Constructing Ranges like 0..<0
+ * @author Dierk Koenig
+ * @author Edwin Tellman
+ */
+public class EmptyRange extends AbstractList implements Range {
+    
+    /**
+     * The value at which the range originates (may be <code>null</code>).
+     */
+    protected Comparable at;
+
+    /**
+     * Creates a new {@link EmptyRange}.
+     * 
+     * @param at the value at which the range starts (may be <code>null</code>).
+     */
+    public EmptyRange(Comparable at) {
+       this.at = at;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Comparable getFrom() {
+        return at;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Comparable getTo() {
+        return at;
+    }
+
+    /**
+     * Never true for an empty range.
+     * 
+     * @return <code>false</code>
+     */
+    public boolean isReverse() {
+        return false;
+    }
+
+    /**
+     * Never true for an empty range.
+     *
+     * @return <code>false</code>
+     */
+    public boolean containsWithinBounds(Object o) {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String inspect() {
+        return InvokerHelper.inspect(at) + "..<" + InvokerHelper.inspect(at);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return (null == at) 
+            ? "null..<null"
+            : at.toString() + "..<" + at.toString();
+    }
+
+    /**
+     * Always 0 for an empty range.
+     * 
+     * @return 0
+     */
+    public int size() {
+        return 0;
+    }
+
+    /**
+     * Always throws <code>IndexOutOfBoundsException</code> for an empty range.
+     * 
+     * @throws IndexOutOfBoundsException always
+     */
+    public Object get(int index) {
+        throw new IndexOutOfBoundsException("can't get values from Empty Ranges");
+    }
+
+    /**
+     * Always throws <code>UnsupportedOperationException</code> for an empty range.
+     *
+     * @throws UnsupportedOperationException always
+     */
+    public boolean add(Object o) {
+        throw new UnsupportedOperationException("cannot add to Empty Ranges");
+    }
+
+    /**
+     * Always throws <code>UnsupportedOperationException</code> for an empty range.
+     *
+     * @throws UnsupportedOperationException
+     */
+    public boolean addAll(int index, Collection c) {
+        throw new UnsupportedOperationException("cannot add to Empty Ranges");
+    }
+
+    /**
+     * Always throws <code>UnsupportedOperationException</code> for an empty range.
+     *
+     * @throws UnsupportedOperationException
+     */
+    public boolean addAll(Collection c) {
+        throw new UnsupportedOperationException("cannot add to Empty Ranges");
+    }
+    
+    /**
+     * Always throws <code>UnsupportedOperationException</code> for an empty range.
+     *
+     * @throws UnsupportedOperationException
+     */
+    public boolean remove(Object o) {
+        throw new UnsupportedOperationException("cannot remove from Empty Ranges");
+    }
+
+    /**
+     * Always throws <code>UnsupportedOperationException</code> for an empty range.
+     *
+     * @throws UnsupportedOperationException
+     */
+    public Object remove(int index) {
+        throw new UnsupportedOperationException("cannot remove from Empty Ranges");
+    }
+
+    /**
+     * Always throws <code>UnsupportedOperationException</code> for an empty range.
+     *
+     * @throws UnsupportedOperationException
+     */
+    public boolean removeAll(Collection c) {
+        throw new UnsupportedOperationException("cannot remove from Empty Ranges");
+    }
+
+    /**
+     * Always throws <code>UnsupportedOperationException</code> for an empty range.
+     *
+     * @throws UnsupportedOperationException
+     */
+    public boolean retainAll(Collection c) {
+        throw new UnsupportedOperationException("cannot retainAll in Empty Ranges");
+    }
+
+     /**
+      * Always throws <code>UnsupportedOperationException</code> for an empty range.
+      *
+     * @throws UnsupportedOperationException
+     */
+    public Object set(int index, Object element) {
+        throw new UnsupportedOperationException("cannot set in Empty Ranges");
+    }
+
+    /**
+     * Always does nothing for an empty range.
+     */
+    public void step(int step, Closure closure) {
+    }
+
+    /**
+     * Always returns an empty list for an empty range.
+     */
+    public List step(int step) {
+        return new ArrayList();
+    }
+}
diff --git a/groovy/src/main/groovy/lang/ExpandoMetaClass.java b/groovy/src/main/groovy/lang/ExpandoMetaClass.java
new file mode 100644
index 0000000..50de0e7
--- /dev/null
+++ b/groovy/src/main/groovy/lang/ExpandoMetaClass.java
@@ -0,0 +1,1121 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.runtime.*;
+import org.codehaus.groovy.runtime.metaclass.ClosureMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.ClosureStaticMetaMethod;
+import org.codehaus.groovy.runtime.metaclass.ConcurrentReaderHashMap;
+import org.codehaus.groovy.runtime.metaclass.ThreadManagedMetaBeanProperty;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/**
+ * A MetaClass that implements GroovyObject and behaves like an Expando, allowing the addition of new methods on the fly
+ *
+ * <code><pre>
+ * // defines or replaces instance method:
+ * metaClass.myMethod = { args -> }
+ *
+ * // defines a new instance method
+ * metaClass.myMethod << { args -> }
+ *
+ * // creates multiple overloaded methods of the same name
+ * metaClass.myMethod << { String s -> } << { Integer i -> }
+ *
+ * // defines or replaces a static method with the 'static' qualifier
+ * metaClass.'static'.myMethod = { args ->  }
+ *
+ * // defines a new static method with the 'static' qualifier
+ * metaClass.'static'.myMethod << { args ->  }
+ *
+ * // defines a new constructor
+ * metaClass.constructor << { String arg -> }
+ *
+ * // defines or replaces a constructor
+ * metaClass.constructor = { String arg -> }
+ *
+ * // defines a new property with an initial value of "blah"
+ * metaClass.myProperty = "blah"
+ *
+ * </code></pre>
+ *
+ * By default methods are only allowed to be added before initialize() is called. In other words you create a new
+ * ExpandoMetaClass, add some methods and then call initialize(). If you attempt to add new methods after initialize()
+ * has been called an error will be thrown.
+ *
+ * This is to ensure that the MetaClass can operate appropriately in multi threaded environments as it forces you
+ * to do all method additions at the beginning, before using the MetaClass.
+ *
+ * If you need more fine grained control of how a method is matched you can use DynamicMethodsMetaClass
+ *
+ * WARNING: This MetaClass uses a thread-bound ThreadLocal instance to store and retrieve properties.
+ * In addition properties stored use soft references so they are both bound by the life of the Thread and by the soft
+ * references. The implication here is you should NEVER use dynamic properties if you want their values to stick around
+ * for long periods because as soon as the JVM is running low on memory or the thread dies they will be garbage collected.
+ *
+ * @author Graeme Rocher
+ * @since 1.1
+ */
+public class ExpandoMetaClass extends MetaClassImpl implements GroovyObject {
+
+	private static final String META_CLASS = "metaClass";
+	private static final String CLASS = "class";
+	private static final String META_METHODS = "metaMethods";
+	private static final String METHODS = "methods";
+	private static final String PROPERTIES = "properties";
+	public static final String STATIC_QUALIFIER = "static";
+	private static final Class[] ZERO_ARGUMENTS = new Class[0];
+	private static final String CONSTRUCTOR = "constructor";
+    private static final String GET_PROPERTY_METHOD = "getProperty";
+    private static final String SET_PROPERTY_METHOD = "setProperty";
+
+    private static final String INVOKE_METHOD_METHOD = "invokeMethod";
+    private static final String CLASS_PROPERTY = "class";
+    private static final String META_CLASS_PROPERTY = "metaClass";
+    private static final String GROOVY_CONSTRUCTOR = "<init>";
+
+    // These two properties are used when no ExpandoMetaClassCreationHandle is present
+
+    private MetaClass myMetaClass;
+    private boolean allowChangesAfterInit;
+
+    private boolean initialized;
+    private boolean initCalled;
+    private boolean modified;
+    private boolean inRegistry;
+    private final Set inheritedMetaMethods = new HashSet();
+    private final Map beanPropertyCache = new ConcurrentReaderHashMap();
+    private final Map staticBeanPropertyCache = new ConcurrentReaderHashMap();
+    private final Map expandoMethods = new ConcurrentReaderHashMap();
+    private final Map expandoProperties = new ConcurrentReaderHashMap();
+    private ClosureMetaMethod getPropertyMethod;
+    private ClosureMetaMethod invokeMethodMethod;
+    private ClosureMetaMethod setPropertyMethod;
+    private ClosureStaticMetaMethod invokeStaticMethodMethod;
+
+    /**
+	 * Constructs a new ExpandoMetaClass instance for the given class
+	 *
+	 * @param theClass The class that the MetaClass applies to
+	 */
+	public ExpandoMetaClass(Class theClass) {
+		super(GroovySystem.getMetaClassRegistry(), theClass);
+		this.myMetaClass = InvokerHelper.getMetaClass(this);
+
+	}
+
+	/**
+	 * Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass
+	 * in the MetaClassRegistry automatically
+	 *
+	 * @param theClass The class that the MetaClass applies to
+	 * @param register True if the MetaClass should be registered inside the MetaClassRegistry. This defaults to true and ExpandoMetaClass will effect all instances if changed
+	 */
+	public ExpandoMetaClass(Class theClass, boolean register) {
+		this(theClass);
+	    this.inRegistry = register;
+	}
+
+	/**
+	 * Constructs a new ExpandoMetaClass instance for the given class optionally placing the MetaClass
+	 * in the MetaClassRegistry automatically
+	 *
+	 * @param theClass The class that the MetaClass applies to
+	 * @param register True if the MetaClass should be registered inside the MetaClassRegistry. This defaults to true and ExpandoMetaClass will effect all instances if changed
+     * @param allowChangesAfterInit Should the meta class be modifiable after initialization. Default is false.
+	 */
+	public ExpandoMetaClass(Class theClass, boolean register, boolean allowChangesAfterInit) {
+		this(theClass);
+	    this.inRegistry = register;
+        this.allowChangesAfterInit = allowChangesAfterInit;
+    }
+
+
+    /**
+     * Overrides the default missing method behaviour and adds the capability to look up a method from super class
+     *
+     * @see MetaClassImpl#invokeMissingMethod(Object, String, Object[])
+     */
+    public Object invokeMissingMethod(Object instance, String methodName, Object[] arguments) {
+        Class superClass = instance instanceof Class ? (Class)instance : instance.getClass();
+        while(superClass != Object.class) {
+            final MetaMethod method = findMethodInClassHeirarchy(methodName, arguments, superClass);
+            if(method != null) {
+                addSuperMethodIfNotOverriden(method);
+                return method.invoke(instance, arguments);
+            }
+            superClass = superClass.getSuperclass();
+        }
+        // still not method here, so see if there is an invokeMethod method up the heirarchy
+        final Object[] invokeMethodArgs = {methodName, arguments};
+        final MetaMethod method = findMethodInClassHeirarchy(INVOKE_METHOD_METHOD, invokeMethodArgs, theClass );
+        if(method!=null && method instanceof ClosureMetaMethod) {
+            this.invokeMethodMethod = (ClosureMetaMethod)method;
+            return method.invoke(instance, invokeMethodArgs);
+        }
+
+        return super.invokeMissingMethod(instance, methodName, arguments);
+    }
+
+    /**
+     * Overrides the default missing method behaviour and adds the capability to look up a method from the super class in the case
+     * where it has been overriden
+     *
+     * @param instance The instance of the object
+     * @param propertyName The property name
+     * @param optionalValue The property value in the case of a setter
+     * @param isGetter True if it is a getter
+     * @return The return value if of a getProperty call or a MissingPropertyException is thrown
+     */
+    public Object invokeMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
+        Class theClass = instance instanceof Class ? (Class)instance : instance.getClass();
+        CachedClass superClass = theCachedClass;
+        while(superClass != ReflectionCache.OBJECT_CLASS) {
+            final MetaBeanProperty property = findPropertyInClassHierarchy(propertyName, superClass);
+            if(property != null) {
+                addMetaBeanProperty(property);
+                if(!isGetter) {
+                    property.setProperty(instance, optionalValue);
+                    return null;
+                }
+                else {
+                    return property.getProperty(instance);
+                }
+            }
+            superClass = superClass.getCachedSuperClass();
+        }
+        // got here to property not found, look for getProperty or setProperty overrides
+        if(isGetter) {
+            final Object[] getPropertyArgs = {propertyName};
+            final MetaMethod method = findMethodInClassHeirarchy(GET_PROPERTY_METHOD, getPropertyArgs, theClass);
+            if(method != null && method instanceof ClosureMetaMethod) {
+                this.getPropertyMethod = (ClosureMetaMethod)method;
+                return method.invoke(instance,getPropertyArgs);
+            }
+        }
+        else {
+            final Object[] setPropertyArgs = {propertyName, optionalValue};
+            final MetaMethod method = findMethodInClassHeirarchy(SET_PROPERTY_METHOD, setPropertyArgs, theClass);
+            if(method != null && method instanceof ClosureMetaMethod) {
+                this.setPropertyMethod = (ClosureMetaMethod)method;
+                return method.invoke(instance, setPropertyArgs);
+            }
+        }
+        return super.invokeMissingProperty(instance, propertyName, optionalValue, isGetter);
+    }
+
+    private MetaBeanProperty findPropertyInClassHierarchy(String propertyName, CachedClass theClass) {
+        MetaBeanProperty property= null;
+        final CachedClass superClass = theClass.getCachedSuperClass();
+        MetaClass metaClass = this.registry.getMetaClass(superClass.getCachedClass());
+        if(metaClass instanceof MutableMetaClass) {
+            property = getMetaPropertyFromMutableMetaClass(propertyName,metaClass);
+            if(property == null) {
+                if(superClass != ReflectionCache.OBJECT_CLASS) {
+                    property = findPropertyInClassHierarchy(propertyName, superClass);
+                }
+                if(property == null) {
+                    final Class[] interfaces = theClass.getCachedClass().getInterfaces();
+                    property = searchInterfacesForMetaProperty(propertyName, interfaces);
+                }
+            }
+        }
+        return property;
+
+    }
+
+    private MetaBeanProperty searchInterfacesForMetaProperty(String propertyName, Class[] interfaces) {
+        MetaBeanProperty property = null;
+        for (int i = 0; i < interfaces.length; i++) {
+            Class anInterface = interfaces[i];
+            MetaClass metaClass = this.registry.getMetaClass(anInterface);
+            if(metaClass instanceof MutableMetaClass) {
+                property = getMetaPropertyFromMutableMetaClass(propertyName,metaClass);
+                if(property != null) break;
+            }
+            Class[] superInterfaces = anInterface.getInterfaces();
+            if(superInterfaces.length > 0) {
+                property = searchInterfacesForMetaProperty(propertyName, superInterfaces);
+                if(property!=null) break;
+            }
+
+        }
+        return property;
+    }
+
+    private MetaBeanProperty getMetaPropertyFromMutableMetaClass(String propertyName, MetaClass metaClass) {
+        final boolean isModified = ((MutableMetaClass) metaClass).isModified();
+        final MetaProperty metaProperty = metaClass.getMetaProperty(propertyName);
+        if(metaProperty instanceof MetaBeanProperty)
+            return isModified ? (MetaBeanProperty)metaProperty : null;
+        else
+            return null;
+
+    }
+
+    private MetaMethod findMethodInClassHeirarchy(String methodName, Object[] arguments, Class theClass) {
+        MetaMethod method = null;
+        final Class superClass = theClass.getSuperclass();
+        if (superClass == null)
+          return null;
+        
+        MetaClass metaClass = this.registry.getMetaClass(superClass);
+        if(metaClass instanceof MutableMetaClass) {
+            method = getMetaMethodFromMutableMetaClass(methodName, arguments, metaClass);
+            if(method == null) {
+                if(superClass != Object.class) {
+                    method = findMethodInClassHeirarchy(methodName, arguments, superClass);
+                }
+                if(method == null) {
+                    final Class[] interfaces = theClass.getInterfaces();
+                    method = searchInterfacesForMetaMethod(methodName, arguments, interfaces);
+                }
+            }
+        }
+        return method;
+    }
+
+    private MetaMethod searchInterfacesForMetaMethod(String methodName, Object[] arguments, Class[] interfaces) {
+        MetaMethod method = null;
+        for (int i = 0; i < interfaces.length; i++) {
+            Class anInterface = interfaces[i];
+            MetaClass metaClass = this.registry.getMetaClass(anInterface);
+            if(metaClass instanceof MutableMetaClass) {
+                method = getMetaMethodFromMutableMetaClass(methodName, arguments, metaClass);
+                if(method != null) break;
+            }
+            Class[] superInterfaces = anInterface.getInterfaces();
+            if(superInterfaces.length > 0) {
+                method = searchInterfacesForMetaMethod(methodName,arguments, superInterfaces);
+                if(method!=null) break;
+            }
+
+        }
+        return method;
+    }
+
+    private MetaMethod getMetaMethodFromMutableMetaClass(String methodName, Object[] arguments, MetaClass metaClass) {
+        final boolean isModified = ((MutableMetaClass) metaClass).isModified();
+        return isModified ? metaClass.getMetaMethod(methodName, arguments) : null;
+    }
+
+    public synchronized boolean isModified() {
+        return this.modified;
+    }
+
+    /**
+     * For simulating closures in Java
+     */
+    private interface Callable {
+		void call();
+	}
+
+    /**
+     * Call to enable global use of global use of ExpandoMetaClass within the registry. This has the advantage that
+     * inheritance will function correctly, but has a higher memory usage on the JVM than normal Groovy
+     */
+    public static void enableGlobally() {
+        ExpandoMetaClassCreationHandle.enable();
+    }
+
+    /**
+     * Call to disable the global use of ExpandoMetaClass
+     */
+    public static void disableGlobally() {
+        ExpandoMetaClassCreationHandle.disable();
+    }
+
+
+
+    /* (non-Javadoc)
+	 * @see groovy.lang.MetaClassImpl#initialize()
+	 */
+	public synchronized void initialize() {
+        if (!isInitialized()) {
+            super.initialize();
+            setInitialized(true);
+            this.initCalled = true;
+        }
+    }
+
+
+	/* (non-Javadoc)
+	 * @see groovy.lang.MetaClassImpl#isInitialized()
+	 */
+	protected synchronized boolean isInitialized() {
+		return this.initialized;
+	}
+
+    protected synchronized void setInitialized(boolean b) {
+        this.initialized = b;
+    }
+
+
+    private void addSuperMethodIfNotOverriden(final MetaMethod metaMethodFromSuper) {
+		performOperationOnMetaClass(new Callable() {
+			public void call() {
+
+                MetaMethod existing = null;
+				try {
+					existing = pickMethod(metaMethodFromSuper.getName(), metaMethodFromSuper.getNativeParameterTypes());}
+				catch ( GroovyRuntimeException e) {
+					// ignore, this happens with overlapping method definitions
+				}
+
+				if(existing == null) {
+                        addMethodWithKey(metaMethodFromSuper);
+				}
+				else {
+                    boolean isGroovyMethod = getMetaMethods().contains(existing);
+
+
+                    if(isGroovyMethod) {
+                        addMethodWithKey(metaMethodFromSuper);
+                    }
+                    else if(inheritedMetaMethods.contains(existing)) {
+                        inheritedMetaMethods.remove(existing);
+
+                        addMethodWithKey(metaMethodFromSuper);
+                    }
+                }
+
+			}
+
+			private void addMethodWithKey(final MetaMethod metaMethodFromSuper) {
+                inheritedMetaMethods.add(metaMethodFromSuper);
+                if(metaMethodFromSuper instanceof ClosureMetaMethod) {
+                    ClosureMetaMethod closureMethod = (ClosureMetaMethod)metaMethodFromSuper;
+                    Closure cloned = (Closure)closureMethod.getClosure().clone();
+                    String name = metaMethodFromSuper.getName();
+                    ClosureMetaMethod localMethod = new ClosureMetaMethod(name, getJavaClass(), cloned);
+                    addMetaMethod(localMethod);
+                    MethodKey key = new DefaultCachedMethodKey(getJavaClass(),name, localMethod.getParameterTypes(),false );
+                    cacheInstanceMethod(key, localMethod);
+
+                    checkIfGroovyObjectMethod(localMethod, name);
+                    expandoMethods.put(key,localMethod);
+
+                }
+            }
+		});
+	}
+
+
+
+	/**
+	 * Instances of this class are returned when using the << left shift operator.
+	 *
+	 * Example:
+	 *
+	 * metaClass.myMethod << { String args -> }
+	 *
+	 * This allows callbacks to the ExpandoMetaClass for registering appending methods
+	 *
+	 * @author Graeme Rocher
+	 *
+	 */
+	protected class ExpandoMetaProperty extends GroovyObjectSupport {
+
+		protected String propertyName;
+		protected boolean isStatic;
+
+
+        protected ExpandoMetaProperty(String name) {
+			this(name, false);
+		}
+		protected ExpandoMetaProperty(String name, boolean isStatic) {
+			this.propertyName = name;
+			this.isStatic = isStatic;
+		}
+
+        public String getPropertyName() { return this.propertyName; }
+        public boolean isStatic() { return this.isStatic; }
+
+        public Object leftShift(Object arg) {
+			registerIfClosure(arg, false);
+			return this;
+		}
+		private void registerIfClosure(Object arg, boolean replace) {
+			if(arg instanceof Closure) {
+				Closure callable = (Closure)arg;
+				Class[] paramTypes = callable.getParameterTypes();
+				if(paramTypes == null)paramTypes = ZERO_ARGUMENTS;
+				if(!this.isStatic) {
+					Method foundMethod = checkIfMethodExists(theClass, propertyName, paramTypes, false);
+
+					if(foundMethod != null && !replace) throw new GroovyRuntimeException("Cannot add new method ["+propertyName+"] for arguments ["+DefaultGroovyMethods.inspect(paramTypes)+"]. It already exists!");
+
+					registerInstanceMethod(propertyName, callable);
+				}
+				else {
+					Method foundMethod = checkIfMethodExists(theClass, propertyName, paramTypes, true);
+					if(foundMethod != null && !replace) throw new GroovyRuntimeException("Cannot add new static method ["+propertyName+"] for arguments ["+DefaultGroovyMethods.inspect(paramTypes)+"]. It already exists!");
+
+					registerStaticMethod(propertyName, callable);
+				}
+			}
+		}
+		private Method checkIfMethodExists(Class methodClass, String methodName, Class[] paramTypes, boolean staticMethod) {
+			Method foundMethod = null;
+			Method[] methods = methodClass.getMethods();
+			for (int i = 0; i < methods.length; i++) {
+				if(methods[i].getName().equals(methodName) && Modifier.isStatic(methods[i].getModifiers()) == staticMethod) {
+					if(MetaClassHelper.parametersAreCompatible( paramTypes, methods[i].getParameterTypes() )) {
+						foundMethod = methods[i];
+						break;
+					}
+				}
+			}
+			return foundMethod;
+		}
+		/* (non-Javadoc)
+		 * @see groovy.lang.GroovyObjectSupport#getProperty(java.lang.String)
+		 */
+		public Object getProperty(String property) {
+			this.propertyName = property;
+			return this;
+		}
+		/* (non-Javadoc)
+		 * @see groovy.lang.GroovyObjectSupport#setProperty(java.lang.String, java.lang.Object)
+		 */
+		public void setProperty(String property, Object newValue) {
+			this.propertyName = property;
+			registerIfClosure(newValue, true);
+		}
+
+
+	}
+
+
+	/* (non-Javadoc)
+	 * @see groovy.lang.MetaClassImpl#invokeConstructor(java.lang.Object[])
+	 */
+	public Object invokeConstructor(Object[] arguments) {
+
+		// TODO This is the only area where this MetaClass needs to do some interception because Groovy's current
+		// MetaClass uses hard coded references to the java.lang.reflect.Constructor class so you can't simply
+		// inject Constructor like you can do properties, methods and fields. When Groovy's MetaClassImpl is
+		// refactored we can fix this
+		Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+		MetaMethod method = pickMethod(GROOVY_CONSTRUCTOR, argClasses);
+		if(method!=null && method.getParameterTypes().length == arguments.length) {
+			return method.invoke(theClass, arguments);
+		}
+		return super.invokeConstructor(arguments);
+	}
+
+
+	/**
+	 * Retrieves a list of super classes. Taken from MetaClassImpl. Ideally this method should be protected
+	 *
+	 * @return A list of super classes
+	 */
+   protected LinkedList getSuperClasses() {
+       LinkedList superClasses = new LinkedList();
+       for (Class c = theClass; c!= null; c = c.getSuperclass()) {
+           superClasses.addFirst(c);
+       }
+       if (getJavaClass().isArray() && getJavaClass()!=Object[].class && !getJavaClass().getComponentType().isPrimitive()) {
+           superClasses.addFirst(Object[].class);
+       }
+       return superClasses;
+   }
+	/**
+	 * Handles the ability to use the left shift operator to append new constructors
+	 *
+	 * @author Graeme Rocher
+	 *
+	 */
+	protected class ExpandoMetaConstructor extends GroovyObjectSupport {
+		public Object leftShift(Closure c) {
+			if(c != null) {
+				Class[] paramTypes = c.getParameterTypes();
+				if(paramTypes == null)paramTypes = ZERO_ARGUMENTS;
+
+				Constructor ctor = retrieveConstructor(paramTypes);
+				if(ctor != null) throw new GroovyRuntimeException("Cannot add new constructor for arguments ["+DefaultGroovyMethods.inspect(paramTypes)+"]. It already exists!");
+
+				registerInstanceMethod(GROOVY_CONSTRUCTOR, c);
+			}
+
+			return this;
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see groovy.lang.GroovyObject#getMetaClass()
+	 */
+	public MetaClass getMetaClass() {
+		return myMetaClass;
+	}
+
+
+
+	/* (non-Javadoc)
+	 * @see groovy.lang.GroovyObject#getProperty(java.lang.String)
+	 */
+	public Object getProperty(String property) {
+		if(isValidExpandoProperty(property)) {
+			if(property.equals(STATIC_QUALIFIER)) {
+				return new ExpandoMetaProperty(property, true);
+			}
+			else if(property.equals(CONSTRUCTOR)) {
+				return new ExpandoMetaConstructor();
+			}
+			else {
+                if (myMetaClass.hasProperty(this, property) == null)
+                  return new ExpandoMetaProperty(property);
+                else
+                  return myMetaClass.getProperty(this, property);
+            }
+		}
+		else {
+			return myMetaClass.getProperty(this, property);
+		}
+	}
+
+	private boolean isValidExpandoProperty(String property) {
+        return !property.equals(META_CLASS) &&
+                !property.equals(CLASS) &&
+                !property.equals(META_METHODS) &&
+                !property.equals(METHODS) &&
+                !property.equals(PROPERTIES);
+    }
+
+	/* (non-Javadoc)
+	 * @see groovy.lang.GroovyObject#invokeMethod(java.lang.String, java.lang.Object)
+	 */
+	public Object invokeMethod(String name, Object args) {
+		return myMetaClass.invokeMethod(this, name, args);
+	}
+
+	/* (non-Javadoc)
+	 * @see groovy.lang.GroovyObject#setMetaClass(groovy.lang.MetaClass)
+	 */
+	public void setMetaClass(MetaClass metaClass) {
+		this.myMetaClass = metaClass;
+	}
+
+	/* (non-Javadoc)
+	 * @see groovy.lang.GroovyObject#setProperty(java.lang.String, java.lang.Object)
+	 */
+	public void setProperty(String property, Object newValue) {
+		if(newValue instanceof Closure) {
+			if(property.equals(CONSTRUCTOR)) {
+				property = GROOVY_CONSTRUCTOR;
+			}
+			Closure callable = (Closure)newValue;
+			// here we don't care if the method exists or not we assume the
+			// developer is responsible and wants to override methods where necessary
+			registerInstanceMethod(property, callable);
+
+		}
+		else {
+			registerBeanProperty(property, newValue);
+		}
+	}
+
+
+    
+    protected synchronized void performOperationOnMetaClass(Callable c) {
+            try {
+                if(allowChangesAfterInit) {
+                    setInitialized(false);
+                }
+
+                c.call();
+            }
+            finally {
+                if(initCalled){
+                    setInitialized(true);
+                }
+            }
+	}
+
+	/**
+	 * Registers a new bean property
+	 *
+	 * @param property The property name
+	 * @param newValue The properties initial value
+	 */
+	protected void registerBeanProperty(final String property, final Object newValue) {
+			performOperationOnMetaClass(new Callable() {
+				public void call() {
+					Class type = newValue == null ? Object.class : newValue.getClass();
+
+					MetaBeanProperty mbp = newValue instanceof MetaBeanProperty ? (MetaBeanProperty)newValue : new ThreadManagedMetaBeanProperty(theClass,property,type,newValue);
+
+                    final MetaMethod getter = mbp.getGetter();
+                    final MethodKey getterKey = new DefaultCachedMethodKey(theClass,getter.getName(), CachedClass.EMPTY_ARRAY,false );
+                    final MetaMethod setter = mbp.getSetter();
+                    final MethodKey setterKey = new DefaultCachedMethodKey(theClass,setter.getName(), setter.getParameterTypes(),false );
+                    addMetaMethod(getter);
+                    addMetaMethod(setter);
+
+                    expandoMethods.put(setterKey,setter);
+                    expandoMethods.put(getterKey,getter);
+                    expandoProperties.put(mbp.getName(),mbp);
+
+					addMetaBeanProperty(mbp);
+                    performRegistryCallbacks();
+                }
+
+			});
+	}
+
+	/**
+	 * Registers a new instance method for the given method name and closure on this MetaClass
+	 *
+	 * @param methodName The method name
+	 * @param callable The callable Closure
+	 */
+	protected void registerInstanceMethod(final String methodName, final Closure callable) {
+			final boolean inited = this.initCalled;
+			performOperationOnMetaClass(new Callable() {
+				public void call() {
+					ClosureMetaMethod metaMethod = new ClosureMetaMethod(methodName, theClass,callable);
+                    checkIfGroovyObjectMethod(metaMethod, methodName);
+                    MethodKey key = new DefaultCachedMethodKey(theClass,methodName, metaMethod.getParameterTypes(),false );
+
+
+					addMetaMethod(metaMethod);
+                    dropMethodCache(methodName);
+                    expandoMethods.put(key,metaMethod);
+
+					if(inited && isGetter(methodName, metaMethod.getParameterTypes())) {
+						String propertyName = getPropertyForGetter(methodName);
+						registerBeanPropertyForMethod(metaMethod, propertyName, true, false);
+
+					}
+					else if(inited && isSetter(methodName, metaMethod.getParameterTypes())) {
+						String propertyName = getPropertyForSetter(methodName);
+						registerBeanPropertyForMethod(metaMethod, propertyName, false, false);
+					}
+					performRegistryCallbacks();
+				}
+
+			});
+	}
+
+    /**
+     * Overrides the behaviour of parent getMethods() method to make MetaClass aware of added Expando methods
+     *
+     * @see MetaObjectProtocol#getMethods()
+     *
+     * @return A list of MetaMethods
+     */
+    public List getMethods() {
+        List methodList =  new ArrayList();
+        methodList.addAll(this.expandoMethods.values());
+        methodList.addAll(super.getMethods());
+        return methodList;
+    }
+
+    public List getProperties() {
+        List propertyList = new ArrayList();
+        propertyList.addAll(expandoProperties.values());
+        propertyList.addAll(super.getProperties());
+        return propertyList;
+    }
+
+    /**
+     * Checks if the metaMethod is a method from the GroovyObject interface such as setProperty, getProperty and invokeMethod
+     *
+     * @param metaMethod The metaMethod instance
+     * @param methodName The method name
+     *
+     * @see groovy.lang.GroovyObject
+     */
+    private void checkIfGroovyObjectMethod(ClosureMetaMethod metaMethod, String methodName) {
+        if(isGetPropertyMethod(methodName)) {
+            getPropertyMethod = metaMethod;
+        }
+        else if(isInvokeMethod(methodName, metaMethod)) {
+            invokeMethodMethod = metaMethod;
+        }
+        else if(isSetPropertyMethod(methodName, metaMethod)) {
+            setPropertyMethod = metaMethod;
+        }
+    }
+
+    private boolean isSetPropertyMethod(String methodName, ClosureMetaMethod metaMethod) {
+        return SET_PROPERTY_METHOD.equals(methodName)  && metaMethod.getParameterTypes().length == 2;
+    }
+
+    private boolean isGetPropertyMethod(String methodName) {
+        return GET_PROPERTY_METHOD.equals(methodName);
+    }
+
+    private boolean isInvokeMethod(String methodName, ClosureMetaMethod metaMethod) {
+        return INVOKE_METHOD_METHOD.equals(methodName) && metaMethod.getParameterTypes().length == 2;
+    }
+
+
+    private void performRegistryCallbacks() {
+		MetaClassRegistry registry =  GroovySystem.getMetaClassRegistry();
+		if(!modified) {
+			modified = true;
+            // Implementation note: By default Groovy uses soft references to store MetaClass
+            // this insures the registry doesn't grow and get out of hand. By doing this we're
+            // saying this this EMC will be a hard reference in the registry. As we're only
+            // going have a small number of classes that have modified EMC this is ok
+            if(inRegistry) {
+                MetaClass currMetaClass = registry.getMetaClass(theClass);
+                if(!(currMetaClass instanceof ExpandoMetaClass) && currMetaClass instanceof AdaptingMetaClass) {
+                    ((AdaptingMetaClass)currMetaClass).setAdaptee(this);
+                } else {
+                    registry.setMetaClass(theClass, this);
+                }
+            }
+
+		}
+		// Implementation note: EMC handles most cases by itself except for the case where yuou
+		// want to call a dynamically injected method registered with a parent on a child class
+		// For this to work the MetaClassRegistry needs to have an ExpandoMetaClassCreationHandle
+		// What this does is ensure that EVERY class created in the registry uses an EMC
+		// Then when an EMC changes it reports back to the EMCCreationHandle which will
+		// tell child classes of this class to re-inherit their methods
+		if(registry.getMetaClassCreationHandler() instanceof ExpandoMetaClassCreationHandle) {
+			ExpandoMetaClassCreationHandle creationHandler = (ExpandoMetaClassCreationHandle)registry.getMetaClassCreationHandler();
+			if(!creationHandler.hasModifiedMetaClass(this))
+				creationHandler.registerModifiedMetaClass(this);
+
+        }
+	}
+
+
+	private void registerBeanPropertyForMethod(MetaMethod metaMethod, String propertyName, boolean getter, boolean isStatic) {
+        Map propertyCache = isStatic ? staticBeanPropertyCache : beanPropertyCache;
+        MetaBeanProperty beanProperty = (MetaBeanProperty)propertyCache.get(propertyName);
+        if(beanProperty == null) {
+            if(getter)
+                beanProperty = new MetaBeanProperty(propertyName,Object.class,metaMethod,null);
+            else
+                beanProperty = new MetaBeanProperty(propertyName,Object.class,null,metaMethod);
+
+            propertyCache.put(propertyName, beanProperty);
+        }
+        else {
+            if(getter) {
+                MetaMethod setterMethod = beanProperty.getSetter();
+                Class type = setterMethod != null ? setterMethod.getParameterTypes()[0].getCachedClass() : Object.class;
+                beanProperty = new MetaBeanProperty(propertyName,type,metaMethod,setterMethod);
+                propertyCache.put(propertyName, beanProperty);
+            }else {
+                MetaMethod getterMethod = beanProperty.getGetter();
+                beanProperty = new MetaBeanProperty(propertyName, metaMethod.getParameterTypes()[0].getCachedClass(),getterMethod,metaMethod);
+                propertyCache .put(propertyName, beanProperty);
+            }
+        }
+         expandoProperties.put(beanProperty.getName(),beanProperty);
+        addMetaBeanProperty(beanProperty);
+	}
+
+	/**
+	 * Registers a new static method for the given method name and closure on this MetaClass
+	 *
+	 * @param name The method name
+	 * @param callable The callable Closure
+	 */
+	protected void registerStaticMethod(final String name, final Closure callable) {
+		performOperationOnMetaClass(new Callable() {
+			public void call() {
+                String methodName;
+                if(name.equals(METHOD_MISSING))
+                    methodName = STATIC_METHOD_MISSING;
+                else if(name.equals(PROPERTY_MISSING))
+                    methodName = STATIC_PROPERTY_MISSING;
+                else
+                    methodName = name;
+
+                ClosureStaticMetaMethod metaMethod = new ClosureStaticMetaMethod(methodName, theClass,callable);
+                if(methodName.equals(INVOKE_METHOD_METHOD) && callable.getParameterTypes().length == 2) {
+                    invokeStaticMethodMethod = metaMethod;
+                }
+                else {
+                    if(methodName.equals(METHOD_MISSING)) {
+                        methodName = STATIC_METHOD_MISSING;
+                    }
+                    MethodKey key = new DefaultCachedMethodKey(theClass,methodName, metaMethod.getParameterTypes(), false );
+
+                    addMetaMethod(metaMethod);
+                    dropStaticMethodCache (methodName);
+                    cacheStaticMethod(key,metaMethod);
+
+                    if(isGetter(methodName, metaMethod.getParameterTypes())) {
+                        String propertyName = getPropertyForGetter(methodName);
+                        registerBeanPropertyForMethod(metaMethod, propertyName, true, true);
+
+                    }
+                    else if(isSetter(methodName, metaMethod.getParameterTypes())) {
+                        String propertyName = getPropertyForSetter(methodName);
+                        registerBeanPropertyForMethod(metaMethod, propertyName, false, true);
+                    }
+                    performRegistryCallbacks();
+                    expandoMethods.put(key,metaMethod);
+                }
+			}
+
+		});
+	}
+
+    /**
+	 * @return The Java class enhanced by this MetaClass
+	 */
+	public Class getJavaClass() {
+		return theClass;
+	}
+
+	/**
+	 * Called from ExpandoMetaClassCreationHandle in the registry if it exists to setup inheritance
+	 * handling
+	 *
+	 * @param modifiedSuperExpandos A list of modified super ExpandoMetaClass
+	 */
+	public void refreshInheritedMethods(Set modifiedSuperExpandos) {
+		for (Iterator i = modifiedSuperExpandos.iterator(); i.hasNext();) {
+
+			ExpandoMetaClass superExpando = (ExpandoMetaClass) i.next();
+            if(superExpando != this) {
+                List metaMethods = superExpando.getExpandoMethods();
+                for (Iterator j = metaMethods.iterator(); j.hasNext();) {
+                    MetaMethod metaMethod = (MetaMethod) j.next();
+                    if(metaMethod.isStatic()) continue; // don't inherit static methodsw
+
+                    addSuperMethodIfNotOverriden(metaMethod);
+                }
+                Collection metaProperties = superExpando.getExpandoProperties();
+                for (Iterator j = metaProperties.iterator(); j.hasNext();) {
+                    MetaBeanProperty property = (MetaBeanProperty) j.next();
+                    expandoProperties.put(property.getName(),property);
+                    addMetaBeanProperty(property);
+                }
+            }
+
+        }
+	}
+
+
+	/**
+	 * Returns a list of expando MetaMethod instances added to this ExpandoMetaClass
+	 *
+	 * @return the expandoMethods
+	 */
+	public List getExpandoMethods() {
+       return Collections.unmodifiableList(DefaultGroovyMethods.toList(expandoMethods.values()));
+    }
+
+
+	/**
+	 * Returns a list of MetaBeanProperty instances added to this ExpandoMetaClass
+	 *
+	 * @return the expandoProperties
+	 */
+	public Collection getExpandoProperties() {
+        return Collections.unmodifiableCollection(expandoProperties.values());
+    }
+
+    /**
+     * Overrides default implementation just in case invokeMethod has been overriden by ExpandoMetaClass
+     *
+     * @see groovy.lang.MetaClassImpl#invokeMethod(Class, Object, String, Object[], boolean, boolean)
+     */
+    public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
+        if(invokeMethodMethod!=null) {
+            return invokeMethodMethod.invoke(object, new Object[]{methodName, originalArguments});
+        }
+        return super.invokeMethod(sender, object, methodName, originalArguments, isCallToSuper, fromInsideClass);
+    }
+
+    /**
+     * Overrides default implementation just in case a static invoke method has been set on ExpandoMetaClass
+     * @see MetaClassImpl#invokeStaticMethod(Object, String, Object[])
+     */
+    public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
+        if(invokeStaticMethodMethod != null) {
+            return invokeStaticMethodMethod.invoke(object, new Object[]{methodName, arguments});
+        }
+        return super.invokeStaticMethod(object, methodName, arguments);
+    }
+
+    /**
+     * Overrides default implementation just in case getProperty method has been overriden by ExpandoMetaClass
+     *
+     * @see MetaClassImpl#getProperty(Class, Object, String, boolean, boolean)
+     */
+    public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
+        if(hasOverrideGetProperty(name) && getJavaClass().isInstance(object)) {
+            return getPropertyMethod.invoke(object, new Object[]{name});
+        }
+        return super.getProperty(sender, object, name, useSuper, fromInsideClass);
+    }
+
+
+    /**
+     * Overrides default implementation just in case getProperty method has been overriden by ExpandoMetaClass
+     *
+     * @see MetaClassImpl#getProperty(Object, String)
+     */
+    public Object getProperty(Object object, String name) {
+        if(hasOverrideGetProperty(name) && getJavaClass().isInstance(object)) {
+            return getPropertyMethod.invoke(object, new Object[]{name});
+        }
+        return super.getProperty(object,name);
+    }
+
+    private boolean hasOverrideGetProperty(String name) {
+        return getPropertyMethod != null && !name.equals(META_CLASS_PROPERTY)&& !name.equals(CLASS_PROPERTY);
+    }
+
+    /**
+     * Overrides default implementation just in case setProperty method has been overriden by ExpandoMetaClass
+     *
+     * @see MetaClassImpl#setProperty(Class, Object, String, Object, boolean, boolean)
+     */
+
+    public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
+        if(setPropertyMethod!=null  && !name.equals(META_CLASS_PROPERTY) && getJavaClass().isInstance(object)) {
+            setPropertyMethod.invoke(object, new Object[]{name, newValue});
+            return;
+        }
+        super.setProperty(sender, object, name, newValue, useSuper, fromInsideClass);
+    }
+
+    /**
+     * Looks up an existing MetaProperty by name
+     *
+     * @param name The name of the MetaProperty
+     * @return The MetaProperty or null if it doesn't exist
+     */
+    public MetaProperty getMetaProperty(String name) {
+        MetaProperty mp = (MetaProperty) this.expandoProperties.get(name);
+        if (mp != null) return mp;
+        return super.getMetaProperty(name);
+    }
+
+    /**
+     * Returns true if the MetaClass has the given property
+     *
+     * @param name The name of the MetaProperty
+     * @return True it exists as a MetaProperty
+     */
+    public boolean hasMetaProperty(String name) {
+        return getMetaProperty(name) != null;
+    }
+
+    /**
+     * Checks whether a MetaMethod for the given name and arguments exists
+     *
+     * @param name The name of the MetaMethod
+     * @param args The arguments to the meta method
+     * @return True if the method exists otherwise null
+     */
+    public boolean hasMetaMethod(String name, Class[] args) {
+        return super.pickMethod(name, args) != null;
+    }
+
+
+    /**
+     * Returns true if the name of the method specified and the number of arguments make it a javabean property
+     *
+     * @param name True if its a Javabean property
+     * @param args The arguments
+     * @return True if it is a javabean property method
+     */
+    private boolean isGetter(String name, CachedClass[] args) {
+        if(name == null || name.length() == 0 || args == null)return false;
+        if(args.length != 0)return false;
+
+        if(name.startsWith("get")) {
+            name = name.substring(3);
+            if(name.length() > 0 && Character.isUpperCase(name.charAt(0))) return true;
+        }
+        else if(name.startsWith("is")) {
+            name = name.substring(2);
+            if(name.length() > 0 && Character.isUpperCase(name.charAt(0))) return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns a property name equivalent for the given getter name or null if it is not a getter
+     *
+     * @param getterName The getter name
+     * @return The property name equivalent
+     */
+    private String getPropertyForGetter(String getterName) {
+        if(getterName == null || getterName.length() == 0)return null;
+
+        if(getterName.startsWith("get")) {
+            String prop = getterName.substring(3);
+            return convertPropertyName(prop);
+        }
+        else if(getterName.startsWith("is")) {
+            String prop = getterName.substring(2);
+            return convertPropertyName(prop);
+        }
+        return null;
+    }
+
+	private String convertPropertyName(String prop) {
+		if(Character.isUpperCase(prop.charAt(0)) && (prop.length() > 1 && Character.isUpperCase(prop.charAt(1)))) {
+			return prop;
+		}
+		else if(Character.isDigit(prop.charAt(0))) {
+			return prop;
+		}
+		else {
+			return Character.toLowerCase(prop.charAt(0)) + (prop.length() > 1 ? prop.substring(1) : "");
+		}
+	}
+
+    /**
+     * Returns a property name equivalent for the given setter name or null if it is not a getter
+     *
+     * @param setterName The setter name
+     * @return The property name equivalent
+     */
+    public String getPropertyForSetter(String setterName) {
+        if(setterName == null || setterName.length() == 0)return null;
+
+        if(setterName.startsWith("set")) {
+            String prop = setterName.substring(3);
+            return convertPropertyName(prop);
+        }
+        return null;
+    }
+
+    public boolean isSetter(String name, CachedClass[] args) {
+        if(name == null || name.length() == 0 || args == null)return false;
+
+        if(name.startsWith("set")) {
+            if(args.length != 1) return false;
+            name = name.substring(3);
+            if(name.length() > 0 && Character.isUpperCase(name.charAt(0))) return true;
+        }
+
+        return false;
+    }
+
+
+}
+
diff --git a/groovy/src/main/groovy/lang/ExpandoMetaClassCreationHandle.java b/groovy/src/main/groovy/lang/ExpandoMetaClassCreationHandle.java
new file mode 100644
index 0000000..ed3ae36
--- /dev/null
+++ b/groovy/src/main/groovy/lang/ExpandoMetaClassCreationHandle.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import groovy.lang.MetaClassRegistry.MetaClassCreationHandle;
+import org.codehaus.groovy.runtime.metaclass.ConcurrentReaderHashMap;
+
+import java.util.*;
+
+/**
+ * <p>A handle for the MetaClassRegistry that changes all classes loaded into the Grails VM
+ * to use ExpandoMetaClass instances
+ *
+ * <p>The handle should be registered with the Groovy runtime <strong>before</before> Groovy loads, for example
+ * in your main method.
+ *
+ * <code>GroovySystem.metaClassRegistry.metaClassCreationHandle = new ExpandoMetaClassCreationHandle()</code>
+ *
+ * @see groovy.lang.MetaClassRegistry
+ * @see groovy.lang.MetaClassRegistry.MetaClassCreationHandle
+ * @see org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl#setMetaClassCreationHandle(groovy.lang.MetaClassRegistry.MetaClassCreationHandle)
+ *
+ * @author Graeme Rocher
+ * @since 1.1
+ */
+public class ExpandoMetaClassCreationHandle extends MetaClassCreationHandle {
+
+    public static final ExpandoMetaClassCreationHandle instance = new ExpandoMetaClassCreationHandle();
+
+	private final Map modifiedExpandos = new ConcurrentReaderHashMap();
+
+
+	/* (non-Javadoc)
+	 * @see groovy.lang.MetaClassRegistry.MetaClassCreationHandle#create(java.lang.Class, groovy.lang.MetaClassRegistry)
+	 */
+	protected MetaClass createNormalMetaClass(Class theClass, MetaClassRegistry registry) {
+		if(theClass != ExpandoMetaClass.class) {
+			ExpandoMetaClass emc = new ExpandoMetaClass(theClass, false ,true);
+			Set modifiedSuperExpandos = retrieveModifiedSuperExpandos(emc);
+            emc.refreshInheritedMethods(modifiedSuperExpandos);
+			return emc;
+		}
+		else {
+			return super.create(theClass, registry);
+		}
+	}
+
+    /*
+     * Looks for modified super class ExpandoMetaClass instances for the given child ExpandoMetaClass
+     */
+    private Set retrieveModifiedSuperExpandos(ExpandoMetaClass child) {
+		Set modifiedSupers = new HashSet();
+		List superClasses = child.getSuperClasses();
+		for (Iterator i = superClasses.iterator(); i.hasNext();) {
+			Class c = (Class) i.next();
+            Class[] interfaces = c.getInterfaces();
+            populateSupersFromInterfaces(modifiedSupers, interfaces);
+            if(modifiedExpandos.containsKey(c)) {
+				modifiedSupers.add(modifiedExpandos.get(c));
+			}
+		}
+        Class[] interfaces = child.getJavaClass().getInterfaces();
+        populateSupersFromInterfaces(modifiedSupers, interfaces);
+        return modifiedSupers;
+	}
+
+    /*
+     * Searches through a given array of interfaces for modified ExpandoMetaClass instances for each interface
+     */
+    private void populateSupersFromInterfaces(Set modifiedSupers, Class[] interfaces) {
+        for (int i = 0; i < interfaces.length; i++) {
+            Class anInterface = interfaces[i];
+            final Class[] superInterfaces = anInterface.getInterfaces();
+            if(modifiedExpandos.containsKey(anInterface)) {
+                modifiedSupers.add(modifiedExpandos.get(anInterface));
+            }
+            if(superInterfaces.length > 0)
+                populateSupersFromInterfaces(modifiedSupers, superInterfaces);
+        }
+    }
+
+
+    /**
+     * Registers a modified ExpandoMetaClass with the creation handle
+     *
+     * @param emc The EMC
+     */
+    public void registerModifiedMetaClass(ExpandoMetaClass emc) {
+        modifiedExpandos.put(emc.getJavaClass(), emc);
+	}
+
+	public boolean hasModifiedMetaClass(ExpandoMetaClass emc) {
+		return modifiedExpandos.containsKey(emc.getJavaClass());
+	}
+
+    /**
+     * <p>Enables the ExpandoMetaClassCreationHandle with the registry
+     *
+     * <code>ExpandoMetaClassCreationHandle.enable();</code>
+     *
+     */
+    public static void enable() {
+        final MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry();
+        if (metaClassRegistry.getMetaClassCreationHandler() != instance) {
+           instance.modifiedExpandos.clear();
+           metaClassRegistry.setMetaClassCreationHandle(instance);
+        }
+    }
+
+    public static void disable() {
+        final MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry();
+        if (metaClassRegistry.getMetaClassCreationHandler() == instance) {
+            instance.modifiedExpandos.clear();
+            metaClassRegistry.setMetaClassCreationHandle(new MetaClassCreationHandle());
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/lang/GString.java b/groovy/src/main/groovy/lang/GString.java
new file mode 100644
index 0000000..3c8e8e2
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GString.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * Represents a String which contains embedded values such as "hello there
+ * ${user} how are you?" which can be evaluated lazily. Advanced users can
+ * iterate over the text and values to perform special processing, such as for
+ * performing SQL operations, the values can be substituted for ? and the
+ * actual value objects can be bound to a JDBC statement. The lovely name of
+ * this class was suggested by Jules Gosnell and was such a good idea, I
+ * couldn't resist :)
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class GString extends GroovyObjectSupport implements Comparable, CharSequence, Writable, Buildable {
+
+    private Object[] values;
+
+    public GString(Object values) {
+        this.values = (Object[]) values;
+    }
+
+    public GString(Object[] values) {
+        this.values = values;
+    }
+
+    // will be static in an instance
+    public abstract String[] getStrings();
+
+    /**
+     * Overloaded to implement duck typing for Strings 
+     * so that any method that can't be evaluated on this
+     * object will be forwarded to the toString() object instead.
+     */
+    public Object invokeMethod(String name, Object args) {
+        try {
+            return super.invokeMethod(name, args);
+        }
+        catch (MissingMethodException e) {
+            // lets try invoke the method on the real String
+            return InvokerHelper.invokeMethod(toString(), name, args);
+        }
+    }
+
+    public Object[] getValues() {
+        return values;
+    }
+
+    public GString plus(GString that) {
+        List stringList = new ArrayList();
+        List valueList = new ArrayList();
+
+        stringList.addAll(Arrays.asList(getStrings()));
+        valueList.addAll(Arrays.asList(getValues()));
+
+        if (stringList.size() > valueList.size()) {
+            valueList.add("");
+        }
+
+        stringList.addAll(Arrays.asList(that.getStrings()));
+        valueList.addAll(Arrays.asList(that.getValues()));
+
+        final String[] newStrings = new String[stringList.size()];
+        stringList.toArray(newStrings);
+        Object[] newValues = valueList.toArray();
+
+        return new GString(newValues) {
+            public String[] getStrings() {
+                return newStrings;
+            }
+        };
+    }
+
+    public GString plus(String that) {
+        String[] currentStrings = getStrings();
+        String[] newStrings = null;
+        Object[] newValues = null;
+
+        newStrings = new String[currentStrings.length + 1];
+        newValues = new Object[getValues().length + 1];
+        int lastIndex = currentStrings.length;
+        System.arraycopy(currentStrings, 0, newStrings, 0, lastIndex);
+        System.arraycopy(getValues(), 0, newValues, 0, getValues().length);
+        newStrings[lastIndex] = that;
+        newValues[getValues().length] = "";
+
+        final String[] finalStrings = newStrings;
+        return new GString(newValues) {
+
+            public String[] getStrings() {
+                return finalStrings;
+            }
+        };
+    }
+
+    public int getValueCount() {
+        return values.length;
+    }
+
+    public Object getValue(int idx) {
+        return values[idx];
+    }
+
+    public String toString() {
+        StringWriter buffer = new StringWriter();
+        try {
+            writeTo(buffer);
+        }
+        catch (IOException e) {
+            throw new StringWriterIOException(e);
+        }
+        return buffer.toString();
+    }
+
+    public Writer writeTo(Writer out) throws IOException {
+    	String[] s = getStrings();
+    	int numberOfValues = values.length;
+    	for (int i = 0, size = s.length; i < size; i++) {
+    		out.write(s[i]);
+    		if (i < numberOfValues) {
+    			final Object value = values[i];
+
+    			if (value instanceof Closure) {
+    				final Closure c = (Closure)value;
+
+    				if (c.getMaximumNumberOfParameters() == 0) {
+    					InvokerHelper.write(out, c.call(null));
+    				} else if (c.getMaximumNumberOfParameters() == 1) {
+    					c.call(new Object[]{out});
+    				} else {
+    					throw new GroovyRuntimeException("Trying to evaluate a GString containing a Closure taking "
+    							+ c.getMaximumNumberOfParameters() + " parameters");
+    				}
+    			} else {
+    				InvokerHelper.write(out, value);
+    			}
+    		}
+    	}
+    	return out;
+    }
+
+    /* (non-Javadoc)
+     * @see groovy.lang.Buildable#build(groovy.lang.GroovyObject)
+     */
+    public void build(final GroovyObject builder) {
+    final String[] s = getStrings();
+    final int numberOfValues = values.length;
+        
+        for (int i = 0, size = s.length; i < size; i++) {
+            builder.getProperty("mkp");
+            builder.invokeMethod("yield", new Object[]{s[i]});
+            if (i < numberOfValues) {
+                builder.getProperty("mkp");
+                builder.invokeMethod("yield", new Object[]{values[i]});
+            }
+        }
+    }
+
+    public boolean equals(Object that) {
+        if (that instanceof GString) {
+            return equals((GString) that);
+        }
+        return false;
+    }
+
+    public boolean equals(GString that) {
+        return toString().equals(that.toString());
+    }
+
+    public int hashCode() {
+        return 37 + toString().hashCode();
+    }
+
+    public int compareTo(Object that) {
+        return toString().compareTo(that.toString());
+    }
+
+    public char charAt(int index) {
+        return toString().charAt(index);
+    }
+
+    public int length() {
+        return toString().length();
+    }
+
+    public CharSequence subSequence(int start, int end) {
+        return toString().subSequence(start, end);
+    }
+
+    /**
+     * Turns a String into a regular expression pattern
+     *
+     * @return the regular expression pattern
+     */
+    public Pattern negate() {
+        return DefaultGroovyMethods.bitwiseNegate(toString());
+    }
+}
diff --git a/groovy/src/main/groovy/lang/GroovyClassLoader.java b/groovy/src/main/groovy/lang/GroovyClassLoader.java
new file mode 100644
index 0000000..8adf7b7
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GroovyClassLoader.java
@@ -0,0 +1,849 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @TODO: multi threaded compiling of the same class but with different roots
+ * for compilation... T1 compiles A, which uses B, T2 compiles B... mark A and B
+ * as parsed and then synchronize compilation. Problems: How to synchronize? 
+ * How to get error messages?   
+ *
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.control.*;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import java.io.*;
+import java.net.*;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
+import java.util.*;
+
+/**
+ * A ClassLoader which can load Groovy classes. The loaded classes are cached,
+ * classes from other classlaoders should not be cached. To be able to load a
+ * script that was asked for earlier but was created later it is essential not
+ * to keep anything like a "class not found" information for that class name.
+ * This includes possible parent loaders. Classes that are not chached are always
+ * reloaded.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Guillaume Laforge
+ * @author Steve Goetze
+ * @author Bing Ran
+ * @author <a href="mailto:scottstirling@rcn.com">Scott Stirling</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @version $Revision$
+ */
+public class GroovyClassLoader extends URLClassLoader {
+
+    /**
+     * this cache contains the loaded classes or PARSING, if the class is currently parsed
+     */
+    protected final Map classCache = new HashMap();
+    protected final Map sourceCache = new HashMap();
+    private final CompilerConfiguration config;
+    private Boolean recompile;
+    // use 1000000 as offset to avoid conflicts with names form the GroovyShell 
+    private static int scriptNameCounter = 1000000;
+
+    private GroovyResourceLoader resourceLoader = new GroovyResourceLoader() {
+        public URL loadGroovySource(final String filename) throws MalformedURLException {
+            URL file = (URL) AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return getSourceFile(filename);
+                }
+            });
+            return file;
+        }
+    };
+
+    /**
+     * creates a GroovyClassLoader using the current Thread's context
+     * Class loader as parent.
+     */
+    public GroovyClassLoader() {
+        this(Thread.currentThread().getContextClassLoader());
+    }
+
+    /**
+     * creates a GroovyClassLoader using the given ClassLoader as parent
+     */
+    public GroovyClassLoader(ClassLoader loader) {
+        this(loader, null);
+    }
+
+    /**
+     * creates a GroovyClassLoader using the given GroovyClassLoader as parent.
+     * This loader will get the parent's CompilerConfiguration
+     */
+    public GroovyClassLoader(GroovyClassLoader parent) {
+        this(parent, parent.config, false);
+    }
+
+    /**
+     * creates a GroovyClassLaoder.
+     *
+     * @param parent                    the parten class loader
+     * @param config                    the compiler configuration
+     * @param useConfigurationClasspath determines if the configurations classpath should be added
+     */
+    public GroovyClassLoader(ClassLoader parent, CompilerConfiguration config, boolean useConfigurationClasspath) {
+        super(new URL[0], parent);
+        if (config == null) config = CompilerConfiguration.DEFAULT;
+        this.config = config;
+        if (useConfigurationClasspath) {
+            for (Iterator it = config.getClasspath().iterator(); it.hasNext();) {
+                String path = (String) it.next();
+                this.addClasspath(path);
+            }
+        }
+    }
+
+    /**
+     * creates a GroovyClassLoader using the given ClassLoader as parent.
+     */
+    public GroovyClassLoader(ClassLoader loader, CompilerConfiguration config) {
+        this(loader, config, true);
+    }
+
+    public void setResourceLoader(GroovyResourceLoader resourceLoader) {
+        if (resourceLoader == null) {
+            throw new IllegalArgumentException("Resource loader must not be null!");
+        }
+        this.resourceLoader = resourceLoader;
+    }
+
+    public GroovyResourceLoader getResourceLoader() {
+        return resourceLoader;
+    }
+
+    /**
+     * Loads the given class node returning the implementation Class
+     *
+     * @param classNode
+     * @return a class
+     * @deprecated
+     */
+    public Class defineClass(ClassNode classNode, String file) {
+        //return defineClass(classNode, file, "/groovy/defineClass");
+        throw new DeprecationException("the method GroovyClassLoader#defineClass(ClassNode, String) is no longer used and removed");
+    }
+
+    /**
+     * Loads the given class node returning the implementation Class.
+     * <p/>
+     * WARNING: this compilation is not synchronized
+     *
+     * @param classNode
+     * @return a class
+     */
+    public Class defineClass(ClassNode classNode, String file, String newCodeBase) {
+        CodeSource codeSource = null;
+        try {
+            codeSource = new CodeSource(new URL("file", "", newCodeBase), (java.security.cert.Certificate[]) null);
+        } catch (MalformedURLException e) {
+            //swallow
+        }
+
+        CompilationUnit unit = createCompilationUnit(config, codeSource);
+        ClassCollector collector = createCollector(unit, classNode.getModule().getContext());
+        try {
+            unit.addClassNode(classNode);
+            unit.setClassgenCallback(collector);
+            unit.compile(Phases.CLASS_GENERATION);
+
+            return collector.generatedClass;
+        } catch (CompilationFailedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Parses the given file into a Java class capable of being run
+     *
+     * @param file the file name to parse
+     * @return the main class defined in the given script
+     */
+    public Class parseClass(File file) throws CompilationFailedException, IOException {
+        return parseClass(new GroovyCodeSource(file));
+    }
+
+    /**
+     * Parses the given text into a Java class capable of being run
+     *
+     * @param text     the text of the script/class to parse
+     * @param fileName the file name to use as the name of the class
+     * @return the main class defined in the given script
+     */
+    public Class parseClass(String text, String fileName) throws CompilationFailedException {
+        return parseClass(new ByteArrayInputStream(text.getBytes()), fileName);
+    }
+
+    /**
+     * Parses the given text into a Java class capable of being run
+     *
+     * @param text the text of the script/class to parse
+     * @return the main class defined in the given script
+     */
+    public Class parseClass(String text) throws CompilationFailedException {
+        return parseClass(new ByteArrayInputStream(text.getBytes()), "script" + System.currentTimeMillis() + ".groovy");
+    }
+
+    /**
+     * Parses the given character stream into a Java class capable of being run
+     *
+     * @param in an InputStream
+     * @return the main class defined in the given script
+     */
+    public Class parseClass(InputStream in) throws CompilationFailedException {
+        return parseClass(in, generateScriptName());
+    }
+
+    public synchronized String generateScriptName() {
+        scriptNameCounter++;
+        return "script" + scriptNameCounter + ".groovy";
+    }
+
+    public Class parseClass(final InputStream in, final String fileName) throws CompilationFailedException {
+        // For generic input streams, provide a catch-all codebase of
+        // GroovyScript
+        // Security for these classes can be administered via policy grants with
+        // a codebase of file:groovy.script
+        GroovyCodeSource gcs = (GroovyCodeSource) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return new GroovyCodeSource(in, fileName, "/groovy/script");
+            }
+        });
+        return parseClass(gcs);
+    }
+
+
+    public Class parseClass(GroovyCodeSource codeSource) throws CompilationFailedException {
+        return parseClass(codeSource, codeSource.isCachable());
+    }
+
+    /**
+     * Parses the given code source into a Java class. If there is a class file
+     * for the given code source, then no parsing is done, instead the cached class is returned.
+     *
+     * @param shouldCacheSource if true then the generated class will be stored in the source cache
+     * @return the main class defined in the given script
+     */
+    public Class parseClass(GroovyCodeSource codeSource, boolean shouldCacheSource) throws CompilationFailedException {
+        synchronized (this) {
+            Class answer = (Class) sourceCache.get(codeSource.getName());
+            if (answer != null) return answer;
+
+            // Was neither already loaded nor compiling, so compile and add to
+            // cache.
+            CompilationUnit unit = createCompilationUnit(config, codeSource.getCodeSource());
+            SourceUnit su = null;
+            if (codeSource.getFile() == null) {
+                su = unit.addSource(codeSource.getName(), codeSource.getInputStream());
+            } else {
+                su = unit.addSource(codeSource.getFile());
+            }
+
+            ClassCollector collector = createCollector(unit, su);
+            unit.setClassgenCallback(collector);
+            int goalPhase = Phases.CLASS_GENERATION;
+            if (config != null && config.getTargetDirectory() != null) goalPhase = Phases.OUTPUT;
+            unit.compile(goalPhase);
+
+            answer = collector.generatedClass;
+            for (Iterator iter = collector.getLoadedClasses().iterator(); iter.hasNext();) {
+                Class clazz = (Class) iter.next();
+                setClassCacheEntry(clazz);
+            }
+            if (shouldCacheSource) sourceCache.put(codeSource.getName(), answer);
+            return answer;
+        }
+    }
+
+    /**
+     * gets the currently used classpath.
+     *
+     * @return a String[] containing the file information of the urls
+     * @see #getURLs()
+     */
+    protected String[] getClassPath() {
+        //workaround for Groovy-835
+        URL[] urls = getURLs();
+        String[] ret = new String[urls.length];
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = urls[i].getFile();
+        }
+        return ret;
+    }
+
+    /**
+     * expands the classpath
+     *
+     * @param pathList  an empty list that will contain the elements of the classpath
+     * @param classpath the classpath specified as a single string
+     * @deprecated
+     */
+    protected void expandClassPath(List pathList, String base, String classpath, boolean isManifestClasspath) {
+        throw new DeprecationException("the method groovy.lang.GroovyClassLoader#expandClassPath(List,String,String,boolean) is no longer used internally and removed");
+    }
+
+    /**
+     * A helper method to allow bytecode to be loaded. spg changed name to
+     * defineClass to make it more consistent with other ClassLoader methods
+     *
+     * @deprecated
+     */
+    protected Class defineClass(String name, byte[] bytecode, ProtectionDomain domain) {
+        throw new DeprecationException("the method groovy.lang.GroovyClassLoader#defineClass(String,byte[],ProtectionDomain) is no longer used internally and removed");
+    }
+
+    public static class InnerLoader extends GroovyClassLoader {
+        private final GroovyClassLoader delegate;
+
+        public InnerLoader(GroovyClassLoader delegate) {
+            super(delegate);
+            this.delegate = delegate;
+        }
+
+        public void addClasspath(String path) {
+            delegate.addClasspath(path);
+        }
+
+        public void clearCache() {
+            delegate.clearCache();
+        }
+
+        public URL findResource(String name) {
+            return delegate.findResource(name);
+        }
+
+        public Enumeration findResources(String name) throws IOException {
+            return delegate.findResources(name);
+        }
+
+        public Class[] getLoadedClasses() {
+            return delegate.getLoadedClasses();
+        }
+
+        public URL getResource(String name) {
+            return delegate.getResource(name);
+        }
+
+        public InputStream getResourceAsStream(String name) {
+            return delegate.getResourceAsStream(name);
+        }
+
+        public GroovyResourceLoader getResourceLoader() {
+            return delegate.getResourceLoader();
+        }
+
+        public URL[] getURLs() {
+            return delegate.getURLs();
+        }
+
+        public Class loadClass(String name, boolean lookupScriptFiles, boolean preferClassOverScript, boolean resolve) throws ClassNotFoundException, CompilationFailedException {
+            Class c = findLoadedClass(name);
+            if (c != null) return c;
+            return delegate.loadClass(name, lookupScriptFiles, preferClassOverScript, resolve);
+        }
+
+        public Class parseClass(GroovyCodeSource codeSource, boolean shouldCache) throws CompilationFailedException {
+            return delegate.parseClass(codeSource, shouldCache);
+        }
+
+        public void setResourceLoader(GroovyResourceLoader resourceLoader) {
+            delegate.setResourceLoader(resourceLoader);
+        }
+
+        public void addURL(URL url) {
+            delegate.addURL(url);
+        }
+    }
+
+    /**
+     * creates a new CompilationUnit. If you want to add additional
+     * phase operations to the CompilationUnit (for example to inject
+     * additional methods, variables, fields), then you should overwrite
+     * this method.
+     *
+     * @param config the compiler configuration, usually the same as for this class loader
+     * @param source the source containing the initial file to compile, more files may follow during compilation
+     * @return the CompilationUnit
+     */
+    protected CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource source) {
+        return new CompilationUnit(config, source, this);
+    }
+
+    /**
+     * creates a ClassCollector for a new compilation.
+     *
+     * @param unit the compilationUnit
+     * @param su   the SoruceUnit
+     * @return the ClassCollector
+     */
+    protected ClassCollector createCollector(CompilationUnit unit, SourceUnit su) {
+        InnerLoader loader = (InnerLoader) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return new InnerLoader(GroovyClassLoader.this);
+            }
+        });
+        return new ClassCollector(loader, unit, su);
+    }
+
+    public static class ClassCollector extends CompilationUnit.ClassgenCallback {
+        private Class generatedClass;
+        private final GroovyClassLoader cl;
+        private final SourceUnit su;
+        private final CompilationUnit unit;
+        private final Collection loadedClasses;
+
+        protected ClassCollector(InnerLoader cl, CompilationUnit unit, SourceUnit su) {
+            this.cl = cl;
+            this.unit = unit;
+            this.loadedClasses = new ArrayList();
+            this.su = su;
+        }
+
+        protected GroovyClassLoader getDefiningClassLoader() {
+            return cl;
+        }
+
+        protected Class createClass(byte[] code, ClassNode classNode) {
+            GroovyClassLoader cl = getDefiningClassLoader();
+            Class theClass = cl.defineClass(classNode.getName(), code, 0, code.length, unit.getAST().getCodeSource());
+            //cl.resolveClass(theClass);
+            this.loadedClasses.add(theClass);
+
+            if (generatedClass == null) {
+                ModuleNode mn = classNode.getModule();
+                SourceUnit msu = null;
+                if (mn != null) msu = mn.getContext();
+                ClassNode main = null;
+                if (mn != null) main = (ClassNode) mn.getClasses().get(0);
+                if (msu == su && main == classNode) generatedClass = theClass;
+            }
+
+            return theClass;
+        }
+
+        protected Class onClassNode(ClassWriter classWriter, ClassNode classNode) {
+            byte[] code = classWriter.toByteArray();
+            return createClass(code, classNode);
+        }
+
+        public void call(ClassVisitor classWriter, ClassNode classNode) {
+            onClassNode((ClassWriter) classWriter, classNode);
+        }
+
+        public Collection getLoadedClasses() {
+            return this.loadedClasses;
+        }
+    }
+
+    /**
+     * open up the super class define that takes raw bytes
+     */
+    public Class defineClass(String name, byte[] b) {
+        return super.defineClass(name, b, 0, b.length);
+    }
+
+    /**
+     * loads a class from a file or a parent classloader.
+     * This method does call loadClass(String, boolean, boolean, boolean)
+     * with the last parameter set to false.
+     *
+     * @throws CompilationFailedException if compilation was not successful
+     */
+    public Class loadClass(final String name, boolean lookupScriptFiles, boolean preferClassOverScript)
+            throws ClassNotFoundException, CompilationFailedException {
+        return loadClass(name, lookupScriptFiles, preferClassOverScript, false);
+    }
+
+    /**
+     * gets a class from the class cache. This cache contains only classes loaded through
+     * this class loader or an InnerLoader instance. If no class is stored for a
+     * specific name, then the method should return null.
+     *
+     * @param name of the class
+     * @return the class stored for the given name
+     * @see #removeClassCacheEntry(String)
+     * @see #setClassCacheEntry(Class)
+     * @see #clearCache()
+     */
+    protected Class getClassCacheEntry(String name) {
+        if (name == null) return null;
+        synchronized (this) {
+            return (Class) classCache.get(name);
+        }
+    }
+
+    /**
+     * sets an entry in the class cache.
+     *
+     * @param cls the class
+     * @see #removeClassCacheEntry(String)
+     * @see #getClassCacheEntry(String)
+     * @see #clearCache()
+     */
+    protected void setClassCacheEntry(Class cls) {
+        synchronized (this) {
+            classCache.put(cls.getName(), cls);
+        }
+    }
+
+    /**
+     * removes a class from the class cache.
+     *
+     * @param name of the class
+     * @see #getClassCacheEntry(String)
+     * @see #setClassCacheEntry(Class)
+     * @see #clearCache()
+     */
+    protected void removeClassCacheEntry(String name) {
+        synchronized (this) {
+            classCache.remove(name);
+        }
+    }
+
+    /**
+     * adds a URL to the classloader.
+     *
+     * @param url the new classpath element
+     */
+    public void addURL(URL url) {
+        super.addURL(url);
+    }
+
+    /**
+     * Indicates if a class is recompilable. Recompileable means, that the classloader
+     * will try to locate a groovy source file for this class and then compile it again,
+     * adding the resulting class as entry to the cache. Giving null as class is like a
+     * recompilation, so the method should always return true here. Only classes that are
+     * implementing GroovyObject are compileable and only if the timestamp in the class
+     * is lower than Long.MAX_VALUE.
+     * <p/>
+     * NOTE: First the parent loaders will be asked and only if they don't return a
+     * class the recompilation will happen. Recompilation also only happen if the source
+     * file is newer.
+     *
+     * @param cls the class to be tested. If null the method should return true
+     * @return true if the class should be compiled again
+     * @see #isSourceNewer(URL, Class)
+     */
+    protected boolean isRecompilable(Class cls) {
+        if (cls == null) return true;
+        if (recompile == null && !config.getRecompileGroovySource()) return false;
+        if (recompile != null && !recompile.booleanValue()) return false;
+        if (!GroovyObject.class.isAssignableFrom(cls)) return false;
+        long timestamp = getTimeStamp(cls);
+        if (timestamp == Long.MAX_VALUE) return false;
+
+        return true;
+    }
+
+    /**
+     * sets if the recompilation should be enable. There are 3 possible
+     * values for this. Any value different than null overrides the
+     * value from the compiler configuration. true means to recompile if needed
+     * false means to never recompile.
+     *
+     * @param mode the recompilation mode
+     * @see CompilerConfiguration
+     */
+    public void setShouldRecompile(Boolean mode) {
+        recompile = mode;
+    }
+
+    /**
+     * gets the currently set recompilation mode. null means, the
+     * compiler configuration is used. False means no recompilation and
+     * true means that recompilation will be done if needed.
+     *
+     * @return the recompilation mode
+     */
+    public Boolean isShouldRecompile() {
+        return recompile;
+    }
+
+    /**
+     * loads a class from a file or a parent classloader.
+     *
+     * @param name                  of the class to be loaded
+     * @param lookupScriptFiles     if false no lookup at files is done at all
+     * @param preferClassOverScript if true the file lookup is only done if there is no class
+     * @param resolve               @see ClassLoader#loadClass(java.lang.String, boolean)
+     * @return the class found or the class created from a file lookup
+     * @throws ClassNotFoundException     if the class could not be found
+     * @throws CompilationFailedException if the source file could not be compiled
+     */
+    public Class loadClass(final String name, boolean lookupScriptFiles, boolean preferClassOverScript, boolean resolve)
+            throws ClassNotFoundException, CompilationFailedException {
+        // look into cache
+        Class cls = getClassCacheEntry(name);
+
+        // enable recompilation?
+        boolean recompile = isRecompilable(cls);
+        if (!recompile) return cls;
+
+        // check security manager
+        SecurityManager sm = System.getSecurityManager();
+        if (sm != null) {
+            String className = name.replace('/', '.');
+            int i = className.lastIndexOf('.');
+            if (i != -1) {
+                sm.checkPackageAccess(className.substring(0, i));
+            }
+        }
+
+        // try parent loader
+        ClassNotFoundException last = null;
+        try {
+            Class parentClassLoaderClass = super.loadClass(name, resolve);
+            // always return if the parent loader was successful
+            if (cls != parentClassLoaderClass) return parentClassLoaderClass;
+        } catch (ClassNotFoundException cnfe) {
+            last = cnfe;
+        } catch (NoClassDefFoundError ncdfe) {
+            if (ncdfe.getMessage().indexOf("wrong name") > 0) {
+                last = new ClassNotFoundException(name);
+            } else {
+                throw ncdfe;
+            }
+        }
+
+        if (cls != null) {
+            // prefer class if no recompilation
+            preferClassOverScript |= !recompile;
+            if (preferClassOverScript) return cls;
+        }
+
+        // at this point the loading from a parent loader failed
+        // and we want to recompile if needed.
+        if (lookupScriptFiles) {
+            // synchronize on cache, as we want only one compilation at the same time
+            synchronized (this) {
+                // try groovy file
+                try {
+                    // check if recompilation already happened.
+                    if (getClassCacheEntry(name) != cls) return getClassCacheEntry(name);
+                    URL source = resourceLoader.loadGroovySource(name);
+                    cls = recompile(source, name, cls);
+                } catch (IOException ioe) {
+                    last = new ClassNotFoundException("IOException while opening groovy source: " + name, ioe);
+                } finally {
+                    if (cls == null) {
+                        removeClassCacheEntry(name);
+                    } else {
+                        setClassCacheEntry(cls);
+                    }
+                }
+            }
+        }
+
+        if (cls == null) {
+            // no class found, there should have been an exception before now
+            if (last == null) throw new AssertionError(true);
+            throw last;
+        }
+        return cls;
+    }
+
+    /**
+     * (Re)Compiles the given source.
+     * This method starts the compilation of a given source, if
+     * the source has changed since the class was created. For
+     * this isSourceNewer is called.
+     *
+     * @param source    the source pointer for the compilation
+     * @param className the name of the class to be generated
+     * @param oldClass  a possible former class
+     * @return the old class if the source wasn't new enough, the new class else
+     * @throws CompilationFailedException if the compilation failed
+     * @throws IOException                if the source is not readable
+     * @see #isSourceNewer(URL, Class)
+     */
+    protected Class recompile(URL source, String className, Class oldClass) throws CompilationFailedException, IOException {
+        if (source != null) {
+            // found a source, compile it if newer
+            if ((oldClass != null && isSourceNewer(source, oldClass)) || (oldClass == null)) {
+                sourceCache.remove(className);
+                return parseClass(source.openStream(), className);
+            }
+        }
+        return oldClass;
+    }
+
+    /**
+     * Implemented here to check package access prior to returning an
+     * already loaded class.
+     *
+     * @throws CompilationFailedException if the compilation failed
+     * @throws ClassNotFoundException     if the class was not found
+     * @see java.lang.ClassLoader#loadClass(java.lang.String, boolean)
+     */
+    protected Class loadClass(final String name, boolean resolve) throws ClassNotFoundException {
+        return loadClass(name, true, false, resolve);
+    }
+
+    /**
+     * gets the time stamp of a given class. For groovy
+     * generated classes this usually means to return the value
+     * of the static field __timeStamp. If the parameter doesn't
+     * have such a field, then Long.MAX_VALUE is returned
+     *
+     * @param cls the class
+     * @return the time stamp
+     */
+    protected long getTimeStamp(Class cls) {
+        return Verifier.getTimestamp(cls);
+    }
+
+    /*
+    * This method will take a file name and try to "decode" any URL encoded characters.  For example
+    * if the file name contains any spaces this method call will take the resulting %20 encoded values
+    * and convert them to spaces.
+    *
+    * This method was added specifically to fix defect:  Groovy-1787.  The defect involved a situation
+    * where two scripts were sitting in a directory with spaces in its name.  The code would fail
+    * when the class loader tried to resolve the file name and would choke on the URLEncoded space values.
+    *
+    */
+    private String decodeFileName(String fileName) {
+        String decodedFile = fileName;
+        try {
+            decodedFile = URLDecoder.decode(fileName, "UTF-8");
+        } catch (UnsupportedEncodingException e) {
+            System.err.println("Encounted an invalid encoding scheme when trying to use URLDecoder.decode() inside of the GroovyClassLoader.decodeFileName() method.  Returning the unencoded URL.");
+            System.err.println("Please note that if you encounter this error and you have spaces in your directory you will run into issues.  Refer to GROOVY-1787 for description of this bug.");
+        }
+
+        return decodedFile;
+    }
+
+    private URL getSourceFile(String name) {
+        String filename = name.replace('.', '/') + config.getDefaultScriptExtension();
+        URL ret = getResource(filename);
+        if (ret != null && ret.getProtocol().equals("file")) {
+            String fileWithoutPackage = filename;
+            if (fileWithoutPackage.indexOf('/') != -1) {
+                int index = fileWithoutPackage.lastIndexOf('/');
+                fileWithoutPackage = fileWithoutPackage.substring(index + 1);
+            }
+            File path = new File(decodeFileName(ret.getFile())).getParentFile();
+            if (path.exists() && path.isDirectory()) {
+                File file = new File(path, fileWithoutPackage);
+                if (file.exists()) {
+                    // file.exists() might be case insensitive. Let's do
+                    // case sensitive match for the filename
+                    File parent = file.getParentFile();
+                    String[] files = parent.list();
+                    for (int j = 0; j < files.length; j++) {
+                        if (files[j].equals(fileWithoutPackage)) return ret;
+                    }
+                }
+            }
+            //file does not exist!
+            return null;
+        }
+        return ret;
+    }
+
+    /**
+     * Decides if the given source is newer than a class.
+     *
+     * @param source the source we may want to compile
+     * @param cls    the former class
+     * @return true if the source is newer, false else
+     * @throws IOException if it is not possible to open an
+     *                     connection for the given source
+     * @see #getTimeStamp(Class)
+     */
+    protected boolean isSourceNewer(URL source, Class cls) throws IOException {
+        long lastMod;
+
+        // Special handling for file:// protocol, as getLastModified() often reports
+        // incorrect results (-1)
+        if (source.getProtocol().equals("file")) {
+            // Coerce the file URL to a File
+            String path = source.getPath().replace('/', File.separatorChar).replace('|', ':');
+            File file = new File(path);
+            lastMod = file.lastModified();
+        } else {
+            URLConnection conn = source.openConnection();
+            lastMod = conn.getLastModified();
+            conn.getInputStream().close();
+        }
+        long classTime = getTimeStamp(cls);
+        return classTime + config.getMinimumRecompilationInterval() < lastMod;
+    }
+
+    /**
+     * adds a classpath to this classloader.
+     *
+     * @param path is a jar file or a directory.
+     * @see #addURL(URL)
+     */
+    public void addClasspath(final String path) {
+        AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                try {
+                    File f = new File(path);
+                    URL newURL = f.toURI().toURL();
+                    URL[] urls = getURLs();
+                    for (int i = 0; i < urls.length; i++) {
+                        if (urls[i].equals(newURL)) return null;
+                    }
+                    addURL(newURL);
+                } catch (MalformedURLException e) {
+                    //TODO: fail through ?
+                }
+                return null;
+            }
+        });
+    }
+
+    /**
+     * <p>Returns all Groovy classes loaded by this class loader.
+     *
+     * @return all classes loaded by this class loader
+     */
+    public Class[] getLoadedClasses() {
+        synchronized (this) {
+            final Collection values = classCache.values();
+            return (Class[]) values.toArray(new Class[values.size()]);
+        }
+    }
+
+    /**
+     * removes all classes from the class cache.
+     *
+     * @see #getClassCacheEntry(String)
+     * @see #setClassCacheEntry(Class)
+     * @see #removeClassCacheEntry(String)
+     */
+    public void clearCache() {
+        synchronized (this) {
+            classCache.clear();
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/lang/GroovyCodeSource.java b/groovy/src/main/groovy/lang/GroovyCodeSource.java
new file mode 100644
index 0000000..09c1f38
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GroovyCodeSource.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import groovy.security.GroovyCodeSourcePermission;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.CodeSource;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.cert.Certificate;
+
+/**
+ * CodeSource wrapper class that allows specific security policies to be associated with a class
+ * compiled from groovy source.
+ * 
+ * @author Steve Goetze
+ */
+public class GroovyCodeSource {
+	
+	/** 
+	 * The codeSource to be given the generated class.  This can be used by policy file
+	 * grants to administer security.
+	 */
+	private CodeSource codeSource;
+	/** The name given to the generated class */
+	private String name;
+	/** The groovy source to be compiled and turned into a class */
+	private InputStream inputStream;
+	/** The certificates used to sign the items from the codesource */
+	Certificate[] certs;
+    private boolean cachable;
+    
+	private File file;
+	
+	public GroovyCodeSource(String script, String name, String codeBase) {
+		this(new ByteArrayInputStream(script.getBytes()), name, codeBase);
+	}
+	
+	/**
+	 * Construct a GroovyCodeSource for an inputStream of groovyCode that has an
+	 * unknown provenance -- meaning it didn't come from a File or a URL (e.g. a String).
+	 * The supplied codeBase will be used to construct a File URL that should match up
+	 * with a java Policy entry that determines the grants to be associated with the
+	 * class that will be built from the InputStream.
+	 * 
+	 * The permission groovy.security.GroovyCodeSourcePermission will be used to determine if the given codeBase
+	 * may be specified.  That is, the current Policy set must have a GroovyCodeSourcePermission that implies
+	 * the codeBase, or an exception will be thrown.  This is to prevent callers from hijacking
+	 * existing codeBase policy entries unless explicitly authorized by the user.
+	 */
+	public GroovyCodeSource(InputStream inputStream, String name, String codeBase) {
+		this.inputStream = inputStream;
+		this.name = name;
+		SecurityManager sm = System.getSecurityManager();
+		if (sm != null) {
+		    sm.checkPermission(new GroovyCodeSourcePermission(codeBase));
+		}
+		try {
+			this.codeSource = new CodeSource(new URL("file", "", codeBase), (java.security.cert.Certificate[])null);
+		} catch (MalformedURLException murle) {
+			throw new RuntimeException("A CodeSource file URL cannot be constructed from the supplied codeBase: " + codeBase);
+		}
+	}
+	
+	public GroovyCodeSource(final File file) throws FileNotFoundException {
+		if (!file.exists())
+		    throw new FileNotFoundException(file.toString() + " (" +  file.getAbsolutePath() +  ")");
+		else {
+		   try {
+		       if (!file.canRead())
+		           throw new RuntimeException(file.toString() + " can not be read. Check the read permisson of the file \"" + file.toString() + "\" (" +  file.getAbsolutePath() +  ").");
+		   }
+		   catch (SecurityException e) {
+		        throw e;
+		    }
+		}
+
+		//this.inputStream = new FileInputStream(file);
+		this.file = file;
+		this.inputStream = null;
+        this.cachable = true;
+		//The calls below require access to user.dir - allow here since getName() and getCodeSource() are
+		//package private and used only by the GroovyClassLoader.
+		try {
+            Object[] info = (Object[]) AccessController.doPrivileged( new PrivilegedExceptionAction() {
+				public Object run() throws MalformedURLException {
+                    Object[] info = new Object[2];
+                    URL url = file.toURI().toURL();
+                    info[0] = url.toExternalForm();
+					//toURI().toURL() will encode, but toURL() will not.
+					info[1] = new CodeSource(url, (Certificate[]) null);
+                    return info;
+				}
+			});
+			this.name = (String) info[0];
+            this.codeSource = (CodeSource) info[1];
+		} catch (PrivilegedActionException pae) {
+			throw new RuntimeException("Could not construct a URL from: " + file);
+		}
+	}
+	
+	public GroovyCodeSource(URL url) throws IOException {
+		if (url == null) {
+			throw new RuntimeException("Could not construct a GroovyCodeSource from a null URL");
+		}
+		this.inputStream = url.openStream();
+		this.name = url.toExternalForm();
+		this.codeSource = new CodeSource(url, (java.security.cert.Certificate[])null);
+	}
+	
+	CodeSource getCodeSource() {
+		return codeSource;
+	}
+
+	public InputStream getInputStream() {
+        if(this.inputStream != null) {
+            return this.inputStream;
+        }
+        else {
+            try {
+                if (file!=null) return new FileInputStream(file);
+            } catch (FileNotFoundException fnfe) {
+                // IGNORE
+            }
+            return inputStream;            
+        }
+	}
+
+	public String getName() {
+		return name;
+	}
+    
+    public File getFile() {
+        return file;
+    }
+    
+    public void setCachable(boolean b) {
+        cachable = b;
+    }
+
+    public boolean isCachable() {
+        return cachable;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/GroovyInterceptable.java b/groovy/src/main/groovy/lang/GroovyInterceptable.java
new file mode 100644
index 0000000..8605eb5
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GroovyInterceptable.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+/**
+ * Marker interface used to notify that all methods should be intercepted through the <code>invokeMethod</code> mechanism
+ * of <code>GroovyObject</code>.
+ *
+ * @author Guillaume Laforge
+ */
+public interface GroovyInterceptable extends GroovyObject {
+}
diff --git a/groovy/src/main/groovy/lang/GroovyLogTestCase.groovy b/groovy/src/main/groovy/lang/GroovyLogTestCase.groovy
new file mode 100644
index 0000000..e57345d
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GroovyLogTestCase.groovy
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang
+
+import java.util.logging.*
+
+/**
+Helper class to spoof log entries as produced by calling arbitrary code.
+This allows non-intrusive testing of dependent objects without
+explicitly using Mock objects as long as those dependent objects
+do some proper logging.
+As a measure of last resort, it can be used on MetaClass to spoof
+it's log entries on 'invokeMethod'.
+
+@author Dierk Koenig
+@see GroovyLogTestCaseTest
+**/
+
+class GroovyLogTestCase extends GroovyTestCase {
+
+    /**
+     Execute the given Closure with the according level for the Logger that
+     is qualified by the qualifier and return the log output as a String.
+     Qualifiers are usually package or class names.
+     Existing log level and handlers are restored after execution.
+    **/
+    static String stringLog (Level level, String qualifier, Closure yield){
+        // store old values
+        Logger logger = Logger.getLogger(qualifier)
+        def usesParentHandlers = logger.useParentHandlers
+        // set new values
+        logger.useParentHandlers = false
+        def out = new ByteArrayOutputStream(1024)
+        Handler stringHandler = new StreamHandler(out, new SimpleFormatter())
+        stringHandler.level = Level.ALL
+        logger.addHandler(stringHandler) // any old handlers remain
+
+        withLevel(level, qualifier, yield)
+
+        // restore old values
+        logger.level = Level.OFF    // temporarily, to avoid logging the 3 stmts below
+        stringHandler.flush()
+        out.close()
+        logger.removeHandler(stringHandler)
+        logger.useParentHandlers = usesParentHandlers
+        return out.toString()
+    }
+
+    /**
+     Execute the given Closure with the according level for the Logger that
+     is qualified by the qualifier. Qualifiers are usually package or class
+     names.
+     The log level is restored after execution.
+    **/
+    static def withLevel(Level level, String qualifier, Closure yield){
+        // store old values
+        Logger logger = Logger.getLogger(qualifier)
+        def loglevel = logger.level
+        // set new values
+        if (!logger.isLoggable(level)) logger.level = level // use min value
+
+        def result = yield()
+
+        // restore old values
+        logger.level = loglevel
+        return result
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/lang/GroovyObject.java b/groovy/src/main/groovy/lang/GroovyObject.java
new file mode 100644
index 0000000..0c1f511
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GroovyObject.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+/**
+ * The interface implemented by all Groovy objects.
+ *
+ * Especially handy for using Groovy objects when in the Java world.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public interface GroovyObject {
+
+    /** 
+     * Invokes the given method
+     */
+    Object invokeMethod(String name, Object args);
+    
+    /**
+     * @return the given property
+     */
+    Object getProperty(String property);
+
+    /**
+     * Sets the given property to the new value
+     */
+    void setProperty(String property, Object newValue);
+        
+    /**
+     * @return the metaClass of this instance
+     */
+    MetaClass getMetaClass();
+    
+    /**
+     * Allows the MetaClass to be replaced with a derived implementation
+     */
+    void setMetaClass(MetaClass metaClass);
+}
diff --git a/groovy/src/main/groovy/lang/GroovyObjectSupport.java b/groovy/src/main/groovy/lang/GroovyObjectSupport.java
new file mode 100644
index 0000000..a448bf0
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GroovyObjectSupport.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * A useful base class for Java objects wishing to be Groovy objects
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class GroovyObjectSupport implements GroovyObject {
+
+	// never persist the MetaClass
+    private transient MetaClass metaClass;
+
+    public GroovyObjectSupport() {
+        this.metaClass = InvokerHelper.getMetaClass(this);
+    }
+    
+    public Object getProperty(String property) {
+        return metaClass.getProperty(this, property);
+    }
+
+    public void setProperty(String property, Object newValue) {
+         metaClass.setProperty(this, property, newValue);
+    }
+
+    public Object invokeMethod(String name, Object args) {
+        return metaClass.invokeMethod(this, name, args);
+    }
+    
+    public MetaClass getMetaClass() {
+        return metaClass;
+    }
+    
+    public void setMetaClass(MetaClass metaClass) {
+        this.metaClass = metaClass;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/GroovyResourceLoader.java b/groovy/src/main/groovy/lang/GroovyResourceLoader.java
new file mode 100644
index 0000000..2912e39
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GroovyResourceLoader.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+
+/**
+ * Allows frameworks that integrate with Groovy to determine how Groovy files are resolved.
+ * 
+ * @author Steven Devijver
+ */
+public interface GroovyResourceLoader {
+
+    /**
+     * Loads a Groovy source file given its name.
+     *
+     * @param filename name of the file
+     * @return a URL
+     * @throws java.net.MalformedURLException if the URL is invalid
+     */
+    URL loadGroovySource(String filename) throws MalformedURLException;
+}
diff --git a/groovy/src/main/groovy/lang/GroovyRuntimeException.java b/groovy/src/main/groovy/lang/GroovyRuntimeException.java
new file mode 100644
index 0000000..e6d3269
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GroovyRuntimeException.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ModuleNode;
+
+/**
+ * An exception thrown by the interpreter
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class GroovyRuntimeException extends RuntimeException {
+
+    private ModuleNode module;
+    private ASTNode node;
+
+    public GroovyRuntimeException() {
+    }
+
+    public GroovyRuntimeException(String message) {
+        super(message);
+    }
+
+    public GroovyRuntimeException(String message, ASTNode node) {
+        super(message);
+        this.node = node;
+    }
+
+    public GroovyRuntimeException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public GroovyRuntimeException(Throwable t) {
+        super();
+        initCause(t);
+    }
+
+    public void setModule(ModuleNode module) {
+        this.module = module;
+    }
+
+    public ModuleNode getModule() {
+        return module;
+    }
+
+    public String getMessage() {
+        return getMessageWithoutLocationText() + getLocationText();
+    }
+
+    public ASTNode getNode() {
+        return node;
+    }
+
+    public String getMessageWithoutLocationText() {
+        return super.getMessage();
+    }
+
+    protected String getLocationText() {
+        String answer = ". ";
+        if (node != null) {
+            answer += "At [" + node.getLineNumber() + ":" + node.getColumnNumber() + "] ";
+        }
+        if (module != null) {
+            answer += module.getDescription();
+        }
+        if (answer.equals(". ")) {
+            return "";
+        }
+        return answer;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/GroovyShell.java b/groovy/src/main/groovy/lang/GroovyShell.java
new file mode 100644
index 0000000..4e27d1d
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GroovyShell.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import groovy.ui.GroovyMain;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.io.*;
+import java.lang.reflect.Constructor;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents a groovy shell capable of running arbitrary groovy scripts
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Guillaume Laforge
+ * @version $Revision$
+ */
+public class GroovyShell extends GroovyObjectSupport {
+       
+    public static final String[] EMPTY_ARGS = {};
+
+    
+    private Binding context;
+    private int counter;
+    private CompilerConfiguration config;
+    private GroovyClassLoader loader;
+
+    public static void main(String[] args) {
+        GroovyMain.main(args);
+    }
+
+    public GroovyShell() {
+        this(null, new Binding());
+    }
+
+    public GroovyShell(Binding binding) {
+        this(null, binding);
+    }
+
+    public GroovyShell(CompilerConfiguration config) {
+        this(new Binding(), config);
+    }
+
+    public GroovyShell(Binding binding, CompilerConfiguration config) {
+        this(null, binding, config);
+    }
+
+    public GroovyShell(ClassLoader parent, Binding binding) {
+        this(parent, binding, CompilerConfiguration.DEFAULT);
+    }
+
+    public GroovyShell(ClassLoader parent) {
+        this(parent, new Binding(), CompilerConfiguration.DEFAULT);
+    }
+    
+    public GroovyShell(ClassLoader parent, Binding binding, final CompilerConfiguration config) {
+        if (binding == null) {
+            throw new IllegalArgumentException("Binding must not be null.");
+        }
+        if (config == null) {
+            throw new IllegalArgumentException("Compiler configuration must not be null.");
+        }
+        final ClassLoader parentLoader = (parent!=null)?parent:GroovyShell.class.getClassLoader();
+        this.loader = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return new GroovyClassLoader(parentLoader,config);
+            }
+        });
+        this.context = binding;        
+        this.config = config;
+    }
+    
+    public void initializeBinding() {
+        Map map = context.getVariables();
+        if (map.get("shell")==null) map.put("shell",this);
+    }
+    
+    public void resetLoadedClasses() {
+        loader.clearCache();
+    }
+
+    /**
+     * Creates a child shell using a new ClassLoader which uses the parent shell's
+     * class loader as its parent
+     *
+     * @param shell is the parent shell used for the variable bindings and the parent class loader
+     */
+    public GroovyShell(GroovyShell shell) {
+        this(shell.loader, shell.context);
+    }
+
+    public Binding getContext() {
+        return context;
+    }
+
+    public GroovyClassLoader getClassLoader() {
+        return loader;
+    }
+
+    public Object getProperty(String property) {
+        Object answer = getVariable(property);
+        if (answer == null) {
+            answer = super.getProperty(property);
+        }
+        return answer;
+    }
+
+    public void setProperty(String property, Object newValue) {
+        setVariable(property, newValue);
+        try {
+            super.setProperty(property, newValue);
+        } catch (GroovyRuntimeException e) {
+            // ignore, was probably a dynamic property
+        }
+    }
+
+    /**
+     * A helper method which runs the given script file with the given command line arguments
+     *
+     * @param scriptFile the file of the script to run
+     * @param list       the command line arguments to pass in
+     */
+    public Object run(File scriptFile, List list) throws CompilationFailedException, IOException {
+        String[] args = new String[list.size()];
+        return run(scriptFile, (String[]) list.toArray(args));
+    }
+
+    /**
+     * A helper method which runs the given cl script with the given command line arguments
+     *
+     * @param scriptText is the text content of the script
+     * @param fileName   is the logical file name of the script (which is used to create the class name of the script)
+     * @param list       the command line arguments to pass in
+     */
+    public Object run(String scriptText, String fileName, List list) throws CompilationFailedException {
+        String[] args = new String[list.size()];
+        list.toArray(args);
+        return run(scriptText, fileName, args);
+    }
+
+    /**
+     * Runs the given script file name with the given command line arguments
+     *
+     * @param scriptFile the file name of the script to run
+     * @param args       the command line arguments to pass in
+     */
+    public Object run(final File scriptFile, String[] args) throws CompilationFailedException, IOException {
+        String scriptName = scriptFile.getName();
+        int p = scriptName.lastIndexOf(".");
+        if (p++ >= 0) {
+            if (scriptName.substring(p).equals("java")) {
+                System.err.println("error: cannot compile file with .java extension: " + scriptName);
+                throw new CompilationFailedException(0, null);
+            }
+        }
+
+        // Get the current context classloader and save it on the stack
+        final Thread thread = Thread.currentThread();
+        //ClassLoader currentClassLoader = thread.getContextClassLoader();
+
+        class DoSetContext implements PrivilegedAction {
+            ClassLoader classLoader;
+
+            public DoSetContext(ClassLoader loader) {
+                classLoader = loader;
+            }
+
+            public Object run() {
+                thread.setContextClassLoader(classLoader);
+                return null;
+            }
+        }
+
+        AccessController.doPrivileged(new DoSetContext(loader));
+
+        // Parse the script, generate the class, and invoke the main method.  This is a little looser than
+        // if you are compiling the script because the JVM isn't executing the main method.
+        Class scriptClass;
+        try {
+            scriptClass = (Class) AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                public Object run() throws CompilationFailedException, IOException {
+                    return loader.parseClass(scriptFile);
+                }
+            });
+        } catch (PrivilegedActionException pae) {
+            Exception e = pae.getException();
+            if (e instanceof CompilationFailedException) {
+                throw (CompilationFailedException) e;
+            } else if (e instanceof IOException) {
+                throw (IOException) e;
+            } else {
+                throw (RuntimeException) pae.getException();
+            }
+        }
+
+        return runMainOrTestOrRunnable(scriptClass, args);
+
+        // Set the context classloader back to what it was.
+        //AccessController.doPrivileged(new DoSetContext(currentClassLoader));
+    }
+
+    /**
+     * if (theClass has a main method) {
+     * run the main method
+     * } else if (theClass instanceof GroovyTestCase) {
+     * use the test runner to run it
+     * } else if (theClass implements Runnable) {
+     * if (theClass has a constructor with String[] params)
+     * instanciate theClass with this constructor and run
+     * else if (theClass has a no-args constructor)
+     * instanciate theClass with the no-args constructor and run
+     * }
+     */
+    private Object runMainOrTestOrRunnable(Class scriptClass, String[] args) {
+        if (scriptClass == null) {
+            return null;
+        }
+        try {
+            // let's find a main method
+            scriptClass.getMethod("main", new Class[]{String[].class});
+            // if that main method exist, invoke it
+            return InvokerHelper.invokeMethod(scriptClass, "main", new Object[]{args});
+        } catch (NoSuchMethodException e) {
+            // if it implements Runnable, try to instantiate it
+            if (Runnable.class.isAssignableFrom(scriptClass)) {
+                return runRunnable(scriptClass, args);
+            }
+            // if it's a unit test extending GroovyTestCase, run it with JUnit's TextRunner
+            if (isUnitTestCase(scriptClass)) {
+                return runTest(scriptClass);
+            }
+            throw new GroovyRuntimeException("This script or class could not be run.\n" +
+                    "It should either: \n" +
+                    "- have a main method, \n" +
+                    "- be a class extending GroovyTestCase, \n" +
+                    "- or implement the Runnable interface.");
+        }
+    }
+
+    private Object runRunnable(Class scriptClass, String[] args) {
+        Constructor constructor = null;
+        Runnable runnable = null;
+        Throwable reason = null;
+        try {
+            // first, fetch the constructor taking String[] as parameter
+            constructor = scriptClass.getConstructor(new Class[]{(new String[]{}).getClass()});
+            try {
+                // instanciate a runnable and run it
+                runnable = (Runnable) constructor.newInstance(new Object[]{args});
+            } catch (Throwable t) {
+                reason = t;
+            }
+        } catch (NoSuchMethodException e1) {
+            try {
+                // otherwise, find the default constructor
+                constructor = scriptClass.getConstructor(new Class[]{});
+                try {
+                    // instanciate a runnable and run it
+                    runnable = (Runnable) constructor.newInstance(new Object[]{});
+                } catch (Throwable t) {
+                    reason = t;
+                }
+            } catch (NoSuchMethodException nsme) {
+                reason = nsme;
+            }
+        }
+        if (constructor != null && runnable != null) {
+            runnable.run();
+        } else {
+            throw new GroovyRuntimeException("This script or class was runnable but could not be run. ", reason);
+        }
+        return null;
+    }
+
+    /**
+     * Run the specified class extending GroovyTestCase as a unit test.
+     * This is done through reflection, to avoid adding a dependency to the JUnit framework.
+     * Otherwise, developers embedding Groovy and using GroovyShell to load/parse/compile
+     * groovy scripts and classes would have to add another dependency on their classpath.
+     *
+     * @param scriptClass the class to be run as a unit test
+     */
+    private Object runTest(Class scriptClass) {
+        try {
+            Object testSuite = InvokerHelper.invokeConstructorOf("junit.framework.TestSuite",new Object[]{scriptClass});
+            return InvokerHelper.invokeStaticMethod("junit.textui.TestRunner", "run", new Object[]{testSuite});
+        } catch (ClassNotFoundException e) {
+            throw new GroovyRuntimeException("Failed to run the unit test. JUnit is not on the Classpath.");
+        }
+    }
+
+    /**
+     * Utility method to check through reflection if the parsed class extends GroovyTestCase.
+     *
+     * @param scriptClass the class we want to know if it extends GroovyTestCase
+     * @return true if the class extends groovy.util.GroovyTestCase
+     */
+    private boolean isUnitTestCase(Class scriptClass) {
+        // check if the parsed class is a GroovyTestCase,
+        // so that it is possible to run it as a JUnit test
+        boolean isUnitTestCase = false;
+        try {
+            try {
+                Class testCaseClass = this.loader.loadClass("groovy.util.GroovyTestCase");
+                // if scriptClass extends testCaseClass
+                if (testCaseClass.isAssignableFrom(scriptClass)) {
+                    isUnitTestCase = true;
+                }
+            } catch (ClassNotFoundException e) {
+                // fall through
+            }
+        } catch (Throwable e) {
+            // fall through
+        }
+        return isUnitTestCase;
+    }
+
+    /**
+     * Runs the given script text with command line arguments
+     *
+     * @param scriptText is the text content of the script
+     * @param fileName   is the logical file name of the script (which is used to create the class name of the script)
+     * @param args       the command line arguments to pass in
+     */
+    public Object run(String scriptText, String fileName, String[] args) throws CompilationFailedException {
+        try {
+            return run(new ByteArrayInputStream(scriptText.getBytes(config.getSourceEncoding())), fileName, args);
+        } catch (UnsupportedEncodingException e) {
+            throw new CompilationFailedException(0, null, e);
+        }
+    }
+
+    /**
+     * Runs the given script with command line arguments
+     *
+     * @param in       the stream reading the script
+     * @param fileName is the logical file name of the script (which is used to create the class name of the script)
+     * @param args     the command line arguments to pass in
+     */
+    public Object run(final InputStream in, final String fileName, String[] args) throws CompilationFailedException {
+        GroovyCodeSource gcs = (GroovyCodeSource) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return new GroovyCodeSource(in, fileName, "/groovy/shell");
+            }
+        });
+        Class scriptClass = parseClass(gcs);
+        return runMainOrTestOrRunnable(scriptClass, args);
+    }
+
+    public Object getVariable(String name) {
+        return context.getVariables().get(name);
+    }
+
+    public void setVariable(String name, Object value) {
+        context.setVariable(name, value);
+    }
+
+    /**
+     * Evaluates some script against the current Binding and returns the result
+     *
+     * @param codeSource
+     * @throws CompilationFailedException
+     * @throws CompilationFailedException
+     */
+    public Object evaluate(GroovyCodeSource codeSource) throws CompilationFailedException {
+        Script script = parse(codeSource);
+        return script.run();
+    }
+
+    /**
+     * Evaluates some script against the current Binding and returns the result
+     *
+     * @param scriptText the text of the script
+     * @param fileName   is the logical file name of the script (which is used to create the class name of the script)
+     */
+    public Object evaluate(String scriptText, String fileName) throws CompilationFailedException {
+        try {
+            return evaluate(new ByteArrayInputStream(scriptText.getBytes(config.getSourceEncoding())), fileName);
+        } catch (UnsupportedEncodingException e) {
+            throw new CompilationFailedException(0, null, e);
+        }
+    }
+
+    /**
+     * Evaluates some script against the current Binding and returns the result.
+     * The .class file created from the script is given the supplied codeBase
+     */
+    public Object evaluate(String scriptText, String fileName, String codeBase) throws CompilationFailedException {
+        try {
+            return evaluate(new GroovyCodeSource(new ByteArrayInputStream(scriptText.getBytes(config.getSourceEncoding())), fileName, codeBase));
+        } catch (UnsupportedEncodingException e) {
+            throw new CompilationFailedException(0, null, e);
+        }
+    }
+
+    /**
+     * Evaluates some script against the current Binding and returns the result
+     *
+     * @param file is the file of the script (which is used to create the class name of the script)
+     */
+    public Object evaluate(File file) throws CompilationFailedException, IOException {
+        return evaluate(new GroovyCodeSource(file));
+    }
+
+    /**
+     * Evaluates some script against the current Binding and returns the result
+     *
+     * @param scriptText the text of the script
+     */
+    public Object evaluate(String scriptText) throws CompilationFailedException {
+        try {
+            return evaluate(new ByteArrayInputStream(scriptText.getBytes(config.getSourceEncoding())), generateScriptName());
+        } catch (UnsupportedEncodingException e) {
+            throw new CompilationFailedException(0, null, e);
+        }
+    }
+
+    /**
+     * Evaluates some script against the current Binding and returns the result
+     *
+     * @param in the stream reading the script
+     */
+    public Object evaluate(InputStream in) throws CompilationFailedException {
+        return evaluate(in, generateScriptName());
+    }
+
+    /**
+     * Evaluates some script against the current Binding and returns the result
+     *
+     * @param in       the stream reading the script
+     * @param fileName is the logical file name of the script (which is used to create the class name of the script)
+     */
+    public Object evaluate(InputStream in, String fileName) throws CompilationFailedException {
+        Script script = null;
+        try {
+            script = parse(in, fileName);
+            return script.run();
+        } finally {
+            if (script != null) {
+                InvokerHelper.removeClass(script.getClass());
+            }
+        }
+    }
+
+    /**
+     * Parses the given script and returns it ready to be run
+     *
+     * @param in       the stream reading the script
+     * @param fileName is the logical file name of the script (which is used to create the class name of the script)
+     * @return the parsed script which is ready to be run via @link Script.run()
+     */
+    public Script parse(final InputStream in, final String fileName) throws CompilationFailedException {
+        GroovyCodeSource gcs = (GroovyCodeSource) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return new GroovyCodeSource(in, fileName, "/groovy/shell");
+            }
+        });
+        return parse(gcs);
+    }
+
+    /**
+     * Parses the groovy code contained in codeSource and returns a java class.
+     */
+    private Class parseClass(final GroovyCodeSource codeSource) throws CompilationFailedException {
+        // Don't cache scripts
+        return loader.parseClass(codeSource, false);
+    }
+
+    /**
+     * Parses the given script and returns it ready to be run.  When running in a secure environment
+     * (-Djava.security.manager) codeSource.getCodeSource() determines what policy grants should be
+     * given to the script.
+     *
+     * @param codeSource
+     * @return ready to run script
+     */
+    public Script parse(final GroovyCodeSource codeSource) throws CompilationFailedException {
+        return InvokerHelper.createScript(parseClass(codeSource), context);
+    }
+
+    /**
+     * Parses the given script and returns it ready to be run
+     *
+     * @param file is the file of the script (which is used to create the class name of the script)
+     */
+    public Script parse(File file) throws CompilationFailedException, IOException {
+        return parse(new GroovyCodeSource(file));
+    }
+
+    /**
+     * Parses the given script and returns it ready to be run
+     *
+     * @param scriptText the text of the script
+     */
+    public Script parse(String scriptText) throws CompilationFailedException {
+        try {
+            return parse(new ByteArrayInputStream(scriptText.getBytes(config.getSourceEncoding())), generateScriptName());
+        } catch (UnsupportedEncodingException e) {
+            throw new CompilationFailedException(0, null, e);
+        }
+    }
+
+    public Script parse(String scriptText, String fileName) throws CompilationFailedException {
+        try {
+            return parse(new ByteArrayInputStream(scriptText.getBytes(config.getSourceEncoding())), fileName);
+        } catch (UnsupportedEncodingException e) {
+            throw new CompilationFailedException(0, null, e);
+        }
+    }
+
+    /**
+     * Parses the given script and returns it ready to be run
+     *
+     * @param in the stream reading the script
+     */
+    public Script parse(InputStream in) throws CompilationFailedException {
+        return parse(in, generateScriptName());
+    }
+
+    protected synchronized String generateScriptName() {
+        return "Script" + (++counter) + ".groovy";
+    }
+}
diff --git a/groovy/src/main/groovy/lang/GroovySystem.java b/groovy/src/main/groovy/lang/GroovySystem.java
new file mode 100644
index 0000000..f0cf8e7
--- /dev/null
+++ b/groovy/src/main/groovy/lang/GroovySystem.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
+
+public final class GroovySystem {
+    //
+    //  TODO: make this initialisation able to set useReflection true
+    //  TODO: have some way of specifying another MetaClass Registry implementation
+    //
+    static {
+        USE_REFLECTION = true;
+        META_CLASS_REGISTRY = new MetaClassRegistryImpl();
+    }
+    
+    /**
+     * The MetaClass for java.lang.Object
+     */
+    private static MetaClass objectMetaClass;
+    /**
+     * If true then the MetaClass will only use reflection for method dispatch, property acess, etc.
+     */
+    private static final boolean USE_REFLECTION;
+    /**
+     * Reference to the MetaClass Registry to be used by the Groovy run time system to map classes to MetaClasses
+     */
+    private static final MetaClassRegistry META_CLASS_REGISTRY;
+
+    private static boolean keepJavaMetaClasses=false;
+    
+    private GroovySystem() {
+        // Do not allow this class to be instantiated
+    }
+
+    public static boolean isUseReflection() {
+        return USE_REFLECTION;
+    }
+
+    public static MetaClassRegistry getMetaClassRegistry() {
+        return META_CLASS_REGISTRY;
+    }
+    
+    public static void setKeepJavaMetaClasses(boolean keepJavaMetaClasses) {
+        GroovySystem.keepJavaMetaClasses = keepJavaMetaClasses;
+    }
+    
+    public static boolean isKeepJavaMetaClasses() {
+        return keepJavaMetaClasses;
+    }
+    
+}
diff --git a/groovy/src/main/groovy/lang/IllegalPropertyAccessException.java b/groovy/src/main/groovy/lang/IllegalPropertyAccessException.java
new file mode 100644
index 0000000..66f4944
--- /dev/null
+++ b/groovy/src/main/groovy/lang/IllegalPropertyAccessException.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+/**
+ * An exception occurred if a dynamic property dispatch fails with a 
+ * field not accessible.
+ * 
+ * @author <a href="mailto:blackdrag@uni.de">Jochen Theodorou</a>
+ * @version $Revision$
+ */
+public class IllegalPropertyAccessException extends MissingPropertyException {
+    
+    private static String makeMessage(String propertyName, Class clazz, int modifiers, boolean isField) {
+        String access = "private";
+        if (Modifier.isProtected(modifiers)) access = "protected";
+        if (Modifier.isPublic(modifiers)) access = "public";
+        String propertyType = "property";
+        if (isField) propertyType = "field";
+        return  "Can not access the "+access+" "+propertyType+" "+propertyName+" in class "+clazz.getName();
+    }
+    
+    public IllegalPropertyAccessException(String propertyName, Class clazz, int modifiers) {
+        super(makeMessage(propertyName,clazz,modifiers,false),propertyName,clazz);
+    }
+    
+    public IllegalPropertyAccessException(Field field, Class clazz) {
+        super(makeMessage(field.getName(),clazz,field.getModifiers(),true),field.getName(),clazz);
+    }
+    
+}
diff --git a/groovy/src/main/groovy/lang/IncorrectClosureArgumentsException.java b/groovy/src/main/groovy/lang/IncorrectClosureArgumentsException.java
new file mode 100644
index 0000000..17cdeb6
--- /dev/null
+++ b/groovy/src/main/groovy/lang/IncorrectClosureArgumentsException.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * An exception occurred when invoking a Closure with the wrong number and/or
+ * types of arguments
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class IncorrectClosureArgumentsException extends GroovyRuntimeException {
+
+    private final Closure closure;
+    private final Object arguments;
+    private final Class[] expected;
+
+    public IncorrectClosureArgumentsException(Closure closure, Object arguments, Class[] expected) {
+        super(
+            "Incorrect arguments to closure: "
+                + closure
+                + ". Expected: "
+                + InvokerHelper.toString(expected)
+                + ", actual: "
+                + InvokerHelper.toString(arguments));
+        this.closure = closure;
+        this.arguments = arguments;
+        this.expected = expected;
+    }
+
+    public Object getArguments() {
+        return arguments;
+    }
+
+    public Closure getClosure() {
+        return closure;
+    }
+
+    public Class[] getExpected() {
+        return expected;
+    }
+
+}
diff --git a/groovy/src/main/groovy/lang/IntRange.java b/groovy/src/main/groovy/lang/IntRange.java
new file mode 100644
index 0000000..3fd0221
--- /dev/null
+++ b/groovy/src/main/groovy/lang/IntRange.java
@@ -0,0 +1,347 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import org.codehaus.groovy.runtime.IteratorClosureAdapter;
+
+import java.math.BigInteger;
+import java.util.AbstractList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Represents a list of Integer objects from a specified int up (or down) to and including
+ * a given to.<p>
+ * <p/>
+ * This class is a copy of {@link ObjectRange} optimized for <code>int</code>.  If you make any
+ * changes to this class, you might consider making parallel changes to {@link ObjectRange}.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class IntRange extends AbstractList implements Range {
+
+    /**
+     * Iterates through each number in an <code>IntRange</code>.
+     */
+    private class IntRangeIterator implements Iterator {
+        /**
+         * Counts from 0 up to size - 1.
+         */
+        private int index;
+
+        /**
+         * The number of values in the range.
+         */
+        private int size = size();
+
+        /**
+         * The next value to return.
+         */
+        private int value = reverse ? to : from;
+
+        /**
+         * {@inheritDoc}
+         */
+        public boolean hasNext() {
+            return index < size;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public Object next() {
+            if (index++ > 0) {
+                if (index > size) {
+                    return null;
+                } else {
+                    if (reverse) {
+                        --value;
+                    } else {
+                        ++value;
+                    }
+                }
+            }
+            return new Integer(value);
+        }
+
+        /**
+         * Not supported.
+         *
+         * @throws javax.naming.OperationNotSupportedException always
+         */
+        public void remove() {
+            IntRange.this.remove(index);
+        }
+    }
+
+    /**
+     * The first number in the range.  <code>from </code> is always less than or equal to <code>to</code>.
+     */
+    private int from;
+
+    /**
+     * The last number in the range. <code>to</code> is always greater than or eqaul to <code>from</code>.
+     */
+    private int to;
+
+    /**
+     * If <code>false</code>, counts up from <code>from</code> to <code>to</code>.  Otherwise, counts down
+     * from <code>to</code> to <code>from</code>.
+     */
+    private boolean reverse;
+
+    /**
+     * Creates a new <code>IntRange</code>. If <code>from</code> is greater
+     * than <code>to</code>, a reverse range is created with
+     * <code>from</code> and <code>to</code> swapped.
+     *
+     * @param from the first number in the range.
+     * @param to   the last number in the range.
+     * @throws IllegalArgumentException if the range would contain more than
+     *                                  {@link Integer#MAX_VALUE} values.
+     */
+    public IntRange(int from, int to) {
+        if (from > to) {
+            this.from = to;
+            this.to = from;
+            this.reverse = true;
+        } else {
+            this.from = from;
+            this.to = to;
+        }
+
+        // size() an integer so ranges can have no more than Integer.MAX_VALUE elements
+        if (this.to - this.from >= Integer.MAX_VALUE) {
+            throw new IllegalArgumentException("range must have no more than " + Integer.MAX_VALUE + " elements");
+        }
+    }
+
+    /**
+     * Creates a new <code>IntRange</code>.
+     *
+     * @param from    the first value in the range.
+     * @param to      the last value in the range.
+     * @param reverse <code>true</code> if the range should count from
+     *                <code>to</code> to <code>from</code>.
+     * @throws IllegalArgumentException if <code>from</code> is greater than <code>to</code>.
+     */
+    protected IntRange(int from, int to, boolean reverse) {
+        if (from > to) {
+            throw new IllegalArgumentException("'from' must be less than or equal to 'to'");
+        }
+
+        this.from = from;
+        this.to = to;
+        this.reverse = reverse;
+    }
+
+    /**
+     * Determines if this object is equal to another object. Delegates to
+     * {@link AbstractList#equals(Object)} if <code>that</code> is anthing
+     * other than an {@link IntRange}.
+     * <p/>
+     * <p/>
+     * It is not necessary to override <code>hashCode</code>, as
+     * {@link AbstractList#hashCode()} provides a suitable hash code.<p>
+     * <p/>
+     * Note that equals is generally handled by {@link org.codehaus.groovy.runtime.DefaultGroovyMethods#equals(List,List)}
+     * instead of this method.
+     *
+     * @param that the object to compare
+     * @return <code>true</code> if the objects are equal
+     */
+    public boolean equals(Object that) {
+        return that instanceof IntRange ? equals((IntRange) that) : super.equals(that);
+    }
+
+    /**
+     * Compares an {@link IntRange} to another {@link IntRange}.
+     *
+     * @return <code>true</code> if the ranges are equal
+     * @param that the object to compare for equality
+     */
+    public boolean equals(IntRange that) {
+        return that != null && this.reverse == that.reverse && this.from == that.from && this.to == that.to;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Comparable getFrom() {
+        return new Integer(from);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Comparable getTo() {
+        return new Integer(to);
+    }
+
+    /**
+     * Gets the 'from' value as an integer.
+     *
+     * @return the 'from' value as an integer.
+     */
+    public int getFromInt() {
+        return from;
+    }
+
+    /**
+     * Gets the 'to' value as an integer.
+     *
+     * @return the 'to' value as an integer.
+     */
+    public int getToInt() {
+        return to;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isReverse() {
+        return reverse;
+    }
+
+    public boolean containsWithinBounds(Object o) {
+        return contains(o);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object get(int index) {
+        if (index < 0) {
+            throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
+        }
+        if (index >= size()) {
+            throw new IndexOutOfBoundsException("Index: " + index + " too big for range: " + this);
+        }
+        int value = reverse ? to - index : index + from;
+        return new Integer(value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int size() {
+        return to - from + 1;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Iterator iterator() {
+        return new IntRangeIterator();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List subList(int fromIndex, int toIndex) {
+        if (fromIndex < 0) {
+            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
+        }
+        if (toIndex > size()) {
+            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
+        }
+        if (fromIndex > toIndex) {
+            throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
+        }
+
+        if (fromIndex == toIndex) {
+            return new EmptyRange(new Integer(from));
+        }
+
+        return new IntRange(fromIndex + this.from, toIndex + this.from - 1, reverse);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return reverse ? "" + to + ".." + from : "" + from + ".." + to;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String inspect() {
+        return toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean contains(Object value) {
+        if (value instanceof Integer) {
+            Integer integer = (Integer) value;
+            int i = integer.intValue();
+            return i >= from && i <= to;
+        }
+        if (value instanceof BigInteger) {
+            BigInteger bigint = (BigInteger) value;
+            return bigint.compareTo(BigInteger.valueOf(from)) >= 0 &&
+                    bigint.compareTo(BigInteger.valueOf(to)) <= 0;
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean containsAll(Collection other) {
+        if (other instanceof IntRange) {
+            final IntRange range = (IntRange) other;
+            return this.from <= range.from && range.to <= this.to;
+        }
+        return super.containsAll(other);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void step(int step, Closure closure) {
+        if (reverse) {
+            step = -step;
+        }
+        if (step >= 0) {
+            int value = from;
+            while (value <= to) {
+                closure.call(new Integer(value));
+                value = value + step;
+            }
+        } else {
+            int value = to;
+            while (value >= from) {
+                closure.call(new Integer(value));
+                value = value + step;
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List step(int step) {
+        IteratorClosureAdapter adapter = new IteratorClosureAdapter(this);
+        step(step, adapter);
+        return adapter.asList();
+    }
+}
diff --git a/groovy/src/main/groovy/lang/Interceptor.java b/groovy/src/main/groovy/lang/Interceptor.java
new file mode 100644
index 0000000..00a2284
--- /dev/null
+++ b/groovy/src/main/groovy/lang/Interceptor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+/**
+ * Implementers of this interface can be registered in the ProxyMetaClass for
+ * notifications about method calls for objects managed by the ProxyMetaClass.
+ * See groovy/lang/InterceptorTest.groovy for details.
+ * @author Dierk Koenig
+ */
+public interface Interceptor {
+    /**
+     * This code is executed before the method is optionally called.
+     * @param object        receiver object for the method call
+     * @param methodName    name of the method to call
+     * @param arguments     arguments to the method call
+     * @return any arbitrary result that replaces the result of the
+     * original method call only if doInvoke() returns false and afterInvoke()
+     * relays this result.
+     */
+    Object beforeInvoke(Object object, String methodName, Object[] arguments);
+    /**
+     * This code is executed after the method is optionally called.
+     * @param object        receiver object for the called method
+     * @param methodName    name of the called method
+     * @param arguments     arguments to the called method
+     * @param result        result of the executed method call or result of beforeInvoke if method was not called
+     * @return any arbitrary result that can replace the result of the
+     * original method call. Typically, the result parameter is returned.
+     */
+    Object afterInvoke(Object object, String methodName, Object[] arguments, Object result);
+    /**
+     * @return whether the target method should be invoked at all.
+     */
+    boolean doInvoke();
+}
diff --git a/groovy/src/main/groovy/lang/MetaArrayLengthProperty.java b/groovy/src/main/groovy/lang/MetaArrayLengthProperty.java
new file mode 100644
index 0000000..42a9732
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MetaArrayLengthProperty.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+
+/**
+ * Represents a property on a bean which may have a getter and/or a setter
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MetaArrayLengthProperty extends MetaProperty {
+
+    public MetaArrayLengthProperty() {
+		super("length", int.class);
+    }
+
+    /**
+     * @return the property of the given object
+     * @throws Exception if the property could not be evaluated
+     */
+    public Object getProperty(Object object) {
+        return new Integer(java.lang.reflect.Array.getLength(object));
+    }
+
+    /**
+     * Sets the property on the given object to the new value
+     * 
+     * @param object on which to set the property
+     * @param newValue the new value of the property
+     * @throws RuntimeException if the property could not be set
+     */
+    public void setProperty(Object object, Object newValue) {
+		throw new ReadOnlyPropertyException("length", object.getClass());
+    }
+}
diff --git a/groovy/src/main/groovy/lang/MetaBeanProperty.java b/groovy/src/main/groovy/lang/MetaBeanProperty.java
new file mode 100644
index 0000000..7d1d471
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MetaBeanProperty.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.lang.reflect.Modifier;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+
+/**
+ * Represents a property on a bean which may have a getter and/or a setter
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+public class MetaBeanProperty extends MetaProperty {
+
+    private MetaMethod getter;
+    private MetaMethod setter;
+    private MetaFieldProperty field;
+
+    public MetaBeanProperty(String name, Class type, MetaMethod getter, MetaMethod setter) {
+        super(name, type);
+        this.getter = getter;
+        this.setter = setter;
+    }
+
+    /**
+     * Get the property of the given object.
+     *
+     * @param object which to be got
+     * @return the property of the given object
+     * @throws Exception if the property could not be evaluated
+     */
+    public Object getProperty(Object object) {
+        if (getter == null) {
+            //TODO: we probably need a WriteOnlyException class
+            throw new GroovyRuntimeException("Cannot read write-only property: " + name);
+        }
+        return getter.invoke(object, MetaClassHelper.EMPTY_ARRAY);
+    }
+
+    /**
+     * Set the property on the given object to the new value.
+     *
+     * @param object   on which to set the property
+     * @param newValue the new value of the property
+     * @throws RuntimeException if the property could not be set
+     */
+    public void setProperty(Object object, Object newValue) {
+        if (setter == null) {
+            throw new GroovyRuntimeException("Cannot set read-only property: " + name);
+        }
+        newValue = DefaultTypeTransformation.castToType(newValue, getType());
+        setter.invoke(object, new Object[]{newValue});
+    }
+
+    /**
+     * Get the getter method.
+     */
+    public MetaMethod getGetter() {
+        return getter;
+    }
+
+    /**
+     * Get the setter method.
+     */
+    public MetaMethod getSetter() {
+        return setter;
+    }
+
+    /**
+     * This is for MetaClass to patch up the object later when looking for get*() methods.
+     */
+    void setGetter(MetaMethod getter) {
+        this.getter = getter;
+    }
+
+    /**
+     * This is for MetaClass to patch up the object later when looking for set*() methods.
+     */
+    void setSetter(MetaMethod setter) {
+        this.setter = setter;
+    }
+
+    public int getModifiers() {
+        if (setter != null && getter == null) return setter.getModifiers();
+        if (getter != null && setter == null) return getter.getModifiers();
+        int modifiers = getter.getModifiers() | setter.getModifiers();
+        int visibility = 0;
+        if (Modifier.isPublic(modifiers)) visibility = Modifier.PUBLIC;
+        if (Modifier.isProtected(modifiers)) visibility = Modifier.PROTECTED;
+        if (Modifier.isPrivate(modifiers)) visibility = Modifier.PRIVATE;
+        int states = getter.getModifiers() & setter.getModifiers();
+        states &= ~(Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE);
+        states |= visibility;
+        return states;
+    }
+
+    public void setField(MetaFieldProperty f) {
+        this.field = f;
+    }
+
+    public MetaFieldProperty getField() {
+        return field;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/MetaClass.java b/groovy/src/main/groovy/lang/MetaClass.java
new file mode 100644
index 0000000..588377c
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MetaClass.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import org.codehaus.groovy.ast.ClassNode;
+
+import java.util.List;
+
+/**
+ * A MetaClass within Groovy defines the behaviour of any given Groovy or Java class. The MetaClass
+ * interface defines two parts. The client API, which is defined via the extend MetaObjectProtocol interface
+ * and the contract with the Groovy runtime system.
+ *
+ * In general the compiler and Groovy runtime engine interact with methods on this class whilst MetaClass
+ * clients interact with the method defined by the MetaObjectProtocol interface
+ *
+ *
+ * @see MetaClassImpl
+ * @see groovy.lang.MetaObjectProtocol
+ * 
+ * @author John Wilson
+ * @author Graeme Rocher
+ */
+public interface MetaClass extends MetaObjectProtocol {
+
+
+    /**
+     * <p>Invokes a method on the given receiver for the specified arguments. The sender is the class that invoked the method on the object.
+     * The MetaClass will attempt to establish the method to invoke based on the name and arguments provided.
+     *
+     * <p>The isCallToSuper and fromInsideClass help the Groovy runtime perform optimisations on the call to go directly
+     * to the super class if necessary
+     *
+     * @param sender The java.lang.Class instance that invoked the method
+     * @param receiver The object which the method was invoked on
+     * @param methodName The name of the method
+     * @param arguments The arguments to the method
+     * @param isCallToSuper Whether the method is a call to a super class method
+     * @param fromInsideClass Whether the call was invoked from the inside or the outside of the class
+     *
+     * @return The return value of the method
+     */
+     Object invokeMethod(Class sender, Object receiver, String methodName, Object[] arguments, boolean isCallToSuper, boolean fromInsideClass);
+
+
+    /**
+     * <p>Retrieves a property on the given receiver for the specified arguments. The sender is the class that is requesting the property from the object.
+     * The MetaClass will attempt to establish the method to invoke based on the name and arguments provided.
+     *
+     * <p>The isCallToSuper and fromInsideClass help the Groovy runtime perform optimisations on the call to go directly
+     * to the super class if necessary
+     *
+     * @param sender The java.lang.Class instance that requested the property
+     * @param receiver The Object which the property is being retrieved from
+     * @param property The name of the property
+     * @param isCallToSuper Whether the call is to a super class property
+     * @param fromInsideClass ??
+     *
+     * @return The properties value
+     */
+     Object getProperty(Class sender, Object receiver, String property, boolean isCallToSuper, boolean fromInsideClass);
+
+    /**
+     * <p>Retrieves a property on the given receiver for the specified arguments. The sender is the class that is requesting the property from the object.
+     * The MetaClass will attempt to establish the method to invoke based on the name and arguments provided.
+     *
+     * <p>The isCallToSuper and fromInsideClass help the Groovy runtime perform optimisations on the call to go directly
+     * to the super class if necessary
+     *
+     * @param sender The java.lang.Class instance that is mutating the property
+     * @param receiver The Object which the property is being set on
+     * @param property The name of the property
+     * @param value The new value of the property to set
+     * @param isCallToSuper Whether the call is to a super class property
+     * @param fromInsideClass ??
+     *
+     */
+     void setProperty(Class sender, Object receiver, String property, Object value, boolean isCallToSuper, boolean fromInsideClass);
+
+    /**
+     *
+     * <p>Attempts to invoke the methodMissing method otherwise throws a MissingMethodException
+     *
+     * @see groovy.lang.MissingMethodException
+     *
+     * @param instance The instance to invoke methodMissing on
+     * @param methodName The name of the method
+     * @param arguments The arguments to the method
+     * @return The results of methodMissing or throws MissingMethodException
+     */
+     Object invokeMissingMethod(Object instance, String methodName, Object[] arguments);
+
+    /**
+     * Invokes the propertyMissing method otherwise throws a MissingPropertyException
+     *
+     * @param instance The instance of the class
+     * @param propertyName The name of the property
+     * @param optionalValue The value of the property which could be null in the case of a getter
+     * @param isGetter Whether the missing property event was the result of a getter or a setter
+     * 
+     * @return The result of the propertyMissing method or throws MissingPropertyException
+     */
+     Object invokeMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter);
+
+
+    /**
+     * Retrieves the value of an attribute (field). This method is to support the Groovy runtime and not for general client API usage.
+     *
+     * @param sender The class of the object that requested the attribute
+     * @param receiver The instance
+     * @param messageName The name of the attribute
+     * @param useSuper Whether to look-up on the super class or not
+     * @return The attribute value
+     */
+     Object getAttribute(Class sender, Object receiver, String messageName, boolean useSuper);
+
+    /**
+     * Sets the value of an attribute (field). This method is to support the Groovy runtime and not for general client API usage.
+     *
+     * @param sender The class of the object that requested the attribute
+     * @param receiver The instance
+     * @param messageName The name of the attribute
+     * @param messageValue The value of the attribute
+     * @param useSuper Whether to look-up on the super class or not
+     * @param fromInsideClass Whether the call happened from the inside or the outside of a class
+     */
+     void setAttribute(Class sender, Object receiver, String messageName, Object messageValue, boolean useSuper, boolean fromInsideClass);
+    
+
+    /**
+     * complete the initlialisation process. After this method
+     * is called no methods should be added to the meta class.
+     * Invocation of methods or access to fields/proeprties is
+     * forbidden unless this method is called. This method 
+     * should contain any initialisation code, taking a longer
+     * time to complete. An example is the creation of the 
+     * Reflector. It is suggested to synchronize this 
+     * method.
+     */
+     void initialize();
+
+
+    /**
+     * Retrives a list of MetaProperty instances that the MetaClass has
+     *
+     * @see MetaProperty
+     *
+     * @return A list of MetaProperty instances
+     */
+     List getProperties();
+
+    /**
+     * Retrieves a list of MetaMethods held by the class
+     *
+     * @return A list of MetaMethods
+     *
+     */
+     List getMethods();
+     
+     /**
+      * Obtains a reference to the original AST for the MetaClass if it is available at runtime
+      *
+      * @return The original AST or null if it cannot be returned
+      */
+     ClassNode getClassNode();
+     
+     
+     /**
+      * Retrieves a list of MetaMethod instances held by this class
+      * @return A list of MetaMethod instances
+      */
+     List getMetaMethods();
+    
+
+     /**
+      *
+      * Internal method to support Groovy runtime. Not for client usage.
+      *
+      * @param numberOfConstructors The number of constructors
+      * @param arguments The arguments
+      *
+      * @return selected index
+      */
+     int selectConstructorAndTransformArguments(int numberOfConstructors, Object[] arguments);
+
+    /**
+     * Selects a method by name and argument classes. This method
+     * does not search for an exact match, it searches for a compatible
+     * method. For this the method selection mechanism is used as provided
+     * bye the implementation of this MetaClass. pickMethod may or may
+     * not used during the method selection process when invoking a method
+     * thereis no warranty for that.
+     *
+     * @return a matching MetaMethod or null
+     * @throws GroovyRuntimeException if there is more than one matching method
+     * @param methodName the name of the method to pick
+     * @param arguments the method arguments
+     */
+     MetaMethod pickMethod(String methodName, Class[] arguments);
+}
diff --git a/groovy/src/main/groovy/lang/MetaClassImpl.java b/groovy/src/main/groovy/lang/MetaClassImpl.java
new file mode 100644
index 0000000..56dccfe
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MetaClassImpl.java
@@ -0,0 +1,2628 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.classgen.BytecodeHelper;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.reflection.*;
+import org.codehaus.groovy.runtime.*;
+import org.codehaus.groovy.runtime.metaclass.*;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.runtime.wrappers.Wrapper;
+import org.objectweb.asm.ClassVisitor;
+
+import java.beans.*;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Allows methods to be dynamically added to existing classes at runtime
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Guillaume Laforge
+ * @author Jochen Theodorou
+ * @author Graeme Rocher
+ * @author Alex Tkachman
+ * @version $Revision$
+ * @see groovy.lang.MetaClass
+ */
+public class MetaClassImpl implements MetaClass, MutableMetaClass {
+
+    private static final String CLOSURE_CALL_METHOD = "call";
+    private static final String CLOSURE_DO_CALL_METHOD = "doCall";
+    private static final String CLOSURE_CURRY_METHOD = "curry";
+    protected static final String STATIC_METHOD_MISSING = "$static_methodMissing";
+    protected static final String STATIC_PROPERTY_MISSING = "$static_propertyMissing";
+    protected static final String METHOD_MISSING = "methodMissing";
+    protected static final String PROPERTY_MISSING = "propertyMissing";
+
+    private static final Class[] METHOD_MISSING_ARGS = new Class[]{String.class, Object.class};
+    private static final Class[] GETTER_MISSING_ARGS = new Class[]{String.class};
+    private static final Class[] SETTER_MISSING_ARGS = METHOD_MISSING_ARGS;
+
+    protected static final Logger LOG = Logger.getLogger(MetaClass.class.getName());
+    protected final Class theClass;
+    protected final CachedClass theCachedClass;
+
+    protected MetaClassRegistry registry;
+    protected final boolean isGroovyObject;
+    protected final boolean isMap;
+    private ClassNode classNode;
+
+    private final Index classPropertyIndex = new MethodIndex();
+    private Index classPropertyIndexForSuper = new MethodIndex();
+    private final SingleKeyHashMap staticPropertyIndex = new SingleKeyHashMap();
+
+    private final Map listeners = new HashMap();
+    private final Map methodCache = new ConcurrentReaderHashMap();
+    private final Map staticMethodCache = new ConcurrentReaderHashMap();
+    private FastArray constructors;
+    private final List allMethods = new ArrayList();
+    private List interfaceMethods;
+    private boolean initialized;
+    // we only need one of these that can be reused over and over.
+    private final MetaProperty arrayLengthProperty = new MetaArrayLengthProperty();
+    private static final MetaMethod AMBIGOUS_LISTENER_METHOD = new DummyMetaMethod();
+    private static final Object[] EMPTY_ARGUMENTS = {};
+    private final Set newGroovyMethodsSet = new HashSet();
+
+    private MetaMethod genericGetMethod;
+    private MetaMethod genericSetMethod;
+    private MetaMethod propertyMissingGet;
+    private MetaMethod propertyMissingSet;
+    private static final MetaMethod NULL_METHOD = new DummyMetaMethod();
+    private MetaMethod methodMissing;
+    private MetaMethodIndex.Header mainClassMethodHeader;
+    private final MetaMethodIndex metaMethodIndex;
+    protected static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
+
+
+    public MetaClassImpl(final Class theClass) {
+        this.theClass = theClass;
+        theCachedClass = ReflectionCache.getCachedClass(theClass);
+        this.isGroovyObject = GroovyObject.class.isAssignableFrom(theClass);
+        this.isMap = Map.class.isAssignableFrom(theClass);
+        this.registry = GroovySystem.getMetaClassRegistry();
+        metaMethodIndex = new MetaMethodIndex(theCachedClass);
+    }
+
+    public MetaClassImpl(MetaClassRegistry registry, final Class theClass) {
+        this(theClass);
+        this.registry = registry;
+        this.constructors = new FastArray(theCachedClass.getConstructors());
+    }
+
+    /**
+     * @see MetaObjectProtocol#respondsTo(Object,String, Object[])
+     */
+    public List respondsTo(Object obj, String name, Object[] argTypes) {
+        Class[] classes = castArgumentsToClassArray(argTypes);
+        MetaMethod m = getMetaMethod(name, classes);
+        List methods = new ArrayList();
+        if (m != null) {
+            methods.add(m);
+        }
+        return methods;
+    }
+
+    private Class[] castArgumentsToClassArray(Object[] argTypes) {
+        if (argTypes == null) return EMPTY_CLASS_ARRAY;
+        Class[] classes = new Class[argTypes.length];
+        for (int i = 0; i < argTypes.length; i++) {
+            Object argType = argTypes[i];
+            if (argType instanceof Class) {
+                classes[i] = (Class) argType;
+            } else if (argType == null) {
+                classes[i] = null;
+            } else {
+//                throw new IllegalArgumentException("Arguments to method [respondsTo] must be of type java.lang.Class!");
+                classes[i] = argType.getClass();
+            }
+        }
+        return classes;
+    }
+
+    /**
+     * @see MetaObjectProtocol#respondsTo(Object,String, Object[])
+     */
+    public List respondsTo(final Object obj, final String name) {
+        final Object o = getMethods(getTheClass(), name, false);
+        if (o instanceof FastArray)
+          return ((FastArray)o).toList();
+        else
+          return Collections.singletonList(o);
+    }
+
+    /**
+     * @see MetaObjectProtocol#hasProperty(Object,String)
+     */
+    public MetaProperty hasProperty(Object obj, String name) {
+        return getMetaProperty(name);
+    }
+
+    /**
+     * @see MetaObjectProtocol#getMetaProperty(String)
+     */
+    public MetaProperty getMetaProperty(String name) {
+        SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
+        if (propertyMap.containsKey(name)) {
+            return (MetaProperty) propertyMap.get(name);
+        } else if (staticPropertyIndex.containsKey(name)) {
+            return (MetaProperty) staticPropertyIndex.get(name);
+        } else {
+            propertyMap = classPropertyIndexForSuper.getNotNull(theCachedClass);
+            return (MetaProperty) propertyMap.get(name);
+        }
+    }
+
+    /**
+     * @see MetaObjectProtocol#getStaticMetaMethod(String, Object[])
+     */
+    public MetaMethod getStaticMetaMethod(String name, Object[] argTypes) {
+        Class[] classes = castArgumentsToClassArray(argTypes);
+        return retrieveStaticMethod(name, classes);
+    }
+
+
+    /**
+     * @see MetaObjectProtocol#getMetaMethod(String, Object[])
+     */
+    public MetaMethod getMetaMethod(String name, Object[] argTypes) {
+        Class[] classes = castArgumentsToClassArray(argTypes);
+        return pickMethod(name, classes);
+    }
+
+    public Class getTheClass() {
+        return this.theClass;
+    }
+
+    public boolean isGroovyObject() {
+        return isGroovyObject;
+    }
+
+    private void fillMethodIndex() {
+        mainClassMethodHeader = metaMethodIndex.getHeader(theClass);
+        LinkedList superClasses = getSuperClasses();
+        CachedClass firstGroovySuper = calcFirstGroovySuperClass(superClasses);
+
+        Set interfaces = theCachedClass.getInterfaces();
+        addInterfaceMethods(interfaces);
+
+        populateMethods(superClasses, firstGroovySuper);
+
+        inheritInterfaceNewMetaMethods(interfaces);
+        if (isGroovyObject) {
+          metaMethodIndex.copyMethodsToSuper();
+
+          connectMultimethods(superClasses, firstGroovySuper);
+          removeMultimethodsOverloadedWithPrivateMethods();
+
+          replaceWithMOPCalls(theCachedClass.mopMethods);
+        }
+    }
+
+    private void populateMethods(LinkedList superClasses, CachedClass firstGroovySuper) {
+        Iterator iter = superClasses.iterator();
+
+        MetaMethodIndex.Header header = metaMethodIndex.getHeader(firstGroovySuper.getCachedClass());
+        CachedClass c;
+        for (; iter.hasNext();) {
+            c = (CachedClass) iter.next();
+
+            CachedMethod[] cachedMethods = c.getMethods();
+            for (int i = 0; i < cachedMethods.length; i++) {
+                MetaMethod metaMethod = cachedMethods[i].getReflectionMetaMethod();
+                addToAllMethodsIfPublic(metaMethod);
+                if (!metaMethod.isPrivate() || c == firstGroovySuper)
+                  addMetaMethodToIndex(metaMethod, header);
+            }
+
+            MetaMethod[] cachedMethods1 = c.getNewMetaMethods();
+            for (int i = 0; i < cachedMethods1.length; i++) {
+                final MetaMethod method = cachedMethods1[i];
+
+                if (!newGroovyMethodsSet.contains(method)) {
+                    newGroovyMethodsSet.add(method);
+                    addMetaMethodToIndex(method, header);
+                }
+            }
+
+            if (c == firstGroovySuper)
+              break;
+        }
+
+        MetaMethodIndex.Header last = header;
+        for (;iter.hasNext();) {
+            c = (CachedClass) iter.next();
+            header = metaMethodIndex.getHeader(c.getCachedClass());
+
+            if (last != null) {
+                metaMethodIndex.copyNonPrivateMethods(last, header);
+            }
+            last = header;
+
+            CachedMethod[] cachedMethods = c.getMethods();
+            for (int i = 0; i < cachedMethods.length; i++) {
+                MetaMethod metaMethod = cachedMethods[i].getReflectionMetaMethod();
+                addToAllMethodsIfPublic(metaMethod);
+                addMetaMethodToIndex(metaMethod, header);
+            }
+
+            MetaMethod[] cachedMethods1 = c.getNewMetaMethods();
+            for (int i = 0; i < cachedMethods1.length; i++) {
+                final MetaMethod method = cachedMethods1[i];
+
+                if (!newGroovyMethodsSet.contains(method)) {
+                    newGroovyMethodsSet.add(method);
+                    addMetaMethodToIndex(method, header);
+                }
+            }
+        }
+    }
+
+    private void addInterfaceMethods(Set interfaces) {
+        MetaMethodIndex.Header header = metaMethodIndex.getHeader(theClass);
+        for (Iterator iter = interfaces.iterator(); iter.hasNext();) {
+            CachedClass c = (CachedClass) iter.next();
+            final CachedMethod[] m = c.getMethods();
+            for (int i=0; i != m.length; ++i) {
+                MetaMethod method = m [i].getReflectionMetaMethod();
+                String name = method.getName();
+                MetaMethodIndex.Entry e = metaMethodIndex.getOrPutMethods(name, header);
+                e.methods = metaMethodIndex.addMethodToList(e.methods, method);
+            }
+        }
+    }
+
+    private LinkedList getSuperClasses() {
+        LinkedList superClasses = new LinkedList();
+
+        if (theClass.isInterface()) {
+            superClasses.addFirst(ReflectionCache.OBJECT_CLASS);
+        } else {
+            for (CachedClass c = theCachedClass; c != null; c = c.getCachedSuperClass()) {
+                superClasses.addFirst(c);
+            }
+            if (theCachedClass.isArray && theClass != Object[].class && !theClass.getComponentType().isPrimitive()) {
+                superClasses.addFirst(ReflectionCache.OBJECT_ARRAY_CLASS);
+            }
+        }
+        return superClasses;
+    }
+
+    private void removeMultimethodsOverloadedWithPrivateMethods() {
+        MethodIndexAction mia = new MethodIndexAction() {
+            public boolean skipClass(Class clazz) {
+                return clazz == theClass;
+            }
+
+            public void methodNameAction(Class clazz, MetaMethodIndex.Entry e) {
+                if (e.methods == null)
+                  return;
+                
+                boolean hasPrivate = false;
+                if (e.methods instanceof FastArray) {
+                    FastArray methods = (FastArray) e.methods;
+                    final int len = methods.size();
+                    final Object[] data = methods.getArray();
+                    for (int i = 0; i != len; ++i) {
+                        MetaMethod method = (MetaMethod) data[i];
+                        if (method.isPrivate() && clazz == method.getDeclaringClass().getCachedClass()) {
+                            hasPrivate = true;
+                            break;
+                        }
+                    }
+                }
+                else {
+                    MetaMethod method = (MetaMethod) e.methods;
+                    if (method.isPrivate() && clazz == method.getDeclaringClass().getCachedClass()) {
+                       hasPrivate = true;
+                    }
+                }
+
+                if (!hasPrivate) return;
+
+                // We have private methods for that name, so remove the
+                // multimethods. That is the same as in our index for
+                // super, so just copy the list from there. It is not
+                // possible to use a pointer here, because the methods
+                // in the index for super are replaced later by MOP
+                // methods like super$5$foo
+                final Object o = e.methodsForSuper;
+                if (o instanceof FastArray)
+                  e.methods = ((FastArray) o).copy();
+                else
+                  e.methods = o;
+            }
+        };
+        mia.iterate();
+    }
+
+
+    private void replaceWithMOPCalls(final CachedMethod[] mopMethods) {
+        // no MOP methods if not a child of GroovyObject
+        if (!isGroovyObject) return;
+
+        class MOPIter extends MethodIndexAction {
+            boolean useThis;
+            public boolean skipClass(CachedClass clazz) {
+                return !useThis && clazz == theCachedClass;
+            }
+
+            public void methodNameAction(Class clazz, MetaMethodIndex.Entry e) {
+                if (useThis) {
+                    if (e.methods == null)
+                      return;
+
+                    if (e.methods instanceof FastArray) {
+                        FastArray methods = (FastArray) e.methods;
+                        processFastArray(methods);
+                    }
+                    else {
+                        MetaMethod method = (MetaMethod) e.methods;
+                        if (method instanceof NewMetaMethod)
+                          return;
+                        if (useThis ^ (method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0)
+                          return;
+                        String mopName = method.getMopName();
+                        int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
+                        if (index >= 0) {
+                            int from = index;
+                            while (from > 0 && mopMethods[from-1].getName().equals(mopName))
+                              from--;
+                            int to = index;
+                            while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName))
+                              to++;
+
+                            int matchingMethod = findMatchingMethod(mopMethods, from, to, method);
+                            if (matchingMethod != -1) {
+                                e.methods = mopMethods[matchingMethod].getReflectionMetaMethod();
+                            }
+                        }
+                    }
+                }
+                else {
+                    if (e.methodsForSuper == null)
+                      return;
+
+                    if (e.methodsForSuper instanceof FastArray) {
+                        FastArray methods = (FastArray) e.methodsForSuper;
+                        processFastArray(methods);
+                    }
+                    else {
+                        MetaMethod method = (MetaMethod) e.methodsForSuper;
+                        if (method instanceof NewMetaMethod)
+                          return;
+                        if (useThis ^ (method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0)
+                          return;
+                        String mopName = method.getMopName();
+                        int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
+                        if (index >= 0) {
+                            int from = index;
+                            while (from > 0 && mopMethods[from-1].getName().equals(mopName))
+                              from--;
+                            int to = index;
+                            while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName))
+                              to++;
+
+                            int matchingMethod = findMatchingMethod(mopMethods, from, to, method);
+                            if (matchingMethod != -1) {
+                                e.methodsForSuper = mopMethods[matchingMethod].getReflectionMetaMethod();
+                            }
+                        }
+                    }
+                }
+            }
+
+            private void processFastArray(FastArray methods) {
+                final int len = methods.size();
+                final Object[] data = methods.getArray();
+                for (int i = 0; i != len; ++i) {
+                    MetaMethod method = (MetaMethod) data[i];
+                    if (method instanceof NewMetaMethod)
+                      continue;
+                    if (useThis ^ (method.getModifiers() & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0) continue;
+                    String mopName = method.getMopName();
+                    int index = Arrays.binarySearch(mopMethods, mopName, CachedClass.CachedMethodComparatorWithString.INSTANCE);
+                    if (index >= 0) {
+                        int from = index;
+                        while (from > 0 && mopMethods[from-1].getName().equals(mopName))
+                          from--;
+                        int to = index;
+                        while (to < mopMethods.length-1 && mopMethods[to+1].getName().equals(mopName))
+                          to++;
+
+                        int matchingMethod = findMatchingMethod(mopMethods, from, to, method);
+                        if (matchingMethod != -1) {
+                            methods.set(i, mopMethods[matchingMethod].getReflectionMetaMethod());
+                        }
+                    }
+                }
+            }
+        }
+        MOPIter iter = new MOPIter();
+
+        // replace all calls for super with the correct MOP method
+        iter.useThis = false;
+        iter.iterate();
+        // replace all calls for this with the correct MOP method
+        iter.useThis = true;
+        iter.iterate();
+    }
+
+    private void inheritInterfaceNewMetaMethods(Set interfaces) {
+        // add methods declared by DGM for interfaces
+        for (Iterator it = interfaces.iterator(); it.hasNext(); ) {
+            CachedClass cls = (CachedClass) it.next();
+            MetaMethod methods [] = cls.getNewMetaMethods();
+            for (int i = 0; i < methods.length; i++) {
+                MetaMethod method = methods[i];
+                if (!newGroovyMethodsSet.contains(method)) {
+                    newGroovyMethodsSet.add(method);
+                }
+                MetaMethodIndex.Entry e = metaMethodIndex.getOrPutMethods(method.getName(), mainClassMethodHeader);
+                e.methods = metaMethodIndex.addMethodToList(e.methods, method);
+            }
+        }
+    }
+
+    private void populateInterfaces(Set interfaces) {
+//        for (Iterator iter = interfaces.iterator(); iter.hasNext();) {
+//            CachedClass iClass = (CachedClass) iter.next();
+//            classIndex.copyNonPrivateMethods(mainClassMethodHeader, classIndex.getHeader(iClass.getCachedClass()));
+//        }
+    }
+
+    private void connectMultimethods(List superClasses, CachedClass firstGroovyClass) {
+        superClasses = DefaultGroovyMethods.reverse(superClasses);
+        MetaMethodIndex.Header last = null;
+        for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
+            CachedClass c = (CachedClass) iter.next();
+            MetaMethodIndex.Header methodIndex = metaMethodIndex.getHeader(c.getCachedClass());
+            // We don't copy DGM methods to superclasses' indexes
+            // The reason we can do that is particular set of DGM methods in use,
+            // if at some point we will define DGM method for some Groovy class or
+            // for a class derived from such, we will need to revise this condition.
+            // It saves us a lot of space and some noticable time
+            if (last != null) metaMethodIndex.copyNonPrivateNonNewMetaMethods(last, methodIndex);
+            last = methodIndex;
+
+            if (c == firstGroovyClass)
+              break;
+        }
+    }
+
+    private void inheritMethods(Collection superClasses, CachedClass firstGroovySuper) {
+        Iterator iter = superClasses.iterator();
+        for (; iter.hasNext();) {
+            CachedClass c = (CachedClass) iter.next();
+            if (c == firstGroovySuper.getCachedSuperClass())
+              break;
+        }
+
+        MetaMethodIndex.Header last = null;
+        for (; iter.hasNext();) {
+            CachedClass c = (CachedClass) iter.next();
+            MetaMethodIndex.Header methodIndex = metaMethodIndex.getHeader(c.getCachedClass());
+            if (last != null) {
+                metaMethodIndex.copyNonPrivateMethods(last, methodIndex);
+            }
+            last = methodIndex;
+        }
+    }
+
+    private CachedClass calcFirstGroovySuperClass(Collection superClasses) {
+        if (theCachedClass.isInterface)
+          return ReflectionCache.OBJECT_CLASS;
+        
+        CachedClass firstGroovy = null;
+        Iterator iter = superClasses.iterator();
+        for (; iter.hasNext();) {
+            CachedClass c = (CachedClass) iter.next();
+            if (GroovyObject.class.isAssignableFrom(c.getCachedClass())) {
+              firstGroovy = c;
+              break;
+            }
+        }
+
+        if (firstGroovy == null)
+          firstGroovy = theCachedClass;
+        else {
+            if (firstGroovy.getCachedClass() == GroovyObjectSupport.class && iter.hasNext()) {
+                firstGroovy = (CachedClass) iter.next();
+                if (firstGroovy.getCachedClass() == Closure.class && iter.hasNext()) {
+                    firstGroovy = (CachedClass) iter.next();
+                }
+            }
+        }
+
+        return GroovyObject.class.isAssignableFrom(firstGroovy.getCachedClass()) ? firstGroovy.getCachedSuperClass() : firstGroovy;
+    }
+
+    /**
+     * @return all the normal instance methods avaiable on this class for the
+     *         given name
+     */
+    private Object getMethods(Class sender, String name, boolean isCallToSuper) {
+        Object answer;
+
+        final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, name);
+        if (entry == null)
+            answer = FastArray.EMPTY_LIST;
+        else
+            if (isCallToSuper) {
+                answer = entry.methodsForSuper;
+            } else {
+                answer = entry.methods;
+            }
+
+        if (answer == null) answer = FastArray.EMPTY_LIST;
+
+        if (!isCallToSuper && GroovyCategorySupport.hasCategoryInAnyThread()) {
+            List used = GroovyCategorySupport.getCategoryMethods(sender, name);
+            if (used != null) {
+                FastArray arr;
+                if (answer instanceof MetaMethod) {
+                    arr = new FastArray();
+                    arr.add(answer);
+                }
+                else
+                  arr = ((FastArray) answer).copy();
+
+                for (Iterator iter = used.iterator(); iter.hasNext();) {
+                    MetaMethod element = (MetaMethod) iter.next();
+                    filterMatchingMethodForCategory(arr, element);
+                }
+                answer = arr;
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * @return all the normal static methods avaiable on this class for the
+     *         given name
+     */
+    private Object getStaticMethods(Class sender, String name) {
+        final MetaMethodIndex.Entry entry = metaMethodIndex.getMethods(sender, name);
+        if (entry == null)
+            return FastArray.EMPTY_LIST;
+        Object answer = entry.staticMethods;
+        if (answer == null)
+            return FastArray.EMPTY_LIST;
+        return answer;
+    }
+
+    public boolean isModified() {
+        return false;  // MetaClassImpl not designed for modification, just return false
+    }
+
+    public void addNewInstanceMethod(Method method) {
+        final CachedMethod cachedMethod = CachedMethod.find(method);
+        NewInstanceMetaMethod newMethod = new NewInstanceMetaMethod(cachedMethod);
+        final CachedClass declaringClass = newMethod.getDeclaringClass();
+        addNewInstanceMethodToIndex(newMethod, metaMethodIndex.getHeader(declaringClass.getCachedClass()));
+    }
+
+    private void addNewInstanceMethodToIndex(MetaMethod newMethod, MetaMethodIndex.Header header) {
+        if (!newGroovyMethodsSet.contains(newMethod)) {
+            newGroovyMethodsSet.add(newMethod);
+            addMetaMethodToIndex(newMethod, header);
+        }
+    }
+
+    public void addNewStaticMethod(Method method) {
+        final CachedMethod cachedMethod = CachedMethod.find(method);
+        NewStaticMetaMethod newMethod = new NewStaticMetaMethod(cachedMethod);
+        final CachedClass declaringClass = newMethod.getDeclaringClass();
+        addNewStaticMethodToIndex(newMethod, metaMethodIndex.getHeader(declaringClass.getCachedClass()));
+    }
+
+    private void addNewStaticMethodToIndex(MetaMethod newMethod, MetaMethodIndex.Header header) {
+        if (!newGroovyMethodsSet.contains(newMethod)) {
+            newGroovyMethodsSet.add(newMethod);
+            addMetaMethodToIndex(newMethod, header);
+        }
+    }
+
+    private void unwrap(Object[] arguments) {
+        //
+        // Temp code to ignore wrapped parameters
+        // The New MOP will deal with these properly
+        //
+        for (int i = 0; i != arguments.length; i++) {
+            if (arguments[i] instanceof Wrapper) {
+                arguments[i] = ((Wrapper) arguments[i]).unwrap();
+            }
+        }
+    }
+
+    public Object invokeMethod(Object object, String methodName, Object arguments) {
+        if (arguments == null) {
+            return invokeMethod(object, methodName, MetaClassHelper.EMPTY_ARRAY);
+        }
+        if (arguments instanceof Tuple) {
+            Tuple tuple = (Tuple) arguments;
+            return invokeMethod(object, methodName, tuple.toArray());
+        }
+        if (arguments instanceof Object[]) {
+            return invokeMethod(object, methodName, (Object[]) arguments);
+        } else {
+            return invokeMethod(object, methodName, new Object[]{arguments});
+        }
+    }
+
+    public Object invokeMissingMethod(Object instance, String methodName, Object[] arguments) {
+        return invokeMissingMethod(instance, methodName, arguments, null);
+    }
+
+    public Object invokeMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
+
+        if (!(instance instanceof Class)) {
+            if (isGetter && propertyMissingGet != null) {
+                return propertyMissingGet.invoke(instance, new Object[]{propertyName});
+            } else {
+                if (propertyMissingSet != null)
+                    return propertyMissingSet.invoke(instance, new Object[]{propertyName, optionalValue});
+            }
+        }
+
+        throw new MissingPropertyExceptionNoStack(propertyName, theClass);
+    }
+
+    private Object invokeMissingMethod(Object instance, String methodName, Object[] arguments, RuntimeException original) {
+        if (methodMissing != null) {
+            return methodMissing.invoke(instance, new Object[]{methodName, arguments});
+        } else if (original != null) throw original;
+        else throw new MissingMethodExceptionNoStack(methodName, theClass, arguments, false);
+    }
+
+
+    /**
+     * Hook to deal with the case of MissingProperty for static properties. The method will look attempt to look up
+     * "propertyMissing" handlers and invoke them otherwise thrown a MissingPropertyException
+     *
+     * @param instance      The instance
+     * @param propertyName  The name of the property
+     * @param optionalValue The value in the case of a setter
+     * @param isGetter      True if its a getter
+     * @return The value in the case of a getter or a MissingPropertyException
+     */
+    protected Object invokeStaticMissingProperty(Object instance, String propertyName, Object optionalValue, boolean isGetter) {
+        MetaClass mc = instance instanceof Class ? registry.getMetaClass((Class) instance) : this;
+        if (isGetter) {
+            MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, GETTER_MISSING_ARGS);
+            if (propertyMissing != null) {
+                return propertyMissing.invoke(instance, new Object[]{propertyName});
+            }
+        } else {
+            MetaMethod propertyMissing = mc.getMetaMethod(STATIC_PROPERTY_MISSING, SETTER_MISSING_ARGS);
+            if (propertyMissing != null) {
+                return propertyMissing.invoke(instance, new Object[]{propertyName, optionalValue});
+            }
+        }
+
+        if (instance instanceof Class) {
+            throw new MissingPropertyException(propertyName, (Class) instance);
+        }
+        throw new MissingPropertyException(propertyName, theClass);
+    }
+
+    /**
+     * Invokes the given method on the object.
+     * TODO: should this be deprecated? If so, we have to propogate to many places.
+     */
+    public Object invokeMethod(Object object, String methodName, Object[] originalArguments) {
+        return invokeMethod(theClass, object, methodName, originalArguments, false, false);
+    }
+
+
+    /**
+     * Invokes the given method on the object.
+     */
+    public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {
+        checkInitalised();
+        if (object == null) {
+            throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");
+        }
+        if (LOG.isLoggable(Level.FINER)) {
+            MetaClassHelper.logMethodCall(object, methodName, originalArguments);
+        }
+        final Object[] arguments = originalArguments == null ? EMPTY_ARGUMENTS : originalArguments;
+        final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+
+        unwrap(arguments);
+
+        MetaMethod method = getMethodWithCaching(sender, methodName, argClasses, isCallToSuper);
+
+        if (method == null && arguments.length == 1 && arguments[0] instanceof List) {
+            Object[] newArguments = ((List) arguments[0]).toArray();
+            Class[] newArgClasses = MetaClassHelper.convertToTypeArray(newArguments);
+            method = getMethodWithCaching(sender, methodName, newArgClasses, isCallToSuper);
+            if (method != null) {
+                method = new TransformMetaMethod(method) {
+                    public Object invoke(Object object, Object[] arguments) {
+                        Object firstArgument = arguments[0];
+                        List list = (List) firstArgument;
+                        arguments = list.toArray();
+                        return super.invoke(object, arguments);
+                    }
+                };
+            }
+        }
+
+        final boolean isClosure = object instanceof Closure;
+        if (isClosure) {
+            final Closure closure = (Closure) object;
+
+            final Object owner = closure.getOwner();
+
+            if (CLOSURE_CALL_METHOD.equals(methodName) || CLOSURE_DO_CALL_METHOD.equals(methodName)) {
+                final Class objectClass = object.getClass();
+                if (objectClass == MethodClosure.class) {
+                    final MethodClosure mc = (MethodClosure) object;
+                    methodName = mc.getMethod();
+                    final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass();
+                    final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
+                    return ownerMetaClass.invokeMethod(ownerClass, owner, methodName, arguments, false, false);
+                } else if (objectClass == CurriedClosure.class) {
+                    final CurriedClosure cc = (CurriedClosure) object;
+                    // change the arguments for an uncurried call
+                    final Object[] curriedArguments = cc.getUncurriedArguments(arguments);
+                    final Class ownerClass = owner instanceof Class ? (Class) owner : owner.getClass();
+                    final MetaClass ownerMetaClass = registry.getMetaClass(ownerClass);
+                    return ownerMetaClass.invokeMethod(owner, methodName, curriedArguments);
+                }
+                if (method==null) invokeMissingMethod(object,methodName,arguments);
+            } else if (CLOSURE_CURRY_METHOD.equals(methodName)) {
+                return closure.curry(arguments);
+            }
+
+            final Object delegate = closure.getDelegate();
+            final boolean isClosureNotOwner = owner != closure;
+            final int resolveStrategy = closure.getResolveStrategy();
+
+            switch (resolveStrategy) {
+                case Closure.TO_SELF:
+                    method = closure.getMetaClass().pickMethod(methodName, argClasses);
+                    if (method != null) return method.invoke(closure, arguments);
+                    break;
+                case Closure.DELEGATE_ONLY:
+                    if (method == null && delegate != closure && delegate != null) {
+                        MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
+                        method = delegateMetaClass.pickMethod(methodName, argClasses);
+                        if (method != null)
+                            return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
+                        else if (delegate != closure && (delegate instanceof GroovyObject)) {
+                            return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
+                        }
+                    }
+                    break;
+                case Closure.OWNER_ONLY:
+                    if (method == null && owner != closure) {
+                        MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
+                        return ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
+                    }
+                    break;
+                case Closure.DELEGATE_FIRST:
+                    if (method == null && delegate != closure && delegate != null) {
+                        MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
+                        method = delegateMetaClass.pickMethod(methodName, argClasses);
+                        if (method != null)
+                            return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
+                    }
+                    if (method == null && owner != closure) {
+                        MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
+                        method = ownerMetaClass.pickMethod(methodName, argClasses);
+                        if (method != null) return ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
+                    }
+                    if (method == null && resolveStrategy != Closure.TO_SELF) {
+                        // still no methods found, test if delegate or owner are GroovyObjects
+                        // and invoke the method on them if so.
+                        MissingMethodException last = null;
+                        if (delegate != closure && (delegate instanceof GroovyObject)) {
+                            try {
+                                return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
+                            } catch (MissingMethodException mme) {
+                                if (last == null) last = mme;
+                            }
+                        }
+                        if (isClosureNotOwner && (owner instanceof GroovyObject)) {
+                            try {
+                                return invokeMethodOnGroovyObject(methodName, originalArguments, owner);
+                            } catch (MissingMethodException mme) {
+                                last = mme;
+                            }
+                        }
+                        if (last != null) return invokeMissingMethod(object, methodName, originalArguments, last);
+                    }
+
+                    break;
+                default:
+                    if (method == null && owner != closure) {
+                        MetaClass ownerMetaClass = lookupObjectMetaClass(owner);
+                        method = ownerMetaClass.pickMethod(methodName, argClasses);
+                        if (method != null) return ownerMetaClass.invokeMethod(owner, methodName, originalArguments);
+                    }
+                    if (method == null && delegate != closure && delegate != null) {
+                        MetaClass delegateMetaClass = lookupObjectMetaClass(delegate);
+                        method = delegateMetaClass.pickMethod(methodName, argClasses);
+                        if (method != null)
+                            return delegateMetaClass.invokeMethod(delegate, methodName, originalArguments);
+                    }
+                    if (method == null && resolveStrategy != Closure.TO_SELF) {
+                        // still no methods found, test if delegate or owner are GroovyObjects
+                        // and invoke the method on them if so.
+                        MissingMethodException last = null;
+                        if (isClosureNotOwner && (owner instanceof GroovyObject)) {
+                            try {
+                                return invokeMethodOnGroovyObject(methodName, originalArguments, owner);
+                            } catch (MissingMethodException mme) {
+                                if (last == null) last = mme;
+                            }
+                        }
+                        if (delegate != closure && (delegate instanceof GroovyObject)) {
+                            try {
+                                return invokeMethodOnGroovyObject(methodName, originalArguments, delegate);
+                            } catch (MissingMethodException mme) {
+                                last = mme;
+                            }
+                        }
+                        if (last != null) return invokeMissingMethod(object, methodName, originalArguments, last);
+                    }
+            }
+        }
+
+        if (method != null) {
+            return MetaClassHelper.doMethodInvoke(object, method, arguments);
+        } else {
+            // if no method was found, try to find a closure defined as a field of the class and run it
+            Object value = null;
+            final MetaProperty metaProperty = this.getMetaProperty(theCachedClass, methodName, false, false);
+            if (metaProperty != null)
+              value = metaProperty.getProperty(object);
+            else {
+                if (object instanceof Map)
+                  value = ((Map)object).get(methodName);
+            }
+
+            if (value instanceof Closure) {  // This test ensures that value != this If you ever change this ensure that value != this
+                Closure closure = (Closure) value;
+                MetaClass delegateMetaClass = closure.getMetaClass();
+                return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);
+            }
+
+            return invokeMissingMethod(object, methodName, originalArguments);
+        }
+    }
+
+    private MetaClass lookupObjectMetaClass(Object object) {
+        if (object instanceof GroovyObject) {
+            GroovyObject go = (GroovyObject) object;
+            return go.getMetaClass();
+        }
+        Class ownerClass = object.getClass();
+        if (ownerClass == Class.class) ownerClass = (Class) object;
+        MetaClass metaClass = registry.getMetaClass(ownerClass);
+        return metaClass;
+    }
+
+    private Object invokeMethodOnGroovyObject(String methodName, Object[] originalArguments, Object owner) {
+        GroovyObject go = (GroovyObject) owner;
+        return go.invokeMethod(methodName, originalArguments);
+    }
+
+    public MetaMethod getMethodWithCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
+        // lets try use the cache to find the method
+        if (GroovyCategorySupport.hasCategoryInAnyThread() && !isCallToSuper) {
+            return getMethodWithoutCaching(sender, methodName, arguments, isCallToSuper);
+        } else {
+            MethodKey methodKey = new DefaultMethodKey(sender, methodName, arguments, isCallToSuper);
+            MetaMethod method = (MetaMethod) methodCache.get(methodKey);
+            if (method == null) {
+                method = getMethodWithoutCaching(sender, methodName, arguments, isCallToSuper);
+                if (method == null)
+                    method = NULL_METHOD;
+                cacheInstanceMethod(methodKey, method);
+                if (method == NULL_METHOD)
+                    method = null;
+            } else {
+                if (method == NULL_METHOD)
+                    method = null;
+            }
+            return method;
+        }
+    }
+
+    protected void cacheInstanceMethod(MethodKey key, MetaMethod method) {
+        if (method != null && method.isCacheable()) {
+            methodCache.put(key, method);
+        }
+    }
+
+    protected void cacheStaticMethod(MethodKey key, MetaMethod method) {
+        if (method != null && method.isCacheable()) {
+            staticMethodCache.put(key, method);
+        }
+    }
+
+
+    public Constructor retrieveConstructor(Class[] arguments) {
+        CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, arguments, false);
+        if (constructor != null) {
+            return constructor.cachedConstructor;
+        }
+        constructor = (CachedConstructor) chooseMethod("<init>", constructors, arguments, true);
+        if (constructor != null) {
+            return constructor.cachedConstructor;
+        }
+        return null;
+    }
+
+    public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
+        MethodKey methodKey = new DefaultMethodKey(theClass, methodName, arguments, false);
+        MetaMethod method = (MetaMethod) staticMethodCache.get(methodKey);
+        if (method == null) {
+            method = pickStaticMethod(methodName, arguments);
+            cacheStaticMethod(methodKey.createCopy(), method);
+        }
+        return method;
+    }
+
+    public MetaMethod getMethodWithoutCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {
+        MetaMethod method = null;
+        Object methods = getMethods(sender, methodName, isCallToSuper);
+        if (methods != null) {
+            method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
+        }
+        return method;
+    }
+
+    public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
+        checkInitalised();
+        if (LOG.isLoggable(Level.FINER)) {
+            MetaClassHelper.logMethodCall(object, methodName, arguments);
+        }
+
+        final Class sender = object instanceof Class ? (Class) object : object.getClass();
+        if (sender != theClass) {
+            MetaClass mc = registry.getMetaClass(sender);
+            return mc.invokeStaticMethod(sender, methodName, arguments);
+        }
+        if (sender == Class.class) {
+            return invokeMethod(object, methodName, arguments);
+        }
+
+        if (arguments == null) arguments = EMPTY_ARGUMENTS;
+        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+
+        MetaMethod method = retrieveStaticMethod(methodName, argClasses);
+        // lets try use the cache to find the method
+
+        if (method != null) {
+            unwrap(arguments);
+            return MetaClassHelper.doMethodInvoke(object, method, arguments);
+        }
+        Object prop = null;
+        try {
+            prop = getProperty(theClass, theClass, methodName, false, false);
+        } catch (MissingPropertyException mpe) {
+            // ignore
+        }
+
+        if (prop instanceof Closure) {
+            return invokeStaticClosureProperty(arguments, prop);
+        }
+
+        Object[] originalArguments = (Object[]) arguments.clone();
+        unwrap(arguments);
+
+        Class superClass = sender.getSuperclass();
+        while (superClass != Object.class && superClass != null) {
+            MetaClass mc = registry.getMetaClass(superClass);
+            method = mc.getStaticMetaMethod(methodName, argClasses);
+            if (method != null) return MetaClassHelper.doMethodInvoke(object, method, arguments);
+
+            try {
+                prop = mc.getProperty(superClass, superClass, methodName, false, false);
+            } catch (MissingPropertyException mpe) {
+                // ignore
+            }
+
+            if (prop instanceof Closure) {
+                return invokeStaticClosureProperty(originalArguments, prop);
+            }
+
+            superClass = superClass.getSuperclass();
+        }
+
+        return invokeStaticMissingMethod(sender, methodName, arguments);
+    }
+
+    private Object invokeStaticClosureProperty(Object[] originalArguments, Object prop) {
+        Closure closure = (Closure) prop;
+        MetaClass delegateMetaClass = closure.getMetaClass();
+        return delegateMetaClass.invokeMethod(closure.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, false);
+    }
+
+    private Object invokeStaticMissingMethod(Class sender, String methodName, Object[] arguments) {
+        MetaMethod metaMethod = getStaticMetaMethod(STATIC_METHOD_MISSING, METHOD_MISSING_ARGS);
+        if (metaMethod != null) {
+            return metaMethod.invoke(sender, new Object[]{methodName, arguments});
+        }
+        throw new MissingMethodException(methodName, sender, arguments, true);
+    }
+
+    private MetaMethod pickStaticMethod(String methodName, Class[] arguments) {
+        MetaMethod method = null;
+        Object methods = getStaticMethods(theClass, methodName);
+
+        if (!(methods instanceof FastArray) || !((FastArray)methods).isEmpty()) {
+            method = (MetaMethod) chooseMethod(methodName, methods, arguments, false);
+        }
+        if (method == null && theClass != Class.class) {
+            MetaClass classMetaClass = registry.getMetaClass(Class.class);
+            method = classMetaClass.pickMethod(methodName, arguments);
+        }
+        if (method == null) {
+            method = (MetaMethod) chooseMethod(methodName, methods, MetaClassHelper.convertToTypeArray(arguments), true);
+        }
+        return method;
+    }
+
+    /**
+     * Warning, this method will be removed
+     *
+     * @deprecated use invokeConstructor instead
+     */
+    public Object invokeConstructorAt(Class at, Object[] arguments) {
+        return invokeConstructor(arguments);
+    }
+
+    public Object invokeConstructor(Object[] arguments) {
+        return invokeConstructor(theClass, arguments);
+    }
+
+    public int selectConstructorAndTransformArguments(int numberOfCosntructors, Object[] arguments) {
+        //TODO: that is just a quick prototype, not the real thing!
+        if (numberOfCosntructors != constructors.size()) {
+            throw new IncompatibleClassChangeError("the number of constructors during runtime and compile time for " +
+                    this.theClass.getName() + " do not match. Expected " + numberOfCosntructors + " but got " + constructors.size());
+        }
+
+        if (arguments == null) arguments = EMPTY_ARGUMENTS;
+        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+        unwrap(arguments);
+        CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses, false);
+        if (constructor == null) {
+            constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses, true);
+        }
+        if (constructor == null) {
+            throw new GroovyRuntimeException(
+                    "Could not find matching constructor for: "
+                            + theClass.getName()
+                            + "(" + InvokerHelper.toTypeString(arguments) + ")");
+        }
+        List l = new ArrayList(constructors.toList());
+        Comparator comp = new Comparator() {
+            public int compare(Object arg0, Object arg1) {
+                CachedConstructor c0 = (CachedConstructor) arg0;
+                CachedConstructor c1 = (CachedConstructor) arg1;
+                String descriptor0 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c0.getNativeParameterTypes());
+                String descriptor1 = BytecodeHelper.getMethodDescriptor(Void.TYPE, c1.getNativeParameterTypes());
+                return descriptor0.compareTo(descriptor1);
+            }
+        };
+        Collections.sort(l, comp);
+        int found = -1;
+        for (int i = 0; i < l.size(); i++) {
+            if (l.get(i) != constructor) continue;
+            found = i;
+            break;
+        }
+        // NOTE: must be changed to "1 |" if constructor was vargs
+        return 0 | (found << 8);
+    }
+
+    /**
+     * checks if the initialisation of the class id complete.
+     * This method should be called as a form of assert, it is no
+     * way to test if there is still initialisation work to be done.
+     * Such logic must be implemented in a different way.
+     *
+     * @throws IllegalStateException if the initialisation is incomplete yet
+     */
+    protected void checkInitalised() {
+        if (!isInitialized())
+            throw new IllegalStateException(
+                    "initialize must be called for meta " +
+                            "class of " + theClass +
+                            "(" + this.getClass() + ") " +
+                            "to complete initialisation process " +
+                            "before any invocation or field/property " +
+                            "access can be done");
+    }
+
+    private Object invokeConstructor(Class at, Object[] arguments) {
+        checkInitalised();
+        if (arguments == null) arguments = EMPTY_ARGUMENTS;
+        Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);
+        unwrap(arguments);
+        CachedConstructor constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses, false);
+        if (constructor != null) {
+            return doConstructorInvoke(at, constructor, arguments, true);
+        }
+        constructor = (CachedConstructor) chooseMethod("<init>", constructors, argClasses, true);
+        if (constructor != null) {
+            return doConstructorInvoke(at, constructor, arguments, true);
+        }
+
+        if (arguments.length == 1) {
+            Object firstArgument = arguments[0];
+            if (firstArgument instanceof Map) {
+                constructor = (CachedConstructor) chooseMethod("<init>", constructors, MetaClassHelper.EMPTY_TYPE_ARRAY, false);
+                if (constructor != null) {
+                    Object bean = doConstructorInvoke(at, constructor, MetaClassHelper.EMPTY_ARRAY, true);
+                    setProperties(bean, ((Map) firstArgument));
+                    return bean;
+                }
+            }
+        }
+        throw new GroovyRuntimeException(
+                "Could not find matching constructor for: "
+                        + theClass.getName()
+                        + "(" + InvokerHelper.toTypeString(arguments) + ")");
+    }
+
+    /**
+     * Sets a number of bean properties from the given Map where the keys are
+     * the String names of properties and the values are the values of the
+     * properties to set
+     */
+    public void setProperties(Object bean, Map map) {
+        checkInitalised();
+        for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            String key = entry.getKey().toString();
+
+            Object value = entry.getValue();
+            setProperty(bean, key, value);
+        }
+    }
+
+    /**
+     * @return the given property's value on the object
+     */
+    public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {
+
+        //----------------------------------------------------------------------
+        // handling of static
+        //----------------------------------------------------------------------
+        boolean isStatic = theClass != Class.class && object instanceof Class;
+        if (isStatic && object != theClass) {
+            MetaClass mc = registry.getMetaClass((Class) object);
+            return mc.getProperty(sender, object, name, useSuper, false);
+        }
+
+        checkInitalised();
+
+        //----------------------------------------------------------------------
+        // turn getProperty on a Map to get on the Map itself
+        //----------------------------------------------------------------------
+        if (!isStatic && this.isMap) {
+            return ((Map) object).get(name);
+        }
+
+        MetaMethod method = null;
+        Object[] arguments = EMPTY_ARGUMENTS;
+
+        //----------------------------------------------------------------------
+        // getter
+        //----------------------------------------------------------------------
+        MetaProperty mp = getMetaProperty(ReflectionCache.getCachedClass(sender), name, useSuper, isStatic);
+        if (mp != null) {
+            if (mp instanceof MetaBeanProperty) {
+                MetaBeanProperty mbp = (MetaBeanProperty) mp;
+                method = mbp.getGetter();
+                mp = mbp.getField();
+            }
+        }
+
+        // check for a category method named like a getter
+        if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
+            String getterName = "get" + MetaClassHelper.capitalize(name);
+            MetaMethod categoryMethod = getCategoryMethodGetter(sender, getterName, false);
+            if (categoryMethod != null) method = categoryMethod;
+        }
+
+        //----------------------------------------------------------------------
+        // field
+        //----------------------------------------------------------------------
+        if (method == null && mp != null) {
+            try {
+                return mp.getProperty(object);
+            } catch (IllegalArgumentException e) {
+                // can't access the field directly but there may be a getter
+                mp = null;
+            }
+        }
+
+        //----------------------------------------------------------------------
+        // generic get method
+        //----------------------------------------------------------------------
+        // check for a generic get method provided through a category
+        if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
+            method = getCategoryMethodGetter(sender, "get", true);
+            if (method != null) arguments = new Object[]{name};
+        }
+
+        // the generic method is valid, if available (!=null), if static or
+        // if it is not static and we do no static access
+        if (method == null && genericGetMethod != null && !(!genericGetMethod.isStatic() && isStatic)) {
+            arguments = new Object[]{name};
+            method = genericGetMethod;
+        }
+
+        //----------------------------------------------------------------------
+        // special cases
+        //----------------------------------------------------------------------
+        if (method == null) {
+            /** todo these special cases should be special MetaClasses maybe */
+            if (theClass != Class.class && object instanceof Class) {
+                MetaClass mc = registry.getMetaClass(Class.class);
+                return mc.getProperty(Class.class, object, name, useSuper, false);
+            } else if (object instanceof Collection) {
+                return DefaultGroovyMethods.getAt((Collection) object, name);
+            } else if (object instanceof Object[]) {
+                return DefaultGroovyMethods.getAt(Arrays.asList((Object[]) object), name);
+            } else {
+                MetaMethod addListenerMethod = (MetaMethod) listeners.get(name);
+                if (addListenerMethod != null) {
+                    //TODO: one day we could try return the previously registered Closure listener for easy removal
+                    return null;
+                }
+            }
+        } else {
+
+            //----------------------------------------------------------------------
+            // executing the getter method
+            //----------------------------------------------------------------------
+            return MetaClassHelper.doMethodInvoke(object, method, arguments);
+        }
+
+        //----------------------------------------------------------------------
+        // error due to missing method/field
+        //----------------------------------------------------------------------
+        if (isStatic || object instanceof Class)
+            return invokeStaticMissingProperty(object, name, null, true);
+        else
+            return invokeMissingProperty(object, name, null, true);
+    }
+
+
+    private MetaMethod getCategoryMethodGetter(Class sender, String name, boolean useLongVersion) {
+        List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name);
+        if (possibleGenericMethods != null) {
+            for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
+                MetaMethod mmethod = (MetaMethod) iter.next();
+                CachedClass[] paramTypes = mmethod.getParameterTypes();
+                if (useLongVersion) {
+                    if (paramTypes.length == 1 && paramTypes[0].getCachedClass() == String.class) {
+                        return mmethod;
+                    }
+                } else {
+                    if (paramTypes.length == 0) return mmethod;
+                }
+            }
+        }
+        return null;
+    }
+
+    private MetaMethod getCategoryMethodSetter(Class sender, String name, boolean useLongVersion) {
+        List possibleGenericMethods = GroovyCategorySupport.getCategoryMethods(sender, name);
+        if (possibleGenericMethods != null) {
+            for (Iterator iter = possibleGenericMethods.iterator(); iter.hasNext();) {
+                MetaMethod mmethod = (MetaMethod) iter.next();
+                CachedClass[] paramTypes = mmethod.getParameterTypes();
+                if (useLongVersion) {
+                    if (paramTypes.length == 2 && paramTypes[0].getCachedClass() == String.class) {
+                        return mmethod;
+                    }
+                } else {
+                    if (paramTypes.length == 1) return mmethod;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Get all the properties defined for this type
+     *
+     * @return a list of MetaProperty objects
+     */
+    public List getProperties() {
+        checkInitalised();
+        SingleKeyHashMap propertyMap = classPropertyIndex.getNullable(theCachedClass);
+        // simply return the values of the metaproperty map as a List
+        List ret = new ArrayList(propertyMap.size());
+        for (ComplexKeyHashMap.EntryIterator iter = propertyMap.getEntrySetIterator(); iter.hasNext();) {
+            MetaProperty element = (MetaProperty) ((SingleKeyHashMap.Entry) iter.next()).value;
+            if (element instanceof MetaFieldProperty) continue;
+            // filter out DGM beans
+            if (element instanceof MetaBeanProperty) {
+                MetaBeanProperty mp = (MetaBeanProperty) element;
+                boolean setter = true;
+                boolean getter = true;
+                if (mp.getGetter() == null || mp.getGetter() instanceof NewInstanceMetaMethod) {
+                    getter = false;
+                }
+                if (mp.getSetter() == null || mp.getSetter() instanceof NewInstanceMetaMethod) {
+                    setter = false;
+                }
+                if (!setter && !getter) continue;
+                if (!setter && mp.getSetter() != null) {
+                    element = new MetaBeanProperty(mp.getName(), mp.getType(), mp.getGetter(), null);
+                }
+                if (!getter && mp.getGetter() != null) {
+                    element = new MetaBeanProperty(mp.getName(), mp.getType(), null, mp.getSetter());
+                }
+            }
+            ret.add(element);
+        }
+        return ret;
+    }
+
+    private MetaMethod findPropertyMethod(Object methodOrList, boolean isGetter) {
+        if (methodOrList == null)
+          return null;
+
+        Object ret = null;
+        if (methodOrList instanceof MetaMethod) {
+            MetaMethod element = (MetaMethod)methodOrList;
+            if (!isGetter &&
+                    //(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
+                    element.getParameterTypes().length == 1) {
+                ret = addElementToList(ret, element);
+            }
+            if (isGetter &&
+                    !(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
+                    element.getParameterTypes().length == 0) {
+                ret = addElementToList(ret, element);
+            }
+
+        }
+        else {
+            FastArray methods = (FastArray) methodOrList;
+            final int len = methods.size();
+            final Object[] data = methods.getArray();
+            for (int i = 0; i != len; ++i) {
+                MetaMethod element = (MetaMethod) data[i];
+                if (!isGetter &&
+                        //(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
+                        element.getParameterTypes().length == 1) {
+                    ret = addElementToList(ret, element);
+                }
+                if (isGetter &&
+                        !(element.getReturnType() == Void.class || element.getReturnType() == Void.TYPE) &&
+                        element.getParameterTypes().length == 0) {
+                    ret = addElementToList(ret, element);
+                }
+            }
+        }
+
+        if (ret == null) return null;
+        if (ret instanceof MetaMethod) return (MetaMethod) ret;
+
+        // we found multiple matching methods
+        // this is a problem, because we can use only one
+        // if it is a getter, then use the most general return
+        // type to decide which method to use. If it is a setter
+        // we use the type of the first parameter
+        MetaMethod method = null;
+        int distance = -1;
+        for (Iterator iter = ((List) ret).iterator(); iter.hasNext();) {
+            MetaMethod element = (MetaMethod) iter.next();
+            Class c;
+            if (isGetter) {
+                c = element.getReturnType();
+            } else {
+                c = element.getParameterTypes()[0].getCachedClass();
+            }
+            int localDistance = distanceToObject(c);
+            //TODO: maybe implement the case localDistance==distance
+            if (distance == -1 || distance > localDistance) {
+                distance = localDistance;
+                method = element;
+            }
+        }
+        return method;
+    }
+
+    private Object addElementToList(Object ret, MetaMethod element) {
+        if (ret == null)
+            ret = element;
+        else if (ret instanceof List)
+            ((List) ret).add(element);
+        else {
+            List list = new LinkedList();
+            list.add(ret);
+            list.add(element);
+            ret = list;
+        }
+        return ret;
+    }
+
+    private static int distanceToObject(Class c) {
+        int count;
+        for (count = 0; c != null; count++) {
+            c = c.getSuperclass();
+        }
+        return count;
+    }
+
+
+    /**
+     * This will build up the property map (Map of MetaProperty objects, keyed on
+     * property name).
+     */
+    private void setupProperties(PropertyDescriptor[] propertyDescriptors) {
+        if (theCachedClass.isInterface) {
+            LinkedList superClasses = new LinkedList();
+            superClasses.add(ReflectionCache.OBJECT_CLASS);
+            Set interfaces = theCachedClass.getInterfaces();
+
+            classPropertyIndexForSuper = classPropertyIndex;
+            final SingleKeyHashMap cPI = classPropertyIndex.getNotNull(theCachedClass);
+            for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
+                CachedClass iclass = (CachedClass) interfaceIter.next();
+                SingleKeyHashMap iPropertyIndex = cPI;
+                addFields(iclass, iPropertyIndex);
+                classPropertyIndex.put(iclass, iPropertyIndex);
+            }
+            classPropertyIndex.put(ReflectionCache.OBJECT_CLASS, cPI);
+
+            applyPropertyDescriptors(propertyDescriptors);
+            applyStrayPropertyMethods(superClasses, classPropertyIndex, true);
+
+            makeStaticPropertyIndex();
+        } else {
+            LinkedList superClasses = getSuperClasses();
+            Set interfaces = theCachedClass.getInterfaces();
+
+            // if this an Array, then add the special read-only "length" property
+            if (theCachedClass.isArray) {
+                SingleKeyHashMap map = new SingleKeyHashMap();
+                map.put("length", arrayLengthProperty);
+                classPropertyIndex.put(theCachedClass, map);
+            }
+
+            inheritStaticInterfaceFields(superClasses, interfaces);
+            inheritFields(superClasses);
+
+            applyPropertyDescriptors(propertyDescriptors);
+
+            applyStrayPropertyMethods(superClasses, classPropertyIndex, true);
+            applyStrayPropertyMethods(superClasses, classPropertyIndexForSuper, false);
+
+            copyClassPropertyIndexForSuper(classPropertyIndexForSuper);
+            makeStaticPropertyIndex();
+        }
+    }
+
+    private void makeStaticPropertyIndex() {
+        SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
+        for (ComplexKeyHashMap.EntryIterator iter = propertyMap.getEntrySetIterator(); iter.hasNext();) {
+            SingleKeyHashMap.Entry entry = ((SingleKeyHashMap.Entry) iter.next());
+
+            MetaProperty mp = (MetaProperty) entry.getValue();
+            if (mp instanceof MetaFieldProperty) {
+                MetaFieldProperty mfp = (MetaFieldProperty) mp;
+                if (!mfp.isStatic()) continue;
+            } else if (mp instanceof MetaBeanProperty) {
+                MetaProperty result = establishStaticMetaProperty(mp);
+                if (result == null) continue;
+                else {
+                    mp = result;
+                }
+            } else {
+                continue; // ignore all other types
+            }
+            staticPropertyIndex.put(entry.getKey(), mp);
+        }
+
+    }
+
+    private MetaProperty establishStaticMetaProperty(MetaProperty mp) {
+        MetaBeanProperty mbp = (MetaBeanProperty) mp;
+        MetaProperty result = null;
+        final MetaMethod getterMethod = mbp.getGetter();
+        final MetaMethod setterMethod = mbp.getSetter();
+        final MetaFieldProperty metaField = mbp.getField();
+
+        boolean getter = getterMethod == null || getterMethod.isStatic();
+        boolean setter = setterMethod == null || setterMethod.isStatic();
+        boolean field = metaField == null || metaField.isStatic();
+
+        if (!getter && !setter && !field) {
+            return result;
+        } else {
+            final String propertyName = mbp.getName();
+            final Class propertyType = mbp.getType();
+
+            if (setter && getter) {
+                if (field) {
+                    result = mbp; // nothing to do
+                } else {
+                    result = new MetaBeanProperty(propertyName, propertyType, getterMethod, setterMethod);
+                }
+            } else if (getter && !setter) {
+                if (getterMethod == null) {
+                    result = metaField;
+                } else {
+                    MetaBeanProperty newmp = new MetaBeanProperty(propertyName, propertyType, getterMethod, null);
+                    if (field) newmp.setField(metaField);
+                    result = newmp;
+                }
+            } else if (setter && !getter) {
+                if (setterMethod == null) {
+                    result = metaField;
+                } else {
+                    MetaBeanProperty newmp = new MetaBeanProperty(propertyName, propertyType, null, setterMethod);
+                    if (field) newmp.setField(metaField);
+                    result = newmp;
+                }
+            } else
+                result = metaField;
+        }
+        return result;
+    }
+
+    private void copyClassPropertyIndexForSuper(Index dest) {
+        for (ComplexKeyHashMap.EntryIterator iter = classPropertyIndex.getEntrySetIterator(); iter.hasNext();) {
+            SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) iter.next();
+            SingleKeyHashMap newVal = new SingleKeyHashMap();
+            dest.put((CachedClass) entry.getKey(), newVal);
+        }
+    }
+
+    private void inheritStaticInterfaceFields(LinkedList superClasses, Set interfaces) {
+        for (Iterator interfaceIter = interfaces.iterator(); interfaceIter.hasNext();) {
+            CachedClass iclass = (CachedClass) interfaceIter.next();
+            SingleKeyHashMap iPropertyIndex = classPropertyIndex.getNotNull(iclass);
+            addFields(iclass, iPropertyIndex);
+            for (Iterator classIter = superClasses.iterator(); classIter.hasNext();) {
+                CachedClass sclass = (CachedClass) classIter.next();
+                if (!iclass.getCachedClass().isAssignableFrom(sclass.getCachedClass())) continue;
+                SingleKeyHashMap sPropertyIndex = classPropertyIndex.getNotNull(sclass);
+                copyNonPrivateFields(iPropertyIndex, sPropertyIndex);
+            }
+        }
+    }
+
+    private void inheritFields(LinkedList superClasses) {
+        SingleKeyHashMap last = null;
+        for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
+            CachedClass klass = (CachedClass) iter.next();
+            SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass);
+            if (last != null) {
+                copyNonPrivateFields(last, propertyIndex);
+            }
+            last = propertyIndex;
+            addFields(klass, propertyIndex);
+        }
+    }
+
+    private void addFields(final CachedClass klass, SingleKeyHashMap propertyIndex) {
+        CachedField[] fields = klass.getFields();
+        for (int i = 0; i < fields.length; i++) {
+            MetaFieldProperty mfp = MetaFieldProperty.create(fields[i]);
+            propertyIndex.put(fields[i].getName(), mfp);
+        }
+    }
+
+    private void copyNonPrivateFields(SingleKeyHashMap from, SingleKeyHashMap to) {
+        for (ComplexKeyHashMap.EntryIterator iter = from.getEntrySetIterator(); iter.hasNext();) {
+            SingleKeyHashMap.Entry entry = (SingleKeyHashMap.Entry) iter.next();
+            MetaFieldProperty mfp = (MetaFieldProperty) entry.getValue();
+            if (!Modifier.isPublic(mfp.getModifiers()) && !Modifier.isProtected(mfp.getModifiers())) continue;
+            to.put(entry.getKey(), mfp);
+        }
+    }
+
+    private void applyStrayPropertyMethods(LinkedList superClasses, Index classPropertyIndex, boolean isThis) {
+        // now look for any stray getters that may be used to define a property
+        for (Iterator iter = superClasses.iterator(); iter.hasNext();) {
+            CachedClass klass = (CachedClass) iter.next();
+            MetaMethodIndex.Header header = metaMethodIndex.getHeader(klass.getCachedClass());
+            SingleKeyHashMap propertyIndex = classPropertyIndex.getNotNull(klass);
+            for (MetaMethodIndex.Entry e = header.head; e != null; e = e.nextClassEntry)
+            {
+                String methodName = e.name;
+                // name too short?
+                if (methodName.length() < 4) continue;
+                // possible getter/setter?
+                boolean isGetter = methodName.startsWith("get");
+                boolean isSetter = methodName.startsWith("set");
+                if (!isGetter && !isSetter) continue;
+
+                MetaMethod propertyMethod = findPropertyMethod(isThis ? e.methods : e.methodsForSuper, isGetter);
+                if (propertyMethod == null) continue;
+
+                String propName = getPropName(methodName);
+                createMetaBeanProperty(propertyIndex, propName, isGetter, propertyMethod);
+            }
+        }
+    }
+
+    private static final HashMap propNames = new HashMap(1024);
+
+    private String getPropName(String methodName) {
+        String name = (String) propNames.get(methodName);
+        if (name != null)
+            return name;
+
+        synchronized (propNames) {
+            // get the name of the property
+            final int len = methodName.length() - 3;
+            char[] pn = new char[len];
+            methodName.getChars(3, 3 + len, pn, 0);
+            pn[0] = Character.toLowerCase(pn[0]);
+            String propName = new String(pn);
+            propNames.put(methodName, propName);
+            return propName;
+        }
+    }
+
+    private void createMetaBeanProperty(SingleKeyHashMap propertyIndex, String propName, boolean isGetter, MetaMethod propertyMethod) {
+        // is this property already accounted for?
+        MetaProperty mp = (MetaProperty) propertyIndex.get(propName);
+        if (mp == null) {
+            if (isGetter) {
+                mp = new MetaBeanProperty(propName,
+                        propertyMethod.getReturnType(),
+                        propertyMethod, null);
+            } else {
+                //isSetter
+                mp = new MetaBeanProperty(propName,
+                        propertyMethod.getParameterTypes()[0].getCachedClass(),
+                        null, propertyMethod);
+            }
+        } else {
+            MetaBeanProperty mbp;
+            MetaFieldProperty mfp;
+            if (mp instanceof MetaBeanProperty) {
+                mbp = (MetaBeanProperty) mp;
+                mfp = mbp.getField();
+            } else if (mp instanceof MetaFieldProperty) {
+                mfp = (MetaFieldProperty) mp;
+                mbp = new MetaBeanProperty(propName,
+                        mfp.getType(),
+                        null, null);
+            } else {
+                throw new GroovyBugError("unknown MetaProperty class used. Class is " + mp.getClass());
+            }
+            // we may have already found one for this name
+            if (isGetter && mbp.getGetter() == null) {
+                mbp.setGetter(propertyMethod);
+            } else if (!isGetter && mbp.getSetter() == null) {
+                mbp.setSetter(propertyMethod);
+            }
+            mbp.setField(mfp);
+            mp = mbp;
+        }
+        propertyIndex.put(propName, mp);
+    }
+
+    private void applyPropertyDescriptors(PropertyDescriptor[] propertyDescriptors) {
+        // now iterate over the map of property descriptors and generate
+        // MetaBeanProperty objects
+        for (int i = 0; i < propertyDescriptors.length; i++) {
+            PropertyDescriptor pd = propertyDescriptors[i];
+
+            // skip if the property type is unknown (this seems to be the case if the
+            // property descriptor is based on a setX() method that has two parameters,
+            // which is not a valid property)
+            if (pd.getPropertyType() == null)
+                continue;
+
+            // get the getter method
+            Method method = pd.getReadMethod();
+            MetaMethod getter;
+            if (method != null)
+                getter = findMethod(CachedMethod.find(method));
+            else
+                getter = null;
+
+            // get the setter method
+            MetaMethod setter;
+            method = pd.getWriteMethod();
+            if (method != null)
+                setter = findMethod(CachedMethod.find(method));
+            else
+                setter = null;
+
+            // now create the MetaProperty object
+            MetaBeanProperty mp = new MetaBeanProperty(pd.getName(), pd.getPropertyType(), getter, setter);
+            addMetaBeanProperty(mp);
+        }
+    }
+
+    /**
+     * Adds a new MetaBeanProperty to this MetaClass
+     *
+     * @param mp The MetaBeanProperty
+     */
+    public void addMetaBeanProperty(MetaBeanProperty mp) {
+
+        MetaProperty staticProperty = establishStaticMetaProperty(mp);
+        if (staticProperty != null) {
+            staticPropertyIndex.put(mp.getName(), mp);
+        } else {
+
+            SingleKeyHashMap propertyMap = classPropertyIndex.getNotNull(theCachedClass);
+            //keep field
+            MetaFieldProperty field;
+            MetaProperty old = (MetaProperty) propertyMap.get(mp.getName());
+            if (old != null) {
+                if (old instanceof MetaBeanProperty) {
+                    field = ((MetaBeanProperty) old).getField();
+                } else {
+                    field = (MetaFieldProperty) old;
+                }
+                mp.setField(field);
+            }
+
+            // put it in the list
+            // this will overwrite a possible field property
+            propertyMap.put(mp.getName(), mp);
+        }
+
+    }
+
+    /**
+     * Sets the property value on an object
+     */
+    public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {
+        checkInitalised();
+
+        //----------------------------------------------------------------------
+        // handling of static
+        //----------------------------------------------------------------------
+        boolean isStatic = theClass != Class.class && object instanceof Class;
+        if (isStatic && object != theClass) {
+            MetaClass mc = registry.getMetaClass((Class) object);
+            mc.getProperty(sender, object, name, useSuper, fromInsideClass);
+            return;
+        }
+
+        //----------------------------------------------------------------------
+        // Unwrap wrapped values fo now - the new MOP will handle them properly
+        //----------------------------------------------------------------------
+        if (newValue instanceof Wrapper) newValue = ((Wrapper) newValue).unwrap();
+
+        //----------------------------------------------------------------------
+        // turn setProperty on a Map to put on the Map itself
+        //----------------------------------------------------------------------
+        if (!isStatic && this.isMap) {
+            ((Map) object).put(name, newValue);
+            return;
+        }
+
+
+        MetaMethod method = null;
+        Object[] arguments = null;
+
+        //----------------------------------------------------------------------
+        // setter
+        //----------------------------------------------------------------------
+        MetaProperty mp = getMetaProperty(ReflectionCache.getCachedClass(sender), name, useSuper, isStatic);
+        MetaProperty field = null;
+        if (mp != null) {
+            if (mp instanceof MetaBeanProperty) {
+                MetaBeanProperty mbp = (MetaBeanProperty) mp;
+                method = mbp.getSetter();
+                MetaProperty f = mbp.getField();
+                if (method != null || (f != null && !Modifier.isFinal(f.getModifiers()))) {
+                    arguments = new Object[]{newValue};
+                    field = f;
+                }
+            } else {
+                field = mp;
+            }
+        }
+
+        // check for a category method named like a setter
+        if (!useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
+            String getterName = "set" + MetaClassHelper.capitalize(name);
+            MetaMethod categoryMethod = getCategoryMethodSetter(sender, getterName, false);
+            if (categoryMethod != null) {
+                method = categoryMethod;
+                arguments = new Object[]{newValue};
+            }
+        }
+
+        //----------------------------------------------------------------------
+        // listener method
+        //----------------------------------------------------------------------
+        boolean ambigousListener = false;
+        if (method == null) {
+            method = (MetaMethod) listeners.get(name);
+            ambigousListener = method == AMBIGOUS_LISTENER_METHOD;
+            if (method != null &&
+                    !ambigousListener &&
+                    newValue instanceof Closure) {
+                // lets create a dynamic proxy
+                Object proxy = Proxy.newProxyInstance(
+                        theClass.getClassLoader(),
+                        new Class[]{method.getParameterTypes()[0].getCachedClass()},
+                        new ConvertedClosure((Closure) newValue, name));
+                arguments = new Object[]{proxy};
+                newValue = proxy;
+            } else {
+                method = null;
+            }
+        }
+
+        //----------------------------------------------------------------------
+        // field
+        //----------------------------------------------------------------------
+        if (method == null && field != null) {
+            field.setProperty(object, newValue);
+            return;
+        }
+
+        //----------------------------------------------------------------------
+        // generic set method
+        //----------------------------------------------------------------------
+        // check for a generic get method provided through a category
+        if (method == null && !useSuper && !isStatic && GroovyCategorySupport.hasCategoryInAnyThread()) {
+            method = getCategoryMethodSetter(sender, "set", true);
+            if (method != null) arguments = new Object[]{name, newValue};
+        }
+
+        // the generic method is valid, if available (!=null), if static or
+        // if it is not static and we do no static access
+        if (method == null && genericSetMethod != null && !(!genericSetMethod.isStatic() && isStatic)) {
+            arguments = new Object[]{name, newValue};
+            method = genericSetMethod;
+        }
+
+        //----------------------------------------------------------------------
+        // executing the getter method
+        //----------------------------------------------------------------------
+        if (method != null) {
+            if (arguments.length == 1) {
+                newValue = DefaultTypeTransformation.castToType(
+                        newValue,
+                        method.getParameterTypes()[0].getCachedClass());
+                arguments[0] = newValue;
+            } else {
+                newValue = DefaultTypeTransformation.castToType(
+                        newValue,
+                        method.getParameterTypes()[1].getCachedClass());
+                arguments[1] = newValue;
+            }
+            MetaClassHelper.doMethodInvoke(object, method, arguments);
+            return;
+        }
+
+        //----------------------------------------------------------------------
+        // error due to missing method/field
+        //----------------------------------------------------------------------
+        if (ambigousListener) {
+            throw new GroovyRuntimeException("There are multiple listeners for the property " + name + ". Please do not use the bean short form to access this listener.");
+        }
+        if (mp != null) {
+            throw new ReadOnlyPropertyException(name, theClass);
+        }
+
+        invokeMissingProperty(object, name, newValue, false);
+    }
+
+    private MetaProperty getMetaProperty(CachedClass clazz, String name, boolean useSuper, boolean useStatic) {
+        while (true) {
+            SingleKeyHashMap propertyMap;
+            if (useStatic) {
+                propertyMap = staticPropertyIndex;
+            } else if (useSuper) {
+                propertyMap = classPropertyIndexForSuper.getNullable(clazz);
+            } else {
+                propertyMap = classPropertyIndex.getNullable(clazz);
+            }
+            if (propertyMap == null) {
+                if (clazz != theCachedClass) {
+                    clazz = theCachedClass;
+                    continue;
+                } else {
+                    return null;
+                }
+            }
+            return (MetaProperty) propertyMap.get(name);
+        }
+    }
+
+
+    public Object getAttribute(Class sender, Object receiver, String messageName, boolean useSuper) {
+        return getAttribute(receiver, messageName);
+    }
+
+    /**
+     * Looks up the given attribute (field) on the given object
+     */
+    public Object getAttribute(Class sender, Object object, String attribute, boolean useSuper, boolean fromInsideClass) {
+        checkInitalised();
+
+        boolean isStatic = theClass != Class.class && object instanceof Class;
+        if (isStatic && object != theClass) {
+            MetaClass mc = registry.getMetaClass((Class) object);
+            return mc.getAttribute(sender, object, attribute, useSuper);
+        }
+
+        MetaProperty mp = getMetaProperty(ReflectionCache.getCachedClass(sender), attribute, useSuper, isStatic);
+
+        if (mp != null) {
+            if (mp instanceof MetaBeanProperty) {
+                MetaBeanProperty mbp = (MetaBeanProperty) mp;
+                mp = mbp.getField();
+            }
+            try {
+                // delegate the get operation to the metaproperty
+                if (mp != null) return mp.getProperty(object);
+            } catch (Exception e) {
+                throw new GroovyRuntimeException("Cannot read field: " + attribute, e);
+            }
+        }
+
+        throw new MissingFieldException(attribute, theClass);
+    }
+
+    /**
+     * Sets the given attribute (field) on the given object
+     */
+    public void setAttribute(Class sender, Object object, String attribute, Object newValue, boolean useSuper, boolean fromInsideClass) {
+        checkInitalised();
+
+        boolean isStatic = theClass != Class.class && object instanceof Class;
+        if (isStatic && object != theClass) {
+            MetaClass mc = registry.getMetaClass((Class) object);
+            mc.setAttribute(sender, object, attribute, newValue, useSuper, fromInsideClass);
+            return;
+        }
+
+        MetaProperty mp = getMetaProperty(ReflectionCache.getCachedClass(sender), attribute, useSuper, isStatic);
+
+        if (mp != null) {
+            if (mp instanceof MetaBeanProperty) {
+                MetaBeanProperty mbp = (MetaBeanProperty) mp;
+                mp = mbp.getField();
+            }
+            if (mp != null) {
+                mp.setProperty(object, newValue);
+                return;
+            }
+        }
+
+        throw new MissingFieldException(attribute, theClass);
+    }
+
+    public ClassNode getClassNode() {
+        if (classNode == null && GroovyObject.class.isAssignableFrom(theClass)) {
+            // lets try load it from the classpath
+            String groovyFile = theClass.getName();
+            int idx = groovyFile.indexOf('$');
+            if (idx > 0) {
+                groovyFile = groovyFile.substring(0, idx);
+            }
+            groovyFile = groovyFile.replace('.', '/') + ".groovy";
+
+            //System.out.println("Attempting to load: " + groovyFile);
+            URL url = theClass.getClassLoader().getResource(groovyFile);
+            if (url == null) {
+                url = Thread.currentThread().getContextClassLoader().getResource(groovyFile);
+            }
+            if (url != null) {
+                try {
+
+                    /**
+                     * todo there is no CompileUnit in scope so class name
+                     * checking won't work but that mostly affects the bytecode
+                     * generation rather than viewing the AST
+                     */
+                    CompilationUnit.ClassgenCallback search = new CompilationUnit.ClassgenCallback() {
+                        public void call(ClassVisitor writer, ClassNode node) {
+                            if (node.getName().equals(theClass.getName())) {
+                                MetaClassImpl.this.classNode = node;
+                            }
+                        }
+                    };
+
+                    final ClassLoader parent = theClass.getClassLoader();
+                    GroovyClassLoader gcl = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+                        public Object run() {
+                            return new GroovyClassLoader(parent);
+                        }
+                    });
+                    CompilationUnit unit = new CompilationUnit();
+                    unit.setClassgenCallback(search);
+                    unit.addSource(url);
+                    unit.compile(Phases.CLASS_GENERATION);
+                }
+                catch (Exception e) {
+                    throw new GroovyRuntimeException("Exception thrown parsing: " + groovyFile + ". Reason: " + e, e);
+                }
+            }
+
+        }
+        return classNode;
+    }
+
+    public String toString() {
+        return super.toString() + "[" + theClass + "]";
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+
+
+    /**
+     * adds a MetaMethod to this class. WARNING: this method will not
+     * do the neccessary steps for multimethod logic and using this
+     * method doesn't mean, that a method added here is replacing another
+     * method from a parent class completely. These steps are usually done
+     * by initalize, which means if you need these steps, you have to add
+     * the method before running initialize the first time.
+     *
+     * @param method the MetaMethod
+     * @see #initialize()
+     */
+    public void addMetaMethod(MetaMethod method) {
+        if (isInitialized()) {
+            throw new RuntimeException("Already initialized, cannot add new method: " + method);
+        }
+
+        final CachedClass declaringClass = method.getDeclaringClass();
+        addMetaMethodToIndex(method, metaMethodIndex.getHeader(declaringClass.getCachedClass()));
+    }
+
+    private void addMetaMethodToIndex(MetaMethod method, MetaMethodIndex.Header header) {
+        checkIfStdMethod(method);
+
+        String name = method.getName();
+        MetaMethodIndex.Entry e = metaMethodIndex.getOrPutMethods(name, header);
+        if (method.isStatic()) {
+            e.staticMethods = metaMethodIndex.addMethodToList(e.staticMethods, method);
+        }
+        e.methods = metaMethodIndex.addMethodToList(e.methods, method);
+    }
+
+    private void addMetaMethodToSuperIndex(MetaMethod method, MetaMethodIndex.Header header) {
+        checkIfStdMethod(method);
+
+        String name = method.getName();
+        MetaMethodIndex.Entry e = metaMethodIndex.getOrPutMethods(name, header);
+        if (method.isStatic()) {
+            e.staticMethods = metaMethodIndex.addMethodToList(e.staticMethods, method);
+        }
+        e.methodsForSuper = metaMethodIndex.addMethodToList(e.methodsForSuper, method);
+    }
+
+    private void checkIfStdMethod(MetaMethod method) {
+        if (isGenericGetMethod(method) && genericGetMethod == null) {
+            genericGetMethod = method;
+        } else if (MetaClassHelper.isGenericSetMethod(method) && genericSetMethod == null) {
+            genericSetMethod = method;
+        }
+        if (propertyMissingGet == null && method.getName().equals(PROPERTY_MISSING)) {
+            CachedClass[] parameterTypes = method.getParameterTypes();
+            if (parameterTypes.length == 1) {
+                propertyMissingGet = method;
+            }
+        }
+        if (propertyMissingSet == null && method.getName().equals(PROPERTY_MISSING)) {
+            CachedClass[] parameterTypes = method.getParameterTypes();
+            if (parameterTypes.length == 2) {
+                propertyMissingSet = method;
+            }
+        }
+        if (method.getName().equals(METHOD_MISSING)) {
+            CachedClass[] parameterTypes = method.getParameterTypes();
+            if (parameterTypes.length == 2
+                    && parameterTypes[0].getCachedClass() == String.class
+                    && parameterTypes[1].getCachedClass() == Object.class) {
+                methodMissing = method;
+            }
+        }
+    }
+
+    protected boolean isInitialized() {
+        return initialized;
+    }
+    
+    /**
+     * return false: add method
+     *        null:  ignore method
+     *        true:  replace
+     */
+    private Boolean getMatchKindForCategory(MetaMethod aMethod, MetaMethod categoryMethod) {
+        CachedClass[] params1 = aMethod.getParameterTypes();
+        CachedClass[] params2 = categoryMethod.getParameterTypes();
+        if (params1.length != params2.length) return Boolean.FALSE;
+
+        for (int i = 0; i < params1.length; i++) {
+            if (params1[i] != params2[i]) return Boolean.FALSE;
+        }
+        
+        Class aMethodClass = aMethod.getDeclaringClass().getCachedClass();
+        Class categoryMethodClass = categoryMethod.getDeclaringClass().getCachedClass();
+        
+        if (aMethodClass==categoryMethodClass) return Boolean.TRUE;
+        boolean match = aMethodClass.isAssignableFrom(categoryMethodClass);
+        if (match) return Boolean.TRUE;
+        return null;
+    }
+
+    private void filterMatchingMethodForCategory(FastArray list, MetaMethod method) {
+        int len = list.size();
+        if (len==0) {
+            list.add(method);
+            return;
+        }
+        
+        Object data[] = list.getArray();
+        for (int j = 0; j != len; ++j) {
+            MetaMethod aMethod = (MetaMethod) data[j];
+            Boolean match = getMatchKindForCategory(aMethod, method);
+            // true == replace
+            if (match==Boolean.TRUE) {
+                list.set(j, method);
+                return;
+            // null == ignore (we have a better method already)    
+            } else if (match==null) {
+                return;
+            }
+        }
+        // the casese true and null for a match are through, the 
+        // remaining case is false and that means adding the method
+        // to our list
+        list.add(method);
+    }
+
+    private int findMatchingMethod(CachedMethod[] data, int from, int to, MetaMethod method) {
+        for (int j = from; j <= to; ++j) {
+            CachedMethod aMethod = data[j];
+            CachedClass[] params1 = aMethod.getParameterTypes();
+            CachedClass[] params2 = method.getParameterTypes();
+            if (params1.length == params2.length) {
+                boolean matches = true;
+                for (int i = 0; i < params1.length; i++) {
+                    if (params1[i] != params2[i]) {
+                        matches = false;
+                        break;
+                    }
+                }
+                if (matches) {
+                    return j;
+                }
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * @return the matching method which should be found
+     */
+    private MetaMethod findMethod(CachedMethod aMethod) {
+        Object methods = getMethods(theClass, aMethod.getName(), false);
+        if (methods instanceof FastArray) {
+            FastArray m  = (FastArray) methods;
+            final int len = m.size;
+            final Object data[] = m.getArray();
+            for (int i = 0; i != len; ++i) {
+                MetaMethod method = (MetaMethod) data[i];
+                if (method.isMethod(aMethod.cachedMethod)) {
+                    return method;
+                }
+            }
+        }
+        else {
+            MetaMethod method = (MetaMethod) methods;
+            if (method.isMethod(aMethod.cachedMethod)) {
+                return method;
+            }
+        }
+        //log.warning("Creating reflection based dispatcher for: " + aMethod);
+        synchronized (aMethod.cachedClass) {
+          return aMethod.getReflectionMetaMethod();
+        }
+    }
+
+
+    private static Object doConstructorInvoke(final Class at, CachedConstructor constructor, Object[] argumentArray, boolean setAccessible) {
+        if (LOG.isLoggable(Level.FINER)) {
+            MetaClassHelper.logMethodCall(constructor.cachedConstructor.getDeclaringClass(), constructor.cachedConstructor.getName(), argumentArray);
+        }
+
+//       if (setAccessible) {
+//           // To fix JIRA 435
+//           // Every constructor should be opened to the accessible classes.
+//           final boolean accessible = MetaClassHelper.accessibleToConstructor(at, constructor);
+//           final Constructor ctor = constructor;
+//           AccessController.doPrivileged(new PrivilegedAction() {
+//               public Object run() {
+//                   ctor.setAccessible(accessible);
+//                   return null;
+//               }
+//           });
+//       }
+        return MetaClassHelper.doConstructorInvoke(constructor, argumentArray);
+    }
+
+    /**
+     * Chooses the correct method to use from a list of methods which match by
+     * name.
+     *
+     * @param methodOrList   the possible methods to choose from
+     * @param arguments
+     */
+    private Object chooseMethod(String methodName, Object methodOrList, Class[] arguments, boolean coerce) {
+        if (methodOrList instanceof MetaMethod) {
+            if (MetaClassHelper.isValidMethod(methodOrList, arguments, coerce)) {
+                return methodOrList;
+            }
+            return null;
+        }
+
+        FastArray methods = (FastArray) methodOrList;
+        int methodCount = methods.size();
+        if (methodCount <= 0) {
+            return null;
+        } else if (methodCount == 1) {
+            Object method = methods.get(0);
+            if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
+                return method;
+            }
+            return null;
+        }
+        Object answer;
+        if (arguments == null || arguments.length == 0) {
+            answer = MetaClassHelper.chooseEmptyMethodParams(methods);
+        } else if (arguments.length == 1 && arguments[0] == null) {
+            answer = MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods);
+        } else {
+            List matchingMethods = new ArrayList(methods.size());
+
+            final int len = methods.size;
+            Object data[] = methods.getArray();
+            for (int i = 0; i != len; ++i) {
+                Object method = data[i];
+
+                // making this false helps find matches
+                if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {
+                    matchingMethods.add(method);
+                }
+            }
+            if (matchingMethods.isEmpty()) {
+                return null;
+            } else if (matchingMethods.size() == 1) {
+                return matchingMethods.get(0);
+            }
+            return chooseMostSpecificParams(methodName, matchingMethods, arguments);
+
+        }
+        if (answer != null) {
+            return answer;
+        }
+        throw new MethodSelectionException(methodName, methods, arguments);
+    }
+
+    private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {
+
+        long matchesDistance = -1;
+        LinkedList matches = new LinkedList();
+        for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {
+            Object method = iter.next();
+            Class[] paramTypes = MetaClassHelper.getParameterTypes(method).getNativeParameterTypes();
+            long dist = MetaClassHelper.calculateParameterDistance(arguments, paramTypes);
+            if (dist == 0) return method;
+            if (matches.size() == 0) {
+                matches.add(method);
+                matchesDistance = dist;
+            } else if (dist < matchesDistance) {
+                matchesDistance = dist;
+                matches.clear();
+                matches.add(method);
+            } else if (dist == matchesDistance) {
+                matches.add(method);
+            }
+
+        }
+        if (matches.size() == 1) {
+            return matches.getFirst();
+        }
+        if (matches.size() == 0) {
+            return null;
+        }
+
+        //more than one matching method found --> ambigous!
+        String msg = "Ambiguous method overloading for method ";
+        msg += theClass.getName() + "#" + name;
+        msg += ".\nCannot resolve which method to invoke for ";
+        msg += InvokerHelper.toString(arguments);
+        msg += " due to overlapping prototypes between:";
+        for (Iterator iter = matches.iterator(); iter.hasNext();) {
+            Class[] types = MetaClassHelper.getParameterTypes(iter.next()).getNativeParameterTypes();
+            msg += "\n\t" + InvokerHelper.toString(types);
+        }
+        throw new GroovyRuntimeException(msg);
+    }
+
+    private boolean isGenericGetMethod(MetaMethod method) {
+        if (method.getName().equals("get")) {
+            CachedClass[] parameterTypes = method.getParameterTypes();
+            return parameterTypes.length == 1 && parameterTypes[0].getCachedClass() == String.class;
+        }
+        return false;
+    }
+
+
+    public synchronized void initialize() {
+        if (!isInitialized()) {
+            fillMethodIndex();
+            addProperties();
+            initialized = true;
+        }
+    }
+
+    private void addProperties() {
+        BeanInfo info;
+        //     introspect
+        try {
+            info = (BeanInfo) AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                public Object run() throws IntrospectionException {
+                    return Introspector.getBeanInfo(theClass);
+                }
+            });
+        } catch (PrivilegedActionException pae) {
+            throw new GroovyRuntimeException("exception while bean introspection", pae.getException());
+        }
+        PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
+        // build up the metaproperties based on the public fields, property descriptors,
+        // and the getters and setters
+        setupProperties(descriptors);
+
+        EventSetDescriptor[] eventDescriptors = info.getEventSetDescriptors();
+        for (int i = 0; i < eventDescriptors.length; i++) {
+            EventSetDescriptor descriptor = eventDescriptors[i];
+            Method[] listenerMethods = descriptor.getListenerMethods();
+            for (int j = 0; j < listenerMethods.length; j++) {
+                Method listenerMethod = listenerMethods[j];
+                final ReflectionMetaMethod metaMethod = new ReflectionMetaMethod(CachedMethod.find(descriptor.getAddListenerMethod()));
+                addToAllMethodsIfPublic(metaMethod);
+                String name = listenerMethod.getName();
+                if (listeners.containsKey(name)) {
+                    listeners.put(name, AMBIGOUS_LISTENER_METHOD);
+                } else {
+                    listeners.put(name, metaMethod);
+                }
+            }
+        }
+    }
+
+    private void addToAllMethodsIfPublic(MetaMethod metaMethod) {
+        if (Modifier.isPublic(metaMethod.getModifiers()))
+            allMethods.add(metaMethod);
+    }
+
+    public List getMethods() {
+        return allMethods;
+    }
+
+    public List getMetaMethods() {
+        return new ArrayList(newGroovyMethodsSet);
+    }
+
+    protected void dropStaticMethodCache(String name) {
+        for (Iterator it = staticMethodCache.keySet().iterator(); it.hasNext();) {
+            MethodKey k = (MethodKey) it.next();
+            if (name.equals(k.getName()))
+                it.remove();
+        }
+    }
+
+    protected void dropMethodCache(String name) {
+        for (Iterator it = methodCache.keySet().iterator(); it.hasNext();) {
+            MethodKey k = (MethodKey) it.next();
+            if (name.equals(k.getName()))
+                it.remove();
+        }
+    }
+
+    private abstract class MethodIndexAction {
+        public void iterate() {
+            final ComplexKeyHashMap.Entry[] table = metaMethodIndex.methodHeaders.getTable();
+            int len = table.length;
+            for (int i = 0; i != len; ++i) {
+                for (SingleKeyHashMap.Entry classEntry = (SingleKeyHashMap.Entry) table[i];
+                     classEntry != null;
+                     classEntry = (SingleKeyHashMap.Entry) classEntry.next) {
+
+                    Class clazz = (Class) classEntry.getKey();
+
+                    if (skipClass(clazz)) continue;
+
+                    MetaMethodIndex.Header header = (MetaMethodIndex.Header) classEntry.getValue();
+                    for (MetaMethodIndex.Entry nameEntry = header.head; nameEntry != null; nameEntry = nameEntry.nextClassEntry) {
+                        methodNameAction(clazz, nameEntry);
+                    }
+                }
+            }
+        }
+
+        public abstract void methodNameAction(Class clazz, MetaMethodIndex.Entry methods);
+
+        public boolean skipClass(Class clazz) {
+            return false;
+        }
+    }
+
+    public Object getProperty(Object object, String property) {
+        return getProperty(theClass, object, property, false, false);
+    }
+
+    public void setProperty(Object object, String property, Object newValue) {
+        setProperty(theClass, object, property, newValue, false, false);
+    }
+
+    public Object getAttribute(Object object, String attribute) {
+        return getAttribute(theClass, object, attribute, false, false);
+    }
+
+    public void setAttribute(Object object, String attribute, Object newValue) {
+        setAttribute(theClass, object, attribute, newValue, false, false);
+    }
+
+    public MetaMethod pickMethod(String methodName, Class[] arguments) {
+        return getMethodWithoutCaching(theClass, methodName, arguments, false);
+    }
+
+    /**
+     * @deprecated use pickMethod instead
+     */
+    protected MetaMethod retrieveMethod(String methodName, Class[] arguments) {
+        return pickMethod(methodName, arguments);
+    }
+
+    /**
+     * remove all method call cache entries. This should be done if a
+     * method is added during runtime, but not by using a category.
+     */
+    protected void clearInvocationCaches() {
+        staticMethodCache.clear();
+        methodCache.clear();
+    }
+
+    private static final SingleKeyHashMap.Copier NAME_INDEX_COPIER = new SingleKeyHashMap.Copier() {
+        public Object copy(Object value) {
+            if (value instanceof FastArray)
+              return ((FastArray) value).copy();
+            else
+              return value;
+        }
+    };
+
+    private static final SingleKeyHashMap.Copier METHOD_INDEX_COPIER = new SingleKeyHashMap.Copier() {
+        public Object copy(Object value) {
+            return SingleKeyHashMap.copy(new SingleKeyHashMap(false), (SingleKeyHashMap) value, NAME_INDEX_COPIER);
+        }
+    };
+
+    class MethodIndex extends Index {
+        public MethodIndex(boolean b) {
+            super(false);
+        }
+
+        public MethodIndex(int size) {
+            super(size);
+        }
+
+        public MethodIndex() {
+            super();
+        }
+
+        MethodIndex copy() {
+            return (MethodIndex) SingleKeyHashMap.copy(new MethodIndex(false), this, METHOD_INDEX_COPIER);
+        }
+
+        protected Object clone() throws CloneNotSupportedException {
+            return super.clone();
+        }
+    }
+
+    public static class Index extends SingleKeyHashMap {
+
+        public Index(int size) {
+        }
+
+        public Index() {
+        }
+
+        public Index(boolean size) {
+            super(false);
+        }
+
+        public SingleKeyHashMap getNotNull(CachedClass key) {
+            Entry res = getOrPut(key);
+            if (res.value == null) {
+                res.value = new SingleKeyHashMap();
+            }
+            return (SingleKeyHashMap) res.value;
+        }
+
+        public void put(CachedClass key, SingleKeyHashMap value) {
+            ((Entry) getOrPut(key)).value = value;
+        }
+
+        public SingleKeyHashMap getNullable(CachedClass clazz) {
+            return (SingleKeyHashMap) get(clazz);
+        }
+
+        public boolean checkEquals(ComplexKeyHashMap.Entry e, Object key) {
+            return ((Entry) e).key.equals(key);
+        }
+    }
+
+    private static class DummyMetaMethod extends MetaMethod {
+
+        public int getModifiers() {
+            return 0;
+        }
+
+        public String getName() {
+            return null;
+        }
+
+        public Class getReturnType() {
+            return null;
+        }
+
+        public CachedClass getDeclaringClass() {
+            return null;
+        }
+
+        public ParameterTypes getParamTypes() {
+            return null;
+        }
+
+        public Object invoke(Object object, Object[] arguments) {
+            return null;
+        }
+    }
+
+}
diff --git a/groovy/src/main/groovy/lang/MetaClassRegistry.java b/groovy/src/main/groovy/lang/MetaClassRegistry.java
new file mode 100644
index 0000000..6ea284d
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MetaClassRegistry.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.runtime.GeneratedClosure;
+import org.codehaus.groovy.runtime.metaclass.ClosureMetaClass;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * A MetaClassRegistry is an object that is responsible for managing the a cache of MetaClass instances. Each
+ * java.lang.Class instance has an associated MetaClass and client code can query this interface for the MetaClass for
+ * a given associated java.lang.Class
+ *
+ * @see groovy.lang.MetaClass
+ *
+ * @author John Wilson
+ * @author Graeme Rocher
+ *
+ */
+public interface MetaClassRegistry {
+    /*
+     * The main function of the Registry
+     * If a Metaclass exists then return it
+     * otherwise create one, put it in the Registry and return it
+     */
+    MetaClass getMetaClass(Class theClass);
+    
+    /*
+     * Do we really want these two?
+     */
+    void setMetaClass(Class theClass, MetaClass theMetaClass);
+
+    /**
+     * Removes a cached MetaClass from the registry
+     * @param theClass The Java class of the MetaClass to remove
+     */
+    void removeMetaClass(Class theClass);
+
+    /**
+     * Retrieves the MetaClassCreationHandle that is responsible for constructing MetaClass instances
+     *
+     * @return The MetaClassCreationHandle instance
+     */
+    MetaClassCreationHandle getMetaClassCreationHandler();
+
+    /**
+     * Sets the MetaClassCreationHandle instance that is responsible for constructing instances
+     *
+     * @param handle The hande instance
+     */
+    void setMetaClassCreationHandle(MetaClassCreationHandle handle);
+
+    /**
+     * Class used as base for the creation of MetaClass implementations.
+     * The Class defaults to MetaClassImpl, if the class loading fails to
+     * find a special meta class. The name for such a meta class would be
+     * the class name it is created for with the prefix
+     * "groovy.runtime.metaclass." By replacing the handle in the registry
+     * you can have any control over the creation of what MetaClass is used
+     * for a class that you want to have. 
+     * WARNING: experimental code, likely to change soon
+     * @author Jochen Theodorou
+     */
+    class MetaClassCreationHandle {
+        public final MetaClass create(Class theClass, MetaClassRegistry registry) {
+	       try {
+	           final Class customMetaClass = Class.forName("groovy.runtime.metaclass." + theClass.getName() + "MetaClass");
+               if (DelegatingMetaClass.class.isAssignableFrom(customMetaClass)) {
+                   final Constructor customMetaClassConstructor = customMetaClass.getConstructor(new Class[]{MetaClass.class});
+                   MetaClass normalMetaClass = createNormalMetaClass(theClass, registry);
+                   return (MetaClass)customMetaClassConstructor.newInstance(new Object[]{normalMetaClass});
+               }
+               else {
+                   final Constructor customMetaClassConstructor = customMetaClass.getConstructor(new Class[]{MetaClassRegistry.class, Class.class});
+                   return (MetaClass)customMetaClassConstructor.newInstance(new Object[]{registry, theClass});
+               }
+           } catch (final ClassNotFoundException e) {
+               return createNormalMetaClass(theClass, registry);
+	       } catch (final Exception e) {
+	           throw new GroovyRuntimeException("Could not instantiate custom Metaclass for class: " + theClass.getName() + ". Reason: " + e, e);
+	       }
+        }
+
+        protected MetaClass createNormalMetaClass(Class theClass,MetaClassRegistry registry) {
+            if (GeneratedClosure.class.isAssignableFrom(theClass)) {
+                return new ClosureMetaClass(registry,theClass);
+            } else {
+                return new MetaClassImpl(registry, theClass);
+            }
+        }
+    }
+
+ }
diff --git a/groovy/src/main/groovy/lang/MetaExpandoProperty.java b/groovy/src/main/groovy/lang/MetaExpandoProperty.java
new file mode 100644
index 0000000..502cf9d
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MetaExpandoProperty.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import java.util.Map.Entry;
+
+/**
+ * Represents a property in an Expando object
+ * 
+ * @author John Stump
+ * @version $Revision$
+ */
+public class MetaExpandoProperty extends MetaProperty {
+
+	Object value = null;
+	
+    public MetaExpandoProperty(Entry entry) {
+		super((String) entry.getKey(), Object.class);
+		
+		value = entry.getValue();
+    }
+
+    /**
+     * @return the property of the given object
+     * @throws Exception if the property could not be evaluated
+     */
+    public Object getProperty(Object object) {
+		return value;
+	}
+
+    /**
+     * Sets the property on the given object to the new value
+     * 
+     * @param object on which to set the property
+     * @param newValue the new value of the property
+     */
+    public void setProperty(Object object, Object newValue) {
+		value = newValue;
+	}
+}
diff --git a/groovy/src/main/groovy/lang/MetaFieldProperty.java b/groovy/src/main/groovy/lang/MetaFieldProperty.java
new file mode 100644
index 0000000..3688584
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MetaFieldProperty.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.reflection.CachedField;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Represents a property on a bean which may have a getter and/or a setter
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MetaFieldProperty extends MetaProperty {
+
+    boolean alreadySetAccessible;
+    private Field field;
+
+    public static MetaFieldProperty create(CachedField field) {
+        return new MetaFieldProperty(field.field);
+    }
+
+    private MetaFieldProperty(Field field) {
+        super(field.getName(), field.getType());
+        this.field = field;
+    }
+
+    /**
+     * @return the property of the given object
+     * @throws Exception if the property could not be evaluated
+     */
+    public Object getProperty(final Object object) {
+        if ( !alreadySetAccessible ) {
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    field.setAccessible(true);
+                    return null;
+                }
+            });
+            alreadySetAccessible = true;
+        }
+
+        try {
+            return field.get(object);
+        } catch (IllegalAccessException e) {
+            throw new GroovyRuntimeException("Cannot get the property '" + name + "'.", e);
+        }
+    }
+
+    /**
+     * Sets the property on the given object to the new value
+     *
+     * @param object on which to set the property
+     * @param newValue the new value of the property
+     * @throws RuntimeException if the property could not be set
+     */
+    public void setProperty(final Object object, Object newValue) {
+        final Object goalValue = DefaultTypeTransformation.castToType(newValue, field.getType());
+
+        if ( !alreadySetAccessible ) {
+            AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    field.setAccessible(true);
+                    return null;
+                }
+            });
+            alreadySetAccessible = true;
+        }
+
+        try {
+            field.set(object, goalValue);
+        } catch (IllegalAccessException ex) {
+            throw new GroovyRuntimeException("Cannot set the property '" + name + "'.", ex);
+        }
+    }
+
+    private String toName(Class c) {
+        String s = c.toString();
+        if (s.startsWith("class ") && s.length() > 6)
+            return s.substring(6);
+        else
+            return s;
+    }
+
+    public int getModifiers() {
+        return field.getModifiers();
+    }
+
+    public boolean isStatic() {
+        return Modifier.isStatic(field.getModifiers());
+    }
+}
diff --git a/groovy/src/main/groovy/lang/MetaMethod.java b/groovy/src/main/groovy/lang/MetaMethod.java
new file mode 100644
index 0000000..0012b2f
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MetaMethod.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import org.codehaus.groovy.classgen.BytecodeHelper;
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.ParameterTypes;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Represents a Method on a Java object a little like {@link java.lang.reflect.Method}
+ * except without using reflection to invoke the method
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class MetaMethod implements Cloneable {
+    private String signature;
+    private String mopName;
+
+    public MetaMethod() {
+    }
+
+    public abstract int getModifiers();
+
+    public abstract String getName();
+
+    public abstract Class getReturnType();
+
+    public abstract CachedClass getDeclaringClass();
+
+    public abstract ParameterTypes getParamTypes();
+
+    public abstract Object invoke(Object object, Object[] arguments);
+
+    public final CachedClass [] getParameterTypes() {
+        return getParamTypes().getParameterTypes();
+    }
+
+    /**
+     * Checks that the given parameters are valid to call this method
+     *
+     * @param arguments the arguments to check
+     * @throws IllegalArgumentException if the parameters are not valid
+     */
+    public void checkParameters(Class[] arguments) {
+        // lets check that the argument types are valid
+        if (!MetaClassHelper.isValidMethod(getParameterTypes(), arguments, false)) {
+            throw new IllegalArgumentException(
+                    "Parameters to method: "
+                    + getName()
+                    + " do not match types: "
+                    + InvokerHelper.toString(getParameterTypes())
+                    + " for arguments: "
+                    + InvokerHelper.toString(arguments));
+        }
+    }
+    public boolean isMethod(Method method) {
+        return getName().equals(method.getName())
+            && getModifiers() == method.getModifiers()
+            && getReturnType().equals(method.getReturnType())
+            && equal(getParameterTypes(), method.getParameterTypes());
+    }
+
+    protected static boolean equal(CachedClass[] a, Class[] b) {
+        if (a.length == b.length) {
+            for (int i = 0, size = a.length; i < size; i++) {
+                if (!a[i].getCachedClass().equals(b[i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    protected static boolean equal(CachedClass[] a, CachedClass[] b) {
+        if (a.length == b.length) {
+            for (int i = 0, size = a.length; i < size; i++) {
+                if (a[i] != b[i]) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public String toString() {
+        return super.toString()
+            + "[name: "
+            + getName()
+            + " params: "
+            + InvokerHelper.toString(getParameterTypes())
+            + " returns: "
+            + getReturnType()
+            + " owner: "
+            + getDeclaringClass()
+            + "]";
+    }
+
+    public Object clone() {
+        try {
+            return super.clone();
+        }
+        catch (CloneNotSupportedException e) {
+            throw new GroovyRuntimeException("This should never happen", e);
+        }
+    }
+
+    public boolean isStatic() {
+        return (getModifiers() & Modifier.STATIC) != 0;
+    }
+
+    public final boolean isPrivate() {
+        return (getModifiers() & Modifier.PRIVATE) != 0;
+    }
+
+    public final boolean isProtected() {
+        return (getModifiers() & Modifier.PROTECTED) != 0;
+    }
+
+    public final boolean isPublic() {
+        return (getModifiers() & Modifier.PUBLIC) != 0;
+    }
+
+    /**
+     * @param method the method to compare against
+     * @return true if the given method has the same name, parameters, return type
+     * and modifiers but may be defined on another type
+     */
+    public final boolean isSame(MetaMethod method) {
+        return getName().equals(method.getName())
+            && compatibleModifiers(getModifiers(), method.getModifiers())
+            && getReturnType().equals(method.getReturnType())
+            && equal(getParameterTypes(), method.getParameterTypes());
+    }
+
+    private static boolean compatibleModifiers(int modifiersA, int modifiersB) {
+        int mask = Modifier.PRIVATE | Modifier.PROTECTED | Modifier.PUBLIC | Modifier.STATIC;
+        return (modifiersA & mask) == (modifiersB & mask);
+    }
+
+    public boolean isCacheable() {
+        return true;
+    }
+
+    public final Class[] getNativeParameterTypes() {
+        return getParamTypes().getNativeParameterTypes();
+    }
+
+    public String getDescriptor() {
+        return BytecodeHelper.getMethodDescriptor(getReturnType(), getNativeParameterTypes());
+    }
+
+    public synchronized String getSignature() {
+        if (signature == null) {
+            CachedClass [] parameters = getParameterTypes();
+            final String name = getName();
+            StringBuffer buf = new StringBuffer(name.length()+parameters.length*10);
+            buf.append(getReturnType().getName());
+            //
+            buf.append(' ');
+            buf.append(name);
+            buf.append('(');
+            for (int i = 0; i < parameters.length; i++) {
+                if (i > 0) {
+                    buf.append(", ");
+                }
+                buf.append(parameters[i].getName());
+            }
+            buf.append(')');
+            signature = buf.toString();
+        }
+        return signature;
+    }
+
+    public String getMopName() {
+        if (mopName == null) {
+          String name = getName();
+          CachedClass declaringClass = getDeclaringClass();
+          if ((getModifiers() & (Modifier.PUBLIC| Modifier.PROTECTED)) == 0)
+            mopName = new StringBuffer().append("this$").append(declaringClass.getSuperClassDistance()).append("$").append(name).toString();
+          else
+            mopName = new StringBuffer().append("super$").append(declaringClass.getSuperClassDistance()).append("$").append(name).toString();
+        }
+        return mopName;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/MetaObjectProtocol.java b/groovy/src/main/groovy/lang/MetaObjectProtocol.java
new file mode 100644
index 0000000..6f14b96
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MetaObjectProtocol.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.util.List;
+
+/**
+ * <p>An interface that defines the API usable by clients of Groovy's Meta Object Protocol (MOP). These methods are
+ * implemented by the reference implementation of the @link groovy.lang.MetaClass interface.</p>
+ *
+ * @see MetaClassImpl
+ *
+ * @author John Wilson
+ * @author Graeme Rocher
+ */
+public interface MetaObjectProtocol {
+
+    /**
+     * Obtain a list of all meta properties available on this meta class
+     *
+     * @see groovy.lang.MetaBeanProperty
+     * @return A list of MetaBeanProperty instances
+     */
+    List getProperties();
+    /**
+     * Obtain a list of all the meta methods available on this meta class
+     *
+     * @see groovy.lang.MetaMethod
+     * @return A list of MetaMethod instances
+     */
+    List getMethods();
+    
+    /**
+     * <p>Returns true if the implementing MetaClass responds a method with the given name and arguments types
+     *
+     * <p>Note that this method will only return true for realised methods and does not take into account
+     * objects or classes that implement invokeMethod or methodMissing
+     *
+     * <p>This method is "safe" and will always return a boolean and never throw an exception
+     *
+     * @param obj The object to inspect
+     * @param name The name of the method
+     * @param argTypes The argument types
+     * @return A List of MetaMethods which is empty if non exist
+     */
+    List respondsTo(Object obj,String name, Object[] argTypes);
+
+    /**
+     * <p>Returns true if the implementing MetaClass responds a method with the given name regardless of
+     * arguments. In other words this method will return for foo() and foo(String)
+     *
+     * <p>Note that this method will only return true for realised methods and does not take into account
+     * objects or classes that implement invokeMethod or methodMissing
+     *
+     * <p>This method is "safe" and will always return a boolean and never throw an exception
+     *
+     * @param obj The object to inspect
+     * @param name The name of the method
+     * @return A List of MetaMethods which is empty if non exist
+     */
+    List respondsTo(Object obj,String name);
+
+    /**
+     * <p>Returns true of the implementing MetaClass has a property of the given name
+     *
+     * <p>Note that this method will only return true for realised properties and does not take into
+     * account implementation of getProperty or propertyMissing
+     *
+     * @param obj The object to inspect
+     * @param name The name of the property
+     * @return The MetaProperty or null if it doesn't exist
+     */
+    MetaProperty hasProperty(Object obj, String name);
+
+    /**
+     * Returns a MetaProperty for the given name or null if it doesn't exist
+     *
+     * @param name The name of the MetaProperty
+     * @return A MetaProperty or null
+     */
+    MetaProperty getMetaProperty(String name);
+
+    /**
+     * Retreives a static MetaMethod for the given name and argument values, using the types of the arguments
+     * to establish the chosen MetaMethod
+     *
+     * @param name The name of the MetaMethod
+     * @param args The argument types
+     * @return A MetaMethod or null if it doesn't exist
+     */
+    MetaMethod getStaticMetaMethod(String name, Object[] args);
+
+
+    /**
+     * Retrieves an instance MetaMethod for the given name and argument values, using the types of the
+     * argument values to establish the chosen MetaMethod
+     *
+     * @param name The name of the MetaMethod
+     * @param args The argument types
+     * @return A MetaMethod or null if it doesn't exist
+     */
+    MetaMethod getMetaMethod(String name, Object[] args);
+
+    /**
+     * Retrieves that Java Class that the attached Meta behaviours apply to
+     *
+     * @return The java.lang.Class instance
+     */
+    Class getTheClass();
+
+    /**
+     * Invokes a constructor for the given arguments. The MetaClass will attempt to pick the best argument which
+     * matches the types of the objects passed within the arguments array
+     *
+     * @param arguments The arguments to the constructor
+     * @return An instance of the java.lang.Class that this MetaObjectProtocol object applies to
+     */
+    Object invokeConstructor(Object[] arguments);
+
+    /**
+     * <p>Invokes a method on the given Object with the given name and arguments. The MetaClass will attempt to pick
+     * the best method for the given name and arguments. If a method cannot be invoked a MissingMethodException will be
+     * thrown.</p>
+     *
+     *
+     * @see groovy.lang.MissingMethodException
+     *
+     * @param object The instance which the method is invoked on
+     * @param methodName The name of the method
+     * @param arguments The arguments to the method
+     * @return The return value of the method which is null if the return type is void
+     */
+    Object invokeMethod(Object object, String methodName, Object[] arguments);
+
+    /**
+     * <p>Invokes a method on the given object, with the given name and single argument.</p>
+     *
+     * @see #invokeMethod(Object, String, Object[])
+     *
+     * @param object The Object to invoke the method on
+     * @param methodName The name of the method
+     * @param arguments The argument to the method
+     * @return The return value of the method which is null if the return type is void
+     */
+     Object invokeMethod(Object object, String methodName, Object arguments);
+
+    /**
+     * <p>Invokes a static method on the given Object with the given name and arguments.</p>
+     *
+     * <p> The Object can either be an instance of the class that this
+     * MetaObjectProtocol instance applies to or the java.lang.Class instance itself. If a method cannot be invoked
+     * a MissingMethodException is will be thrown</p>
+     *
+     * @see groovy.lang.MissingMethodException
+     *
+     * @param object An instance of the class returned by the getTheClass() method or the class itself
+     * @param methodName The name of the method
+     * @param arguments The arguments to the method
+     * @return The return value of the method which is null if the return type is void
+     */
+    Object invokeStaticMethod(Object object, String methodName, Object[] arguments);
+
+    /**
+     * <p>Retrieves a property of an instance of the class returned by the getTheClass() method. </p>
+     *
+     * <p>What this means is largely down to the MetaClass implementation, however the default case would result
+     * in an attempt to invoke a JavaBean getter, or if no such getter exists a public field of the instance.</p>
+     *
+     * @see MetaClassImpl
+     *
+     * @param object An instance of the class returned by the getTheClass() method
+     * @param property The name of the property to retrieve the value for
+     * @return The properties value
+     */
+    Object getProperty(Object object, String property);
+
+    /**
+     * <p>Sets a property of an instance of the class returned by the getTheClass() method.</p>
+     *
+     * <p>What this means is largely down to the MetaClass implementation, however the default case would result
+     * in an attempt to invoke a JavaBean setter, or if no such setter exists to set a public field of the instance.</p>
+     *
+     * @see MetaClassImpl
+     *
+     * @param object An instance of the class returned by the getTheClass() method
+     * @param property The name of the property to set
+     * @param newValue The new value of the property
+     */
+    void setProperty(Object object, String property, Object newValue);
+
+    /**
+     * <p>Retrieves an attribute of an instance of the class returned by the getTheClass() method. </p>
+     *
+     * <p>What this means is largely down to the MetaClass implementation, however the default case would result
+     * in attempt to read a field of the instance.</p>
+     *
+     * @see MetaClassImpl
+     *
+     * @param object An instance of the class returned by the getTheClass() method
+     * @param attribute The name of the attribute to retrieve the value for
+     * @return The attribute value
+     */
+    Object getAttribute(Object object, String attribute);
+
+    /**
+     * <p>Sets an attribute of an instance of the class returned by the getTheClass() method.</p>
+     *
+     * <p>What this means is largely down to the MetaClass implementation, however the default case would result
+     * in an attempt to set a field of the instance.</p>
+     *
+     * @see MetaClassImpl
+     *
+     * @param object An instance of the class returned by the getTheClass() method
+     * @param attribute The name of the attribute to set
+     * @param newValue The new value of the attribute
+     */
+    void setAttribute(Object object, String attribute, Object newValue);
+}
diff --git a/groovy/src/main/groovy/lang/MetaProperty.java b/groovy/src/main/groovy/lang/MetaProperty.java
new file mode 100644
index 0000000..55734f8
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MetaProperty.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * Represents a property on a bean which may have a getter and/or a setter
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class MetaProperty {
+
+    protected final String name;
+    protected Class type;
+
+    public MetaProperty(String name, Class type) {
+        this.name = name;
+        this.type = type;
+    }
+
+    /**
+     * @return the property of the given object
+     * @throws Exception if the property could not be evaluated
+     */
+    public abstract Object getProperty(Object object);
+
+    /**
+     * Sets the property on the given object to the new value
+     * 
+     * @param object on which to set the property
+     * @param newValue the new value of the property
+     * @throws RuntimeException if the property could not be set
+     */
+    public abstract void setProperty(Object object, Object newValue);
+
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @return the type of the property
+     */
+    public Class getType() {
+        return type;
+    }
+    
+    public int getModifiers() {
+        return Modifier.PUBLIC;
+    }
+
+}
diff --git a/groovy/src/main/groovy/lang/MissingClassException.java b/groovy/src/main/groovy/lang/MissingClassException.java
new file mode 100644
index 0000000..bb72cf0
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MissingClassException.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+
+/**
+ * An exception occurred if a dynamic method dispatch fails with an unknown class.
+ * 
+ * Note that the Missing*Exception classes were named for consistency and
+ * to avoid conflicts with JDK exceptions of the same name.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MissingClassException extends GroovyRuntimeException {
+
+    private final String type;
+
+    public MissingClassException(String type, ASTNode node, String message) {
+        super("No such class: " + type + " " + message, node);
+        this.type = type;
+    }
+    
+    public MissingClassException(ClassNode type, String message){
+        super("No such class: " + type.getName() + " " + message);
+        this.type = type.getName();
+    }
+
+    /**
+     * 
+     * @return The type that could not be resolved
+     */
+    public String getType() {
+        return type;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/MissingFieldException.java b/groovy/src/main/groovy/lang/MissingFieldException.java
new file mode 100644
index 0000000..3654231
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MissingFieldException.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+
+/**
+ * An exception occurred if a dynamic field dispatch fails with an unknown field.
+ * 
+ * Note that the Missing*Exception classes were named for consistency and
+ * to avoid conflicts with JDK exceptions of the same name.
+ * 
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @version $Revision$
+ */
+public class MissingFieldException extends GroovyRuntimeException {
+
+    private final String field;
+    private final Class type;
+
+    public MissingFieldException(String field, Class type) {
+        super("No such field: " + field + " for class: " + type.getName());
+        this.field = field;
+        this.type = type;
+    }
+
+    public MissingFieldException(String field, Class type, Throwable e) {
+        super("No such field: " + field + " for class: " + type.getName() + ". Reason: " + e, e);
+        this.field = field;
+        this.type = type;
+    }
+
+    public MissingFieldException(String message, String field, Class type) {
+        super(message);
+        this.field = field;
+        this.type = type;
+    }
+
+    /**
+     * @return the name of the field that could not be found
+     */
+    public String getField() {
+        return field;
+    }
+
+    /**
+     * 
+     * @return The type on which the field was attempted to be called
+     */
+    public Class getType() {
+        return type;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/MissingMethodException.java b/groovy/src/main/groovy/lang/MissingMethodException.java
new file mode 100644
index 0000000..8c2d5e8
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MissingMethodException.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * An exception occurred if a dynamic method dispatch fails with an unknown method.
+ * 
+ * Note that the Missing*Exception classes were named for consistency and
+ * to avoid conflicts with JDK exceptions of the same name.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MissingMethodException extends GroovyRuntimeException {
+
+    private final String method;
+    private final Class type;
+    private final boolean isStatic;
+
+    public Object[] getArguments() {
+        return arguments;
+    }
+
+    private final Object arguments [];
+
+    public MissingMethodException(String method, Class type, Object[] arguments) {
+        this(method,type,arguments,false);
+    }
+    
+    public MissingMethodException(String method, Class type, Object[] arguments, boolean isStatic) {
+	    super();
+	    this.method = method;
+	    this.type = type;
+        this.isStatic = isStatic;
+        this.arguments = arguments;
+    }
+
+    public String getMessage() {
+        return "No signature of method: "
+                    + (isStatic ? "static " : "")
+                    + type.getName()
+                    + "."
+                    + method
+                    + "() is applicable for argument types: ("
+                    + InvokerHelper.toTypeString(arguments)
+                    + ") values: "
+                    + InvokerHelper.toString(arguments);
+    }
+
+    /**
+     * @return the name of the method that could not be found
+     */
+    public String getMethod() {
+        return method;
+    }
+
+    /**
+     * @return The type on which the method was attempted to be called
+     */
+    public Class getType() {
+        return type;
+    }
+    
+    /**
+     * @return Whether the method was called in a static way, 
+     * i.e. on a class rather than an object.
+     */
+    public boolean isStatic() {
+        return isStatic;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/MissingPropertyException.java b/groovy/src/main/groovy/lang/MissingPropertyException.java
new file mode 100644
index 0000000..88b0a99
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MissingPropertyException.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+
+/**
+ * An exception occurred if a dynamic property dispatch fails with an unknown property.
+ * 
+ * Note that the Missing*Exception classes were named for consistency and
+ * to avoid conflicts with JDK exceptions of the same name.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MissingPropertyException extends GroovyRuntimeException {
+
+    public static final Object MPE = new Object();
+
+    private final String property;
+    private final Class type;
+
+    public MissingPropertyException(String property, Class type) {
+        this.property = property;
+        this.type = type;
+    }
+
+    public MissingPropertyException(String property, Class type, Throwable t) {
+        super(t);
+        this.property = property;
+        this.type = type;
+    }
+
+    public MissingPropertyException(String message) {
+        super(message);
+        this.property = null;
+        this.type = null;
+    }
+
+    public MissingPropertyException(String message, String property, Class type) {
+        super(message);
+        this.property = property;
+        this.type = type;
+    }
+
+    public String getMessageWithoutLocationText() {
+        final Throwable cause = getCause();
+        if (cause == null) {
+            if (super.getMessageWithoutLocationText() != null) {
+                return super.getMessageWithoutLocationText();
+            }
+            return "No such property: " + property + " for class: " + type.getName();
+        }
+        return "No such property: " + property + " for class: " + type.getName() + ". Reason: " + cause;
+    }
+
+    /**
+     * @return the name of the property that could not be found
+     */
+    public String getProperty() {
+        return property;
+    }
+
+    /**
+     * 
+     * @return The type on which the property was attempted to be called
+     */
+    public Class getType() {
+        return type;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/MutableMetaClass.java b/groovy/src/main/groovy/lang/MutableMetaClass.java
new file mode 100644
index 0000000..f23d810
--- /dev/null
+++ b/groovy/src/main/groovy/lang/MutableMetaClass.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import java.lang.reflect.Method;
+
+/**
+ * <p>An interface that defines methods that implementors of mutable Meta classes should specifiy. It provides operations to perform mutations
+ * on the MetaClass instance.</p>
+ *
+ * <p>Whether a MetaClass allows mutation is up to the MetaClass itself and considerations of Thread safety
+ * need to be taken into account when making a MetaClass mutable</p>
+ *
+ * <p>The default implementation allows mutation of MetaClass instances before initialisation (before the initialize() method is called)
+ * but not after, thus ensuring Thread safety once a MetaClass has been constructed and placed in the registry</p>
+ *
+ *
+ * @see MetaClassImpl
+ * @see MetaClass
+ *
+ * @author Graeme Rocher
+ * @since 1.1
+ *
+ *        <p/>
+ *        Created: Feb 24, 2007
+ *        Time: 4:05:44 PM
+ */
+public interface MutableMetaClass extends MetaClass {
+
+    /**
+     * Return whether the MetaClass has been modified or not
+     * @return True if it has
+     */
+    boolean isModified();
+
+    /**
+     * adds a new instance method to this MetaClass. Instance
+     * methods are able to overwrite the original methods of the
+     * class. Calling this method should not be done after
+     * initlise was called.
+     *
+     * @param method the method to be added
+     */
+     void addNewInstanceMethod(Method method);
+
+    /**
+     * adds a new static method to this MetaClass. This is only
+     * possible as long as initilise was not called.
+     *
+     * @param method the method to be added
+     */
+     void addNewStaticMethod(Method method);
+
+    /**
+     * Adds a new MetaMethod to the MetaClass
+     *
+     * @param metaMethod The MetaMethod to add
+     */
+    void addMetaMethod(MetaMethod metaMethod);
+
+    /**
+     * Adds a new MetaBeanProperty to the MetaClass
+     *
+     * @param metaBeanProperty The MetaBeanProperty instance
+     */
+    void addMetaBeanProperty(MetaBeanProperty metaBeanProperty);
+    
+    // TODO: Add methods like addMetaConstructor, addMetaAttribute, addMetaAnnotation etc.
+}
diff --git a/groovy/src/main/groovy/lang/NonEmptySequence.java b/groovy/src/main/groovy/lang/NonEmptySequence.java
new file mode 100644
index 0000000..a3b3236
--- /dev/null
+++ b/groovy/src/main/groovy/lang/NonEmptySequence.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.util.List;
+
+/**
+ * Represents a sequence of objects which represents one or many instances of
+ * of objects of a given type. The type can be ommitted in which case any type of
+ * object can be added.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class NonEmptySequence extends Sequence {
+
+    public NonEmptySequence() {
+        super(null);
+    }
+
+    public NonEmptySequence(Class type) {
+        super(type);
+    }
+
+    public NonEmptySequence(Class type, List content) {
+        super(type, content);
+    }
+
+    public int minimumSize() {
+        return 1;
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/lang/ObjectRange.java b/groovy/src/main/groovy/lang/ObjectRange.java
new file mode 100644
index 0000000..3b12695
--- /dev/null
+++ b/groovy/src/main/groovy/lang/ObjectRange.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.IteratorClosureAdapter;
+import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.AbstractList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Represents an inclusive list of objects from a value to a value using
+ * comparators.
+ * <p/>
+ * This class is similar to {@link IntRange}. If you make any changes to this
+ * class, you might consider making parallel changes to {@link IntRange}.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ObjectRange extends AbstractList implements Range {
+
+    /**
+     * The first value in the range.
+     */
+    private Comparable from;
+
+    /**
+     * The last value in the range.
+     */
+    private Comparable to;
+
+    /**
+     * The cached size, or -1 if not yet computed
+     */
+    private int size = -1;
+
+    /**
+     * <code>true</code> if the range counts backwards from <code>to</code> to <code>from</code>.
+     */
+    private final boolean reverse;
+
+    /**
+     * Creates a new {@link ObjectRange}. Creates a reversed range if
+     * <code>from</code> < <code>to</code>.
+     *
+     * @param from the first value in the range.
+     * @param to   the last value in the range.
+     */
+    public ObjectRange(Comparable from, Comparable to) {
+        if (from == null) {
+            throw new IllegalArgumentException("Must specify a non-null value for the 'from' index in a Range");
+        }
+        if (to == null) {
+            throw new IllegalArgumentException("Must specify a non-null value for the 'to' index in a Range");
+        }
+
+        this.reverse = ScriptBytecodeAdapter.compareGreaterThan(from, to);
+        if (this.reverse) {
+            constructorHelper(to, from);
+        } else {
+            constructorHelper(from, to);
+        }
+    }
+
+    public ObjectRange(Comparable from, Comparable to, boolean reverse) {
+        constructorHelper(from, to);
+
+        this.reverse = reverse;
+    }
+
+    private void constructorHelper(Comparable from, Comparable to) {
+        if (from instanceof Short) {
+            from = new Integer(((Short) from).intValue());
+        }
+        if (to instanceof Short) {
+            to = new Integer(((Short) to).intValue());
+        }
+        if (from instanceof Float) {
+            from = new Double(((Float) from).doubleValue());
+        }
+        if (to instanceof Float) {
+            to = new Double(((Float) to).doubleValue());
+        }
+        // TODO: should we care about different types here?
+        if (from.getClass() == to.getClass()) {
+            this.from = from;
+            this.to = to;
+        } else {
+            this.from = normaliseStringType(from);
+            this.to = normaliseStringType(to);
+        }
+        if (from instanceof String || to instanceof String) {
+            // this test depends deeply on the String.next implementation
+            // 009.next is 00:, not 010 
+            String start = from.toString();
+            String end = to.toString();
+            if (start.length() > end.length()) {
+                throw new IllegalArgumentException("Incompatible Strings for Range: starting String is longer than ending string");
+            }
+            int length = Math.min(start.length(), end.length());
+            int i;
+            for (i = 0; i < length; i++) {
+                if (start.charAt(i) != end.charAt(i)) break;
+            }
+            if (i < length - 1) {
+                throw new IllegalArgumentException("Incompatible Strings for Range: String#next() will not reach the expected value");
+            }
+
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean equals(Object that) {
+        return (that instanceof ObjectRange) ? equals((ObjectRange) that) : super.equals(that);
+    }
+
+    /**
+     * Compares an {@link ObjectRange} to another {@link ObjectRange}.
+     *
+     * @return <code>true</code> if the ranges are equal
+     * @param that the object to check equality with
+     */
+    public boolean equals(ObjectRange that) {
+        return that != null
+                && this.reverse == that.reverse
+                && DefaultTypeTransformation.compareEqual(this.from, that.from)
+                && DefaultTypeTransformation.compareEqual(this.to, that.to);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Comparable getFrom() {
+        return from;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Comparable getTo() {
+        return to;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isReverse() {
+        return reverse;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object get(int index) {
+        if (index < 0) {
+            throw new IndexOutOfBoundsException("Index: " + index + " should not be negative");
+        }
+        if (index >= size()) {
+            throw new IndexOutOfBoundsException("Index: " + index + " is too big for range: " + this);
+        }
+        Object value;
+        if (reverse) {
+            value = to;
+
+            for (int i = 0; i < index; i++) {
+                value = decrement(value);
+            }
+        } else {
+            value = from;
+            for (int i = 0; i < index; i++) {
+                value = increment(value);
+            }
+        }
+        return value;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Iterator iterator() {
+        return new Iterator() {
+            private int index;
+            private Object value = reverse ? to : from;
+
+            public boolean hasNext() {
+                return index < size();
+            }
+
+            public Object next() {
+                if (index++ > 0) {
+                    if (index > size()) {
+                        value = null;
+                    } else {
+                        if (reverse) {
+                            value = decrement(value);
+                        } else {
+                            value = increment(value);
+                        }
+                    }
+                }
+                return value;
+            }
+
+            public void remove() {
+                ObjectRange.this.remove(index);
+            }
+        };
+    }
+
+    /**
+     * Checks whether a value is between the from and to values of a Range
+     *
+     * @param value the value of interest
+     * @return true if the value is within the bounds
+     */
+    public boolean containsWithinBounds(Object value) {
+        if (value instanceof Comparable) {
+            int result = from.compareTo(value);
+            return result == 0 || result < 0 && to.compareTo(value) >= 0;
+        }
+        return contains(value);
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public int size() {
+        if (size == -1) {
+            if (from instanceof Integer && to instanceof Integer
+                    || from instanceof Long && to instanceof Long) {
+                // let's fast calculate the size
+                size = 0;
+                int fromNum = ((Number) from).intValue();
+                int toNum = ((Number) to).intValue();
+                size = toNum - fromNum + 1;
+            } else if (from instanceof Character && to instanceof Character) {
+                // let's fast calculate the size
+                size = 0;
+                char fromNum = ((Character) from).charValue();
+                char toNum = ((Character) to).charValue();
+                size = toNum - fromNum + 1;
+            } else if (from instanceof BigDecimal || to instanceof BigDecimal) {
+                // let's fast calculate the size
+                size = 0;
+                BigDecimal fromNum = new BigDecimal("" + from);
+                BigDecimal toNum = new BigDecimal("" + to);
+                BigInteger sizeNum = toNum.subtract(fromNum).add(new BigDecimal(1.0)).toBigInteger();
+                size = sizeNum.intValue();
+            } else {
+                // let's lazily calculate the size
+                size = 0;
+                Object value = from;
+                while (to.compareTo(value) >= 0) {
+                    value = increment(value);
+                    size++;
+                }
+            }
+        }
+        return size;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List subList(int fromIndex, int toIndex) {
+        if (fromIndex < 0) {
+            throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
+        }
+        if (toIndex > size()) {
+            throw new IndexOutOfBoundsException("toIndex = " + toIndex);
+        }
+        if (fromIndex > toIndex) {
+            throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")");
+        }
+        if (fromIndex == toIndex) {
+            return new EmptyRange(from);
+        }
+
+        return new ObjectRange((Comparable) get(fromIndex), (Comparable) get(--toIndex), reverse);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String toString() {
+        return reverse ? "" + to + ".." + from : "" + from + ".." + to;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String inspect() {
+        String toText = InvokerHelper.inspect(to);
+        String fromText = InvokerHelper.inspect(from);
+        return reverse ? "" + toText + ".." + fromText : "" + fromText + ".." + toText;
+    }
+
+    public boolean contains(Object value) {
+        Iterator it = iterator();
+        if (value==null) return false;
+        while (it.hasNext()) {
+            try {
+                if (DefaultTypeTransformation.compareEqual(value, it.next())) return true;
+            } catch (ClassCastException e) {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void step(int step, Closure closure) {
+        if (reverse) {
+            step = -step;
+        }
+        if (step >= 0) {
+            Comparable value = from;
+            while (value.compareTo(to) <= 0) {
+                closure.call(value);
+                for (int i = 0; i < step; i++) {
+                    value = (Comparable) increment(value);
+                }
+            }
+        } else {
+            step = -step;
+            Comparable value = to;
+            while (value.compareTo(from) >= 0) {
+                closure.call(value);
+                for (int i = 0; i < step; i++) {
+                    value = (Comparable) decrement(value);
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public List step(int step) {
+        IteratorClosureAdapter adapter = new IteratorClosureAdapter(this);
+        step(step, adapter);
+        return adapter.asList();
+    }
+
+    /**
+     * Increments by one
+     *
+     * @param value the value to increment
+     * @return the incremented value
+     */
+    protected Object increment(Object value) {
+        return InvokerHelper.invokeMethod(value, "next", null);
+    }
+
+    /**
+     * Decrements by one
+     *
+     * @param value the value to decrement
+     * @return the decremented value
+     */
+    protected Object decrement(Object value) {
+        return InvokerHelper.invokeMethod(value, "previous", null);
+    }
+
+    private static Comparable normaliseStringType(final Comparable operand) {
+        if (operand instanceof Character) {
+            return new Integer(((Character) operand).charValue());
+        } else if (operand instanceof String) {
+            final String string = (String) operand;
+
+            if (string.length() == 1)
+                return new Integer(string.charAt(0));
+            else
+                return string;
+        } else {
+            return operand;
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/lang/ParameterArray.java b/groovy/src/main/groovy/lang/ParameterArray.java
new file mode 100644
index 0000000..d562f5d
--- /dev/null
+++ b/groovy/src/main/groovy/lang/ParameterArray.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+/**
+ * Distinguish a parameter array from Object[].
+ *
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+public class ParameterArray {
+
+    private Object parameters;
+
+    public ParameterArray(Object data) {
+        parameters = packArray(data);
+    }
+
+    private Object packArray(Object object) {
+        if (object instanceof Object[])
+            return (Object[]) object;
+        else
+            return object;
+    }
+
+    public Object get() {
+        return parameters;
+    }
+
+    public String toString() {
+        if (parameters == null)
+            return "<null parameter>";
+        return parameters.toString();
+    }
+}
diff --git a/groovy/src/main/groovy/lang/PropertyAccessInterceptor.java b/groovy/src/main/groovy/lang/PropertyAccessInterceptor.java
new file mode 100644
index 0000000..5b2378e
--- /dev/null
+++ b/groovy/src/main/groovy/lang/PropertyAccessInterceptor.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+/**
+ * <p>An interface that adds the ability to intercept
+ * property getters/setters
+ *
+ * @author Graeme Rocher
+ * @since Oct 24, 2005
+ */
+public interface PropertyAccessInterceptor extends Interceptor  {
+
+	/**
+	 * Intercepts a getXXX call and returns a result. The result is replaced by the
+	 * real value if doGet() return false
+	 *
+	 * @param object The target object
+	 * @param property The property to get
+	 *
+	 * @return A value supplied by the interceptor
+	 */
+	Object beforeGet(Object object, String property);
+
+	/**
+	 * Intercepts a setXXX call
+	 *
+	 * @param object The target object
+	 * @param property The property to set
+	 * @param newValue The new value
+	 *
+	 */
+	void beforeSet(Object object, String property, Object newValue);
+
+}
diff --git a/groovy/src/main/groovy/lang/PropertyValue.java b/groovy/src/main/groovy/lang/PropertyValue.java
new file mode 100644
index 0000000..cc36bc6
--- /dev/null
+++ b/groovy/src/main/groovy/lang/PropertyValue.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+public class PropertyValue {
+	// the owner of the property
+	private Object bean;
+	
+	// the description of the property
+	private MetaProperty mp;
+	
+	public PropertyValue(Object bean, MetaProperty mp) {
+		this.bean = bean;
+		this.mp = mp;
+	}
+	
+	public String getName() {
+		return mp.getName();
+	}
+	
+	public Class getType() {
+		return mp.getType();
+	}
+	
+	public Object getValue() {
+		return mp.getProperty(bean);
+	}
+	
+	public void setValue(Object value)  {
+		mp.setProperty(bean, value);
+	}
+}
+
diff --git a/groovy/src/main/groovy/lang/ProxyMetaClass.java b/groovy/src/main/groovy/lang/ProxyMetaClass.java
new file mode 100644
index 0000000..661e096
--- /dev/null
+++ b/groovy/src/main/groovy/lang/ProxyMetaClass.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.beans.IntrospectionException;
+
+/**
+ * As subclass of MetaClass, ProxyMetaClass manages calls from Groovy Objects to POJOs.
+ * It enriches MetaClass with the feature of making method invokations interceptable by
+ * an Interceptor. To this end, it acts as a decorator (decorator pattern) allowing
+ * to add or withdraw this feature at runtime.
+ * See groovy/lang/InterceptorTest.groovy for details.
+ * <p/>
+ * <p>WARNING: This implementation of ProxyMetaClass is NOT threadsafe and hence should only be used for
+ * as a per-instance MetaClass running in a single thread. Do not place this MetaClass in the MetaClassRegistry
+ * as it will result in unpredictable behaviour
+ *
+ * @author Dierk Koenig
+ * @author Graeme Rocher
+ * @see groovy.lang.MetaClassRegistry
+ */
+public class ProxyMetaClass extends MetaClassImpl implements AdaptingMetaClass {
+
+    protected MetaClass adaptee = null;
+    protected Interceptor interceptor = null;
+
+
+    /**
+     * convenience factory method for the most usual case.
+     */
+    public static ProxyMetaClass getInstance(Class theClass) throws IntrospectionException {
+        MetaClassRegistry metaRegistry = GroovySystem.getMetaClassRegistry();
+        MetaClass meta = metaRegistry.getMetaClass(theClass);
+        return new ProxyMetaClass(metaRegistry, theClass, meta);
+    }
+
+    /**
+     * @param adaptee the MetaClass to decorate with interceptability
+     */
+    public ProxyMetaClass(MetaClassRegistry registry, Class theClass, MetaClass adaptee) throws IntrospectionException {
+        super(registry, theClass);
+        this.adaptee = adaptee;
+        if (null == adaptee) throw new IllegalArgumentException("adaptee must not be null");
+        super.initialize();
+    }
+
+    public synchronized void initialize() {
+        this.adaptee.initialize();
+    }
+
+    /**
+     * Use the ProxyMetaClass for the given Closure.
+     * Cares for balanced register/unregister.
+     *
+     * @param closure piece of code to be executed with registered ProxyMetaClass
+     */
+    public void use(Closure closure) {
+        // grab existing meta (usually adaptee but we may have nested use calls)
+        MetaClass origMetaClass = registry.getMetaClass(theClass);
+        registry.setMetaClass(theClass, this);
+        try {
+            closure.call();
+        } finally {
+            registry.setMetaClass(theClass, origMetaClass);
+        }
+    }
+
+    /**
+     * Use the ProxyMetaClass for the given Closure.
+     * Cares for balanced setting/unsetting ProxyMetaClass.
+     *
+     * @param closure piece of code to be executed with ProxyMetaClass
+     */
+    public void use(GroovyObject object, Closure closure) {
+        // grab existing meta (usually adaptee but we may have nested use calls)
+        MetaClass origMetaClass = object.getMetaClass();
+        object.setMetaClass(this);
+        try {
+            closure.call();
+        } finally {
+            object.setMetaClass(origMetaClass);
+        }
+    }
+
+    /**
+     * @return the interceptor in use or null if no interceptor is used
+     */
+    public Interceptor getInterceptor() {
+        return interceptor;
+    }
+
+    /**
+     * @param interceptor may be null to reset any interception
+     */
+    public void setInterceptor(Interceptor interceptor) {
+        this.interceptor = interceptor;
+    }
+
+    /**
+     * Call invokeMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
+     * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
+     * The method call is suppressed if Interceptor.doInvoke() returns false.
+     * See Interceptor for details.
+     */
+    public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
+        return doCall(object, methodName, arguments, interceptor, new Callable() {
+            public Object call() {
+                return adaptee.invokeMethod(object, methodName, arguments);
+            }
+        });
+    }
+
+    /**
+     * Call invokeStaticMethod on adaptee with logic like in MetaClass unless we have an Interceptor.
+     * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
+     * The method call is suppressed if Interceptor.doInvoke() returns false.
+     * See Interceptor for details.
+     */
+    public Object invokeStaticMethod(final Object object, final String methodName, final Object[] arguments) {
+        return doCall(object, methodName, arguments, interceptor, new Callable() {
+            public Object call() {
+                return adaptee.invokeStaticMethod(object, methodName, arguments);
+            }
+        });
+    }
+
+    /**
+     * Call invokeConstructor on adaptee with logic like in MetaClass unless we have an Interceptor.
+     * With Interceptor the call is nested in its beforeInvoke and afterInvoke methods.
+     * The method call is suppressed if Interceptor.doInvoke() returns false.
+     * See Interceptor for details.
+     */
+    public Object invokeConstructor(final Object[] arguments) {
+        return doCall(theClass, "ctor", arguments, interceptor, new Callable() {
+            public Object call() {
+                return adaptee.invokeConstructor(arguments);
+            }
+        });
+    }
+
+    /**
+     * Interceptors the call to getProperty if a PropertyAccessInterceptor is
+     * available
+     *
+     * @param object   the object to invoke the getter on
+     * @param property the property name
+     * @return the value of the property
+     */
+    public Object getProperty(Class aClass, Object object, String property, boolean b, boolean b1) {
+        if (null == interceptor) {
+            return super.getProperty(aClass, object, property, b, b1);
+        }
+        if (interceptor instanceof PropertyAccessInterceptor) {
+            PropertyAccessInterceptor pae = (PropertyAccessInterceptor) interceptor;
+
+            Object result = pae.beforeGet(object, property);
+            if (interceptor.doInvoke()) {
+                result = super.getProperty(aClass, object, property, b, b1);
+            }
+            return result;
+        }
+        return super.getProperty(aClass, object, property, b, b1);
+    }
+
+    /**
+     * Interceptors the call to a property setter if a PropertyAccessInterceptor
+     * is available
+     *
+     * @param object   The object to invoke the setter on
+     * @param property The property name to set
+     * @param newValue The new value of the property
+     */
+    public void setProperty(Class aClass, Object object, String property, Object newValue, boolean b, boolean b1) {
+        if (null == interceptor) {
+            super.setProperty(aClass, object, property, newValue, b, b1);
+        }
+        if (interceptor instanceof PropertyAccessInterceptor) {
+            PropertyAccessInterceptor pae = (PropertyAccessInterceptor) interceptor;
+
+            pae.beforeSet(object, property, newValue);
+            if (interceptor.doInvoke()) {
+                super.setProperty(aClass, object, property, newValue, b, b1);
+            }
+        } else {
+            super.setProperty(aClass, object, property, newValue, b, b1);
+        }
+    }
+
+    public MetaClass getAdaptee() {
+        return this.adaptee;
+    }
+
+    public void setAdaptee(MetaClass metaClass) {
+        this.adaptee = metaClass;
+    }
+
+    // since Java has no Closures...
+    private interface Callable {
+        Object call();
+    }
+
+    private Object doCall(Object object, String methodName, Object[] arguments, Interceptor interceptor, Callable howToInvoke) {
+        if (null == interceptor) {
+            return howToInvoke.call();
+        }
+        Object result = interceptor.beforeInvoke(object, methodName, arguments);
+        if (interceptor.doInvoke()) {
+            result = howToInvoke.call();
+        }
+        result = interceptor.afterInvoke(object, methodName, arguments, result);
+        return result;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/Range.java b/groovy/src/main/groovy/lang/Range.java
new file mode 100644
index 0000000..97c3941
--- /dev/null
+++ b/groovy/src/main/groovy/lang/Range.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.util.List;
+
+/**
+ * Represents the interface of a Range implementation which includes the
+ * from and to values.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public interface Range extends List {
+    /**
+     * Gets the lower value in the range.
+     *
+     * @return the lower value in the range.
+     */
+    Comparable getFrom();
+
+    /**
+     * Gets the lower value in the range.
+     *
+     * @return the upper value in the range
+     */
+    Comparable getTo();
+
+    /**
+     * Indicates whether this is a reverse range which iterates backwards
+     * starting from the to value and ending on the from value
+     *
+     * @return <code>true</code> if this is a reverse range
+     */
+    boolean isReverse();
+
+    /**
+     * Indicates whether this is a reverse range which iterates backwards
+     * starting from the to value and ending on the from value
+     *
+     * @param o the object to check against the boundaries of the range
+     * @return <code>true</code> if the object is between the from and to values
+     */
+    boolean containsWithinBounds(Object o);
+
+    /**
+     * Steps through the range, calling a closure for each number.
+     *
+     * @param step    the amount by which to step. If negative, steps through the
+     *                range backwards.
+     * @param closure the {@link Closure} to call
+     */
+    void step(int step, Closure closure);
+
+    /**
+     * Forms a list by stepping through the range by the indicated interval.
+     *
+     * @param step the amount by which to step. If negative, steps through the
+     *             range backwards.
+     * @return the list formed by stepping through the range by the indicated
+     *         interval.
+     */
+    List step(int step);
+
+    /**
+     * @return the verbose {@link String} representation of this {@link Range} as would be typed into a console
+     *         to create the {@link Range} instance
+     */
+    String inspect();
+}
diff --git a/groovy/src/main/groovy/lang/ReadOnlyPropertyException.java b/groovy/src/main/groovy/lang/ReadOnlyPropertyException.java
new file mode 100644
index 0000000..c64d6ee
--- /dev/null
+++ b/groovy/src/main/groovy/lang/ReadOnlyPropertyException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+
+/**
+ * This exception is thrown if an attempt is made to set a read only property
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ReadOnlyPropertyException extends MissingPropertyException {
+
+    public ReadOnlyPropertyException(String property, Class type) {
+        super("Cannot set readonly property: " + property + " for class: " + type.getName(), property, type);
+    }
+}
diff --git a/groovy/src/main/groovy/lang/Reference.java b/groovy/src/main/groovy/lang/Reference.java
new file mode 100644
index 0000000..c888522
--- /dev/null
+++ b/groovy/src/main/groovy/lang/Reference.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * Represents a reference to a value
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class Reference extends GroovyObjectSupport {
+
+    private Object value;
+
+    public Reference() {
+    }
+
+    public Reference(Object value) {
+        this.value = value;
+    }
+
+    public Object getProperty(String property) {
+        Object value = get();
+        if (value != null) {
+            return InvokerHelper.getProperty(value, property);
+        }
+        return super.getProperty(property);
+    }
+
+    public void setProperty(String property, Object newValue) {
+        Object value = get();
+        if (value != null) {
+            InvokerHelper.setProperty(value, property, newValue);
+        }
+        else {
+            super.setProperty(property, newValue);
+        }
+    }
+
+    public Object invokeMethod(String name, Object args) {
+        Object value = get();
+        if (value != null) {
+            try {
+                return InvokerHelper.invokeMethod(value, name, args);
+            }
+            catch (Exception e) {
+                return super.invokeMethod(name, args);
+            }
+        }
+        else {
+            return super.invokeMethod(name, args);
+        }
+    }
+
+    public Object get() {
+        return value;
+    }
+
+    public void set(Object value) {
+        this.value = value;
+    }
+}
diff --git a/groovy/src/main/groovy/lang/Script.java b/groovy/src/main/groovy/lang/Script.java
new file mode 100644
index 0000000..a232e58
--- /dev/null
+++ b/groovy/src/main/groovy/lang/Script.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * This object represents a Groovy script
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Guillaume Laforge
+ * @version $Revision$
+ */
+public abstract class Script extends GroovyObjectSupport {
+    private Binding binding;
+
+    protected Script() {
+        this(new Binding());
+    }
+
+    protected Script(Binding binding) {
+        this.binding = binding;
+    }
+
+    public Binding getBinding() {
+        return binding;
+    }
+
+    public void setBinding(Binding binding) {
+        this.binding = binding;
+    }
+
+    public Object getProperty(String property) {
+        try {
+            return binding.getVariable(property);
+        } catch (MissingPropertyException e) {
+            return super.getProperty(property);
+        }
+    }
+
+    public void setProperty(String property, Object newValue) {
+        if ("binding".equals(property))
+            setBinding((Binding) newValue);
+        else if("metaClass".equals(property))
+            setMetaClass((MetaClass)newValue);
+        else
+            binding.setVariable(property, newValue);
+    }
+
+    /**
+     * Invoke a method (or closure in the binding) defined.
+     *
+     * @param name method to call
+     * @param args arguments to pass to the method
+     * @return value
+     */
+    public Object invokeMethod(String name, Object args) {
+        try {
+            return super.invokeMethod(name, args);
+        }
+        // if the method was not found in the current scope (the script's methods)
+        // let's try to see if there's a method closure with the same name in the binding
+        catch (MissingMethodException mme) {
+            try {
+                if (name.equals(mme.getMethod())) {
+                    Object boundClosure = binding.getVariable(name);
+                    if (boundClosure != null && boundClosure instanceof Closure) {
+                        return ((Closure) boundClosure).call((Object[])args);
+                    } else {
+                        throw mme;
+                    }
+                } else {
+                    throw mme;
+                }
+            } catch (MissingPropertyException mpe) {
+                throw mme;
+            }
+        }
+    }
+
+    /**
+     * The main instance method of a script which has variables in scope
+     * as defined by the current {@link Binding} instance.
+     */
+    public abstract Object run();
+
+    // println helper methods
+
+    /**
+     * Prints a newline to the current 'out' variable which should be a PrintWriter
+     * or at least have a println() method defined on it.
+     * If there is no 'out' property then print to standard out.
+     */
+    public void println() {
+        Object object;
+
+        try {
+            object = getProperty("out");
+        } catch (MissingPropertyException e) {
+            System.out.println();
+            return;
+        }
+
+        InvokerHelper.invokeMethod(object, "println", ArgumentListExpression.EMPTY_ARRAY);
+    }
+
+    /**
+     * Prints the value to the current 'out' variable which should be a PrintWriter
+     * or at least have a print() method defined on it.
+     * If there is no 'out' property then print to standard out.
+     */
+    public void print(Object value) {
+        Object object;
+
+        try {
+            object = getProperty("out");
+        } catch (MissingPropertyException e) {
+            DefaultGroovyMethods.print(System.out,value);
+            return;
+        }
+
+        InvokerHelper.invokeMethod(object, "print", new Object[]{value});
+    }
+
+    /**
+     * Prints the value and a newline to the current 'out' variable which should be a PrintWriter
+     * or at least have a println() method defined on it.
+     * If there is no 'out' property then print to standard out.
+     */
+    public void println(Object value) {
+        Object object;
+
+        try {
+            object = getProperty("out");
+        } catch (MissingPropertyException e) {
+            DefaultGroovyMethods.println(System.out,value);
+            return;
+        }
+
+        InvokerHelper.invokeMethod(object, "println", new Object[]{value});
+    }
+
+    /**
+     * A helper method to allow the dynamic evaluation of groovy expressions using this
+     * scripts binding as the variable scope
+     *
+     * @param expression is the Groovy script expression to evaluate
+     */
+    public Object evaluate(String expression) throws CompilationFailedException, IOException {
+        GroovyShell shell = new GroovyShell(binding);
+        return shell.evaluate(expression);
+    }
+
+    /**
+     * A helper method to allow the dynamic evaluation of groovy expressions using this
+     * scripts binding as the variable scope
+     *
+     * @param file is the Groovy script to evaluate
+     */
+    public Object evaluate(File file) throws CompilationFailedException, IOException {
+        GroovyShell shell = new GroovyShell(binding);
+        return shell.evaluate(file);
+    }
+
+    /**
+     * A helper method to allow scripts to be run taking command line arguments
+     */
+    public void run(File file, String[] arguments) throws CompilationFailedException, IOException {
+        GroovyShell shell = new GroovyShell(binding);
+        shell.run(file, arguments);
+    }
+}
diff --git a/groovy/src/main/groovy/lang/Sequence.java b/groovy/src/main/groovy/lang/Sequence.java
new file mode 100644
index 0000000..af0c365
--- /dev/null
+++ b/groovy/src/main/groovy/lang/Sequence.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+/**
+ * Represents a sequence of objects which represents zero or many instances of
+ * of objects of a given type. The type can be ommitted in which case any type of
+ * object can be added.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class Sequence extends ArrayList implements GroovyObject {
+
+    private MetaClass metaClass = InvokerHelper.getMetaClass(this);
+    private Class type;
+    private int hashCode;
+
+    public Sequence() {
+        this(null);
+    }
+
+    public Sequence(Class type) {
+        this.type = type;
+    }
+
+    public Sequence(Class type, List content) {
+        super(content.size());
+        this.type = type;
+        addAll(content);
+    }
+
+    /**
+     * Sets the contents of this sequence to that
+     * of the given collection.
+     */
+    public void set(Collection collection) {
+        checkCollectionType(collection);
+        clear();
+        addAll(collection);
+    }
+    
+    public boolean equals(Object that) {
+        if (that instanceof Sequence) {
+            return equals((Sequence) that);
+        }
+        return false;
+    }
+
+    public boolean equals(Sequence that) {
+        if (size() == that.size()) {
+            for (int i = 0; i < size(); i++) {
+                if (!DefaultTypeTransformation.compareEqual(this.get(i), that.get(i))) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public int hashCode() {
+        if (hashCode == 0) {
+            for (int i = 0; i < size(); i++) {
+                Object value = get(i);
+                int hash = (value != null) ? value.hashCode() : 0xbabe;
+                hashCode ^= hash;
+            }
+            if (hashCode == 0) {
+                hashCode = 0xbabe;
+            }
+        }
+        return hashCode;
+    }
+
+    public int minimumSize() {
+        return 0;
+    }
+
+    /**
+     * @return the type of the elements in the sequence or null if there is no
+     * type constraint on this sequence
+     */
+    public Class type() {
+        return type;
+    }
+    
+    public void add(int index, Object element) {
+        checkType(element);
+        hashCode = 0;
+        super.add(index, element);
+    }
+
+    public boolean add(Object element) {
+        checkType(element);
+        hashCode = 0;
+        return super.add(element);
+    }
+
+    public boolean addAll(Collection c) {
+        checkCollectionType(c);
+        hashCode = 0;
+        return super.addAll(c);
+    }
+
+    public boolean addAll(int index, Collection c) {
+        checkCollectionType(c);
+        hashCode = 0;
+        return super.addAll(index, c);
+    }
+
+    public void clear() {
+        hashCode = 0;
+        super.clear();
+    }
+
+    public Object remove(int index) {
+        hashCode = 0;
+        return super.remove(index);
+    }
+
+    protected void removeRange(int fromIndex, int toIndex) {
+        hashCode = 0;
+        super.removeRange(fromIndex, toIndex);
+    }
+
+    public Object set(int index, Object element) {
+        hashCode = 0;
+        return super.set(index, element);
+    }
+
+    // GroovyObject interface
+    //-------------------------------------------------------------------------
+    public Object invokeMethod(String name, Object args) {
+        try {
+        return getMetaClass().invokeMethod(this, name, args);
+        }
+        catch (MissingMethodException e) {
+            // lets apply the method to each item in the collection
+            List answer = new ArrayList(size());
+            for (Iterator iter = iterator(); iter.hasNext(); ) {
+                Object element = iter.next();
+                Object value = InvokerHelper.invokeMethod(element, name, args);
+                answer.add(value);
+            }
+            return answer;
+        }
+    }
+
+    public Object getProperty(String property) {
+        return getMetaClass().getProperty(this, property);
+    }
+
+    public void setProperty(String property, Object newValue) {
+        getMetaClass().setProperty(this, property, newValue);
+    }
+
+    public MetaClass getMetaClass() {
+        return metaClass;
+    }
+
+    public void setMetaClass(MetaClass metaClass) {
+        this.metaClass = metaClass;
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+    
+    /**
+     * Checks that each member of the given collection are of the correct
+     * type
+     */
+    protected void checkCollectionType(Collection c) {
+        if (type != null) {
+            for (Iterator iter = c.iterator(); iter.hasNext(); ) {
+                Object element = iter.next();
+                checkType(element);
+            }
+        }
+    }
+
+
+    /** 
+     * Checks that the given object instance is of the correct type
+     * otherwise a runtime exception is thrown
+     */
+    protected void checkType(Object object) {
+        if (object == null) {
+            throw new NullPointerException("Sequences cannot contain null, use a List instead");
+        }
+        if (type != null) {
+            if (!type.isInstance(object)) {
+                throw new IllegalArgumentException(
+                    "Invalid type of argument for sequence of type: "
+                        + type.getName()
+                        + " cannot add object: "
+                        + object);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/lang/SpreadListEvaluatingException.java b/groovy/src/main/groovy/lang/SpreadListEvaluatingException.java
new file mode 100644
index 0000000..e6931df
--- /dev/null
+++ b/groovy/src/main/groovy/lang/SpreadListEvaluatingException.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+public class SpreadListEvaluatingException extends GroovyRuntimeException {
+    public SpreadListEvaluatingException(String message) {
+        super(message);
+    }
+}
diff --git a/groovy/src/main/groovy/lang/SpreadMap.java b/groovy/src/main/groovy/lang/SpreadMap.java
new file mode 100644
index 0000000..58f9e15
--- /dev/null
+++ b/groovy/src/main/groovy/lang/SpreadMap.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Iterator;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+/**
+ * Represents a spreadable map which extends java.util.HashMap.
+ * 
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+public class SpreadMap extends HashMap {
+
+    private Map mapData;
+    private int hashCode;
+
+    public SpreadMap(Object[] values) {
+        mapData = new HashMap(values.length / 2);
+        int i = 0;
+        while (i < values.length) {
+           mapData.put(values[i++], values[i++]);
+        }
+    }
+
+    public SpreadMap(Map map) {
+        this.mapData = map;
+    }
+
+    public Object get(Object obj) {
+        return mapData.get(obj);
+    }
+
+    public Object put(Object key, Object value) {
+        throw new RuntimeException("SpreadMap: " + this + " is an immutable map, and so ("
+                                   + key + ": " + value + ") cannot be added.");
+    }
+
+    public Object remove(Object key) {
+        throw new RuntimeException("SpreadMap: " + this + " is an immutable map, and so the key ("
+                                   + key + ") cannot be deleteded.");
+    }
+
+    public void putAll(Map t) {
+        throw new RuntimeException("SpreadMap: " + this + " is an immutable map, and so the map ("
+                                   + t + ") cannot be put in this spreadMap.");
+    }
+
+    public int size() {
+        return mapData.keySet().size();
+    }
+
+    public boolean equals(Object that) {
+        if (that instanceof SpreadMap) {
+            return equals((SpreadMap) that);
+        }
+        return false;
+    }
+
+    public boolean equals(SpreadMap that) {
+        if (that == null) return false;        
+
+        if (size() == that.size()) {
+            SpreadMap other = (SpreadMap) that;
+            Iterator iter = mapData.keySet().iterator();
+            for (; iter.hasNext(); ) {
+                Object key = iter.next();
+                if (! DefaultTypeTransformation.compareEqual(get(key), other.get(key)) ) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+
+    public int hashCode() {
+        if (hashCode == 0) {
+            Iterator iter = mapData.keySet().iterator();
+            for (; iter.hasNext(); ) {
+                Object key = iter.next();
+                int hash = (key != null) ? key.hashCode() : 0xbabe;
+                hashCode ^= hash;
+            }
+        }
+        return hashCode;
+    }
+
+    /**
+     * Returns the string expression of <code>this</code>.
+     *
+     * @return the string expression of <code>this</code>
+     */
+    public String toString() {
+        if (mapData.isEmpty()) {
+            return "*:[:]";
+        }
+        StringBuffer buff = new StringBuffer("*:[");
+        Iterator iter = mapData.keySet().iterator();
+        for (; iter.hasNext(); ) {
+            Object key = iter.next();
+            buff.append(key + ":" + mapData.get(key));
+            if (iter.hasNext())
+                buff.append(", ");
+        }
+        buff.append("]");
+        return buff.toString();
+    }
+}
diff --git a/groovy/src/main/groovy/lang/SpreadMapEvaluatingException.java b/groovy/src/main/groovy/lang/SpreadMapEvaluatingException.java
new file mode 100644
index 0000000..f7299a4
--- /dev/null
+++ b/groovy/src/main/groovy/lang/SpreadMapEvaluatingException.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+public class SpreadMapEvaluatingException extends GroovyRuntimeException {
+    public SpreadMapEvaluatingException(String message) {
+        super(message);
+    }
+}
diff --git a/groovy/src/main/groovy/lang/StringWriterIOException.java b/groovy/src/main/groovy/lang/StringWriterIOException.java
new file mode 100644
index 0000000..e4cfc2d
--- /dev/null
+++ b/groovy/src/main/groovy/lang/StringWriterIOException.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.io.IOException;
+
+/**
+ * An IO exception occurred trying to append to a StringWriter which should never happen.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class StringWriterIOException extends RuntimeException {
+
+    public StringWriterIOException(IOException e) {
+        super(e);
+    }
+
+    public IOException getIOException() {
+        return (IOException) getCause();
+    }
+}
diff --git a/groovy/src/main/groovy/lang/TracingInterceptor.java b/groovy/src/main/groovy/lang/TracingInterceptor.java
new file mode 100644
index 0000000..62ab0ad
--- /dev/null
+++ b/groovy/src/main/groovy/lang/TracingInterceptor.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.lang;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+public class TracingInterceptor implements Interceptor {
+
+    protected Writer writer = new PrintWriter(System.out);
+    private int indent = 0;
+
+    public Writer getWriter() {
+        return writer;
+    }
+
+    public void setWriter(Writer writer) {
+        this.writer = writer;
+    }
+
+    public Object beforeInvoke(Object object, String methodName, Object[] arguments) {
+        write(object, methodName, arguments, "before");
+        indent++ ;
+        return null;
+    }
+
+    public Object afterInvoke(Object object, String methodName, Object[] arguments, Object result) {
+        indent--;
+        write(object, methodName, arguments, "after ");
+        return result;
+    }
+
+    public boolean doInvoke() {
+        return true;
+    }
+    private String indent(){
+        StringBuffer result = new StringBuffer();
+        for (int i=0; i<indent;i++){
+            result.append("  ");
+        }
+        return result.toString();
+    }
+
+    protected void write(Object object, String methodName, Object[] arguments, final String origin) {
+        try {
+            writer.write(indent());
+            writer.write(origin);
+            writer.write(" ");
+            Class theClass = object instanceof Class ? (Class) object: object.getClass();
+            writeInfo(theClass, methodName, arguments);
+            writer.write("\n");
+            writer.flush();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    protected void writeInfo(final Class aClass, String methodName, Object[] arguments) throws IOException {
+        writer.write(aClass.getName());
+        writer.write(".");
+        writer.write(methodName);
+        writer.write("(");
+        for (int i = 0; i < arguments.length; i++) {
+            if (i > 0) writer.write(", ");
+            Object argument = arguments[i];
+            writer.write(argument.getClass().getName());
+        }
+        writer.write(")");
+    }
+}
diff --git a/groovy/src/main/groovy/lang/Tuple.java b/groovy/src/main/groovy/lang/Tuple.java
new file mode 100644
index 0000000..e30e5c5
--- /dev/null
+++ b/groovy/src/main/groovy/lang/Tuple.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.util.AbstractList;
+import java.util.List;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+/**
+ * Represents a list of Integer objects from a specified int up to but not including
+ * a given and to.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class Tuple extends AbstractList {
+
+    private Object[] contents;
+    private int hashCode;
+
+    public Tuple(Object[] contents) {
+        this.contents = contents;
+    }
+
+    public Object get(int index) {
+        return contents[index];
+    }
+
+    public int size() {
+        return contents.length;
+    }
+
+    public boolean equals(Object that) {
+        if (that instanceof Tuple) {
+            return equals((Tuple) that);
+        }
+        return false;
+    }
+
+    public boolean equals(Tuple that) {
+        if (contents.length == that.contents.length) {
+            for (int i = 0; i < contents.length; i++) {
+                if (! DefaultTypeTransformation.compareEqual(this.contents[i], that.contents[i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+
+    public int hashCode() {
+        if (hashCode == 0) {
+            for (int i = 0; i < contents.length; i++ ) {
+                Object value = contents[i];
+                int hash = (value != null) ? value.hashCode() : 0xbabe;
+                hashCode ^= hash;
+            }
+            if (hashCode == 0) {
+                hashCode = 0xbabe;
+            }
+        }
+        return hashCode;
+    }
+
+    public List subList(int fromIndex, int toIndex) {
+        int size = toIndex - fromIndex;
+        Object[] newContent = new Object[size];
+        System.arraycopy(contents, fromIndex, newContent, 0, size);
+        return new Tuple(newContent);
+    }
+}
diff --git a/groovy/src/main/groovy/lang/Writable.java b/groovy/src/main/groovy/lang/Writable.java
new file mode 100644
index 0000000..9a7c0db
--- /dev/null
+++ b/groovy/src/main/groovy/lang/Writable.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+import java.io.IOException;
+import java.io.Writer;
+
+
+/**
+ * Represents an object which is capable of writing itself to a text stream
+ * in a more efficient format than just creating a toString() representation
+ * of itself. This mechanism is particularly useful for templates and such like.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public interface Writable {
+
+    /**
+     * writes this object to the given stream
+     */
+    Writer writeTo(Writer out) throws IOException;
+        
+}
diff --git a/groovy/src/main/groovy/lang/package.html b/groovy/src/main/groovy/lang/package.html
new file mode 100644
index 0000000..688fc33
--- /dev/null
+++ b/groovy/src/main/groovy/lang/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.lang.*</title>
+  </head>
+  <body>
+    <p>Core Groovy language classes for implementing data structures, closures, metadata and so forth.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/mock/ClosureConstraintMatcher.java b/groovy/src/main/groovy/mock/ClosureConstraintMatcher.java
new file mode 100644
index 0000000..2192ef9
--- /dev/null
+++ b/groovy/src/main/groovy/mock/ClosureConstraintMatcher.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.mock;
+
+import groovy.lang.Closure;
+import com.mockobjects.constraint.Constraint;
+
+/**
+ * 
+ * @author Joe Walnes
+ * @author Chris Stevenson
+ * @version $Revision$
+ */
+public class ClosureConstraintMatcher implements Constraint {
+    private final Closure closure;
+    private String message = "closure";
+
+    public ClosureConstraintMatcher(Closure closure) {
+        this.closure = closure;
+    }
+
+    public boolean eval(Object object) {
+        try {
+            closure.call((Object[])object);
+            return true;
+        }
+        catch (AssertionError e) {
+            message = e.getMessage();
+            return false;
+        }
+    }
+
+    public String toString() {
+        return message;
+    }
+
+}
diff --git a/groovy/src/main/groovy/mock/GroovyMock.java b/groovy/src/main/groovy/mock/GroovyMock.java
new file mode 100644
index 0000000..c25fc7d
--- /dev/null
+++ b/groovy/src/main/groovy/mock/GroovyMock.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.mock;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+
+import com.mockobjects.Verifiable;
+import com.mockobjects.dynamic.*;
+
+/**
+ * 
+ * @author Joe Walnes
+ * @author Chris Stevenson
+ * @version $Revision$
+ */
+public class GroovyMock extends GroovyObjectSupport implements Verifiable {
+
+    private CallBag calls = new CallBag();
+    private CallFactory callFactory = new DefaultCallFactory();
+    private Mock mock = new Mock(I.class);
+
+    interface I {
+    }
+
+    private GroovyObject instance = new GroovyObjectSupport() {
+        public Object invokeMethod(String name, Object args) {
+            return callMethod(name, args);
+        }
+    };
+
+    public Object invokeMethod(String name, Object args) {
+        if (name.equals("verify")) {
+            verify();
+        }
+        else {
+            expectMethod(name, args);
+        }
+        return null;
+    }
+
+    public GroovyObject getInstance() {
+        return instance;
+    }
+
+    public static GroovyMock newInstance() {
+        return new GroovyMock();
+    }
+
+    private void expectMethod(String name, Object args) {
+        ConstraintMatcher constraintMatcher = createMatcher(args);
+        calls.addExpect(
+            callFactory.createCallExpectation(
+                callFactory.createCallSignature(name, constraintMatcher, callFactory.createVoidStub())));
+    }
+
+    private ConstraintMatcher createMatcher(Object args) {
+        if(args.getClass().isArray()) {
+            Object argArray[] = (Object[]) args;
+            if (argArray[0] instanceof Closure) {
+                Closure closure = (Closure) argArray[0];
+                return C.args(new ClosureConstraintMatcher(closure));
+            }
+        }
+        return C.args(C.eq(args));
+    }
+
+    private Object callMethod(String name, Object args) {
+        try {
+            return calls.call(mock, name, new Object[] { args });
+        }
+        catch (Throwable throwable) {
+            throw new RuntimeException(throwable);
+        }
+    }
+
+    public void verify() {
+        calls.verify();
+    }
+
+}
diff --git a/groovy/src/main/groovy/mock/interceptor/Demand.groovy b/groovy/src/main/groovy/mock/interceptor/Demand.groovy
new file mode 100644
index 0000000..172ebc4
--- /dev/null
+++ b/groovy/src/main/groovy/mock/interceptor/Demand.groovy
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.mock.interceptor
+
+import junit.framework.AssertionFailedError
+
+/**
+    The object that registers method calls on it for the use with Mocks and Stubs.
+    For each call a CallSpec object is added to the recorded list.
+    @author Dierk Koenig
+*/
+
+class Demand {
+
+    def List recorded = []
+
+    Object invokeMethod(String methodName, Object args) {
+        def range = 1..1
+        if (args[0] instanceof IntRange) {
+            range = args[0]
+            if (range.reverse) throw new IllegalArgumentException('Reverse ranges not supported.')
+        }
+        if (args[-1] instanceof Closure) {
+            recorded << new CallSpec(name:methodName, behavior:args[-1], range:range)
+        }
+    }
+
+    def verify(List calls) {
+        for (i in 0 ..< recorded.size()) {
+            def call = recorded[i]
+            def callCounter = calls[i] ? calls[i] : 0
+            if (! call.range.contains( callCounter ) ) {
+                def msg = "verify[$i]: expected ${call.range.toString()} call(s) to '${call.name}' but was "
+                throw new AssertionFailedError(msg + "called $callCounter time(s).")
+            }
+        }
+    }
+
+}
+
+class CallSpec {
+    String  name
+    Closure behavior
+    Range   range
+}
diff --git a/groovy/src/main/groovy/mock/interceptor/LooseExpectation.groovy b/groovy/src/main/groovy/mock/interceptor/LooseExpectation.groovy
new file mode 100644
index 0000000..5f7e32b
--- /dev/null
+++ b/groovy/src/main/groovy/mock/interceptor/LooseExpectation.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.mock.interceptor
+
+import junit.framework.AssertionFailedError
+
+/**
+    Expects demanded call cardinalities to match demanded ranges.
+    The calls are allowed to be out of the recorded sequence.
+    If a method is demanded multiple times, the ranges are filled by order of recording.
+    @See StrictExpectation
+    @author Dierk Koenig
+*/
+
+class LooseExpectation {
+    Demand fDemand  = null
+    List fCalls      = []
+
+    LooseExpectation(Demand demand) {
+        fDemand = demand
+    }
+
+    /**
+        Match the requested method name against eligible demands.
+        Fail early if no match possible.
+        Return the demand's behavior closure on match.
+    */
+    Closure match(String name) {
+        def callIndex = 0
+        // find first eligible callSpec
+        while (! isEligible(name, callIndex) ) callIndex++
+
+        // register the call
+        fCalls[callIndex] += 1
+
+        return fDemand.recorded[callIndex].behavior
+    }
+
+    boolean isEligible(String name, int i) {
+        def calls = fDemand.recorded
+        if (i >= calls.size())  {
+            throw new AssertionFailedError("No more calls to '$name' expected at this point. End of demands.")
+        }
+        if (calls[i].name != name)              return false
+        if (null == fCalls[i])                  fCalls[i] = 0
+        if (fCalls[i] >= calls[i].range.to)     return false
+        return true
+    }
+
+    /** verify all calls are in expected range */ 
+    void verify() {
+    	fDemand.verify(fCalls)
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/mock/interceptor/MockFor.groovy b/groovy/src/main/groovy/mock/interceptor/MockFor.groovy
new file mode 100644
index 0000000..814430c
--- /dev/null
+++ b/groovy/src/main/groovy/mock/interceptor/MockFor.groovy
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.mock.interceptor
+
+import java.lang.reflect.Modifier
+
+/**
+    Facade over the Mocking details.
+    A Mock's expectation is always sequence dependent and it's use always ends with a verify().
+    See also StubFor.
+    @author Dierk Koenig
+    @author Paul King
+*/
+
+class MockFor {
+
+    MockProxyMetaClass proxy
+    Demand demand
+    def expect
+    Map instanceExpectations = [:]
+    Class clazz
+
+    MockFor(Class clazz) {
+        this.clazz = clazz
+        proxy = MockProxyMetaClass.make(clazz)
+        demand = new Demand()
+        expect = new StrictExpectation(demand)
+        proxy.interceptor = new MockInterceptor(expectation: expect)
+    }
+
+    void use(Closure closure) {
+        proxy.use closure
+        expect.verify()
+    }
+
+    void use(GroovyObject obj, Closure closure) {
+        proxy.use obj, closure
+        expect.verify()
+    }
+
+    void verify(GroovyObject obj) {
+        instanceExpectations[obj].verify()
+    }
+
+    Object proxyInstance() {
+        proxyInstance(null)
+    }
+
+    Object proxyInstance(args) {
+        def instance = getInstance(clazz, args)
+        def thisproxy = MockProxyMetaClass.make(clazz)
+        def thisdemand = new Demand(recorded: new ArrayList(demand.recorded))
+        def thisexpect = new StrictExpectation(thisdemand)
+        thisproxy.interceptor = new MockInterceptor(expectation: thisexpect)
+        instance.metaClass = thisproxy
+        instanceExpectations[instance] = thisexpect
+        return instance
+    }
+
+    Object proxyDelegateInstance() {
+        proxyDelegateInstance(null)
+    }
+
+    Object proxyDelegateInstance(args) {
+        def instance = getInstance(clazz, args)
+        def thisproxy = MockProxyMetaClass.make(clazz)
+        def thisdemand = new Demand(recorded: new ArrayList(demand.recorded))
+        def thisexpect = new StrictExpectation(thisdemand)
+        thisproxy.interceptor = new MockInterceptor(expectation: thisexpect)
+        instance.metaClass = thisproxy
+        def wrapped = null
+        if (clazz.isInterface()) {
+            wrapped = ProxyGenerator.instantiateDelegate([clazz], instance)
+        } else {
+            wrapped = ProxyGenerator.instantiateDelegate(instance)
+        }
+        instanceExpectations[wrapped] = thisexpect
+        return wrapped
+    }
+
+    private getInstance(Class clazz, args) {
+        def instance = null
+        if (clazz.isInterface()) {
+            instance = ProxyGenerator.instantiateAggregateFromInterface(clazz)
+        } else if (Modifier.isAbstract(clazz.modifiers)) {
+            instance = ProxyGenerator.instantiateAggregateFromBaseClass(clazz, args)
+        } else if (args != null) {
+            if (clazz instanceof GroovyObject) {
+                instance = clazz.newInstance(args)
+            } else {
+                instance = ProxyGenerator.instantiateDelegate(clazz.newInstance(args))
+            }
+        } else {
+            if (clazz instanceof GroovyObject) {
+                instance = clazz.newInstance()
+            } else {
+                instance = ProxyGenerator.instantiateDelegate(clazz.newInstance())
+            }
+        }
+        return instance
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/mock/interceptor/MockInterceptor.groovy b/groovy/src/main/groovy/mock/interceptor/MockInterceptor.groovy
new file mode 100644
index 0000000..8f6b263
--- /dev/null
+++ b/groovy/src/main/groovy/mock/interceptor/MockInterceptor.groovy
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.mock.interceptor
+
+/**
+    Intercepting calls to the collaborating object and notify the expectation object.
+    @author Dierk Koenig
+*/
+
+class MockInterceptor implements PropertyAccessInterceptor {
+
+    def expectation = null
+
+    Object beforeInvoke(Object object, String methodName, Object[] arguments) {
+        if (!expectation) throw new IllegalStateException("Property 'expectation' must be set before use.")
+        return expectation.match(methodName).call(arguments)
+    }
+
+    Object beforeGet(Object object, String property) {
+        if (!expectation) throw new IllegalStateException("Property 'expectation' must be set before use.")
+        String name = "get${property[0].toUpperCase()}${property[1..-1]}"
+        return expectation.match(name).call()                    
+    }
+
+    void beforeSet(Object object, String property, Object newValue) {
+        if (!expectation) throw new IllegalStateException("Property 'expectation' must be set before use.")
+        String name = "set${property[0].toUpperCase()}${property[1..-1]}"
+        expectation.match(name).call(newValue)
+    }
+
+    Object afterInvoke(Object object, String methodName, Object[] arguments, Object result) {
+        return null // never used
+    }
+
+    boolean doInvoke() {
+        return false // future versions may allow collaborator method calls depending on state
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/mock/interceptor/MockProxyMetaClass.java b/groovy/src/main/groovy/mock/interceptor/MockProxyMetaClass.java
new file mode 100644
index 0000000..345d439
--- /dev/null
+++ b/groovy/src/main/groovy/mock/interceptor/MockProxyMetaClass.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.mock.interceptor;
+
+import groovy.lang.*;
+
+import java.beans.IntrospectionException;
+
+/**
+ * The ProxyMetaClass for the MockInterceptor.
+ * Instance and class methods are intercepted, but constructors are not to allow mocking of aggregated objects.
+ * @author Dierk Koenig
+ */
+
+public class MockProxyMetaClass extends ProxyMetaClass {
+
+    /**
+     * @param adaptee the MetaClass to decorate with interceptability
+     */
+    public MockProxyMetaClass(MetaClassRegistry registry, Class theClass, MetaClass adaptee) throws IntrospectionException {
+        super(registry, theClass, adaptee);
+    }
+
+    /**
+     * convenience factory method for the most usual case.
+     */
+    public static MockProxyMetaClass make(Class theClass) throws IntrospectionException {
+        MetaClassRegistry metaRegistry = GroovySystem.getMetaClassRegistry();
+        MetaClass meta = metaRegistry.getMetaClass(theClass);
+        return new MockProxyMetaClass(metaRegistry, theClass, meta);
+    }
+
+
+    public Object invokeMethod(final Object object, final String methodName, final Object[] arguments) {
+        if (null == interceptor) {
+            throw new RuntimeException("cannot invoke without interceptor");
+        }
+        return interceptor.beforeInvoke(object, methodName, arguments);
+    }
+
+    public Object invokeStaticMethod(final Object object, final String methodName, final Object[] arguments) {
+        if (null == interceptor) {
+            throw new RuntimeException("cannot invoke without interceptor");
+        }
+        return interceptor.beforeInvoke(object, methodName, arguments);
+    }
+
+    public Object getProperty(Class aClass, Object object, String property, boolean b, boolean b1) {
+        if (null == interceptor) {
+            throw new RuntimeException("cannot invoke without interceptor");
+        }
+        if(interceptor instanceof PropertyAccessInterceptor) {
+            return ((PropertyAccessInterceptor)interceptor).beforeGet(object,property);
+        }
+        else {
+            return super.getProperty(aClass,object,property,b,b);
+        }
+
+
+    }
+
+    public void setProperty(Class aClass, Object object, String property, Object newValue, boolean b, boolean b1) {
+        if (null == interceptor) {
+            throw new RuntimeException("cannot invoke without interceptor");
+        }
+
+        if(interceptor instanceof PropertyAccessInterceptor) {
+            ((PropertyAccessInterceptor)interceptor).beforeSet(object,property, newValue);
+        }
+        else {
+            super.setProperty(aClass,object,property,newValue,b,b);
+        }
+
+    }
+
+    /**
+     * Unlike general impl in superclass, ctors are not intercepted but relayed
+     */
+    public Object invokeConstructor(final Object[] arguments) {
+        return adaptee.invokeConstructor(arguments);
+    }
+    
+}
diff --git a/groovy/src/main/groovy/mock/interceptor/StrictExpectation.groovy b/groovy/src/main/groovy/mock/interceptor/StrictExpectation.groovy
new file mode 100644
index 0000000..81a50a7
--- /dev/null
+++ b/groovy/src/main/groovy/mock/interceptor/StrictExpectation.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.mock.interceptor
+
+import junit.framework.AssertionFailedError
+
+/**
+    Expects demanded call cardinalities to match demanded ranges in the sequence of recording.
+    @See LooseExpectation
+    @author Dierk Koenig
+*/
+
+class StrictExpectation {
+    Demand fDemand  = null
+    int fCallSpecIdx = 0
+    List fCalls      = []
+
+    StrictExpectation(Demand demand) {
+        fDemand = demand
+    }
+
+    /**
+        Match the requested method name against eligible demands.
+        Fail early if no match possible.
+        Return the demand's behavior closure on match.
+    */
+    Closure match(String name) {
+        if (!fCalls[fCallSpecIdx]) fCalls[fCallSpecIdx] = 0
+
+        if (fCallSpecIdx >= fDemand.recorded.size()) {
+            throw new AssertionFailedError("No more calls to '$name' expected at this point. End of demands.")
+        }
+
+        def call = fDemand.recorded[fCallSpecIdx]
+        if (name != call.name) {                             // if name does not match...
+            def open = call.range.from - fCalls[fCallSpecIdx]
+            if ( open > 0) {                                 // ... if we haven't reached the minimum, yet -> Exception
+                throw new AssertionFailedError("No call to '$name' expected at this point. "+
+                "Still $open call(s) to '${call.name}' expected.")
+            } else {                                         // ... proceed finding
+                fCallSpecIdx++
+                return match(name)
+            }
+        }
+
+        // register the call
+        fCalls[fCallSpecIdx] += 1
+
+        // store the behavior for returning
+        def result = call.behavior
+
+        // proceed to next callSpec if we need to
+        if (fCalls[fCallSpecIdx] >= call.range.to ) fCallSpecIdx++
+
+        return result
+    }
+
+    /** verify all calls are in expected range */
+    void verify() {
+        fDemand.verify(fCalls)
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/mock/interceptor/StubFor.groovy b/groovy/src/main/groovy/mock/interceptor/StubFor.groovy
new file mode 100644
index 0000000..f24143e
--- /dev/null
+++ b/groovy/src/main/groovy/mock/interceptor/StubFor.groovy
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.mock.interceptor
+
+import java.lang.reflect.Modifier
+
+/**
+    Facade over the Stubbing details.
+    A Stub's expectation is sequence independent and use of verify() is left to the user.
+    @See MockFor.
+    @author Dierk Koenig
+    @author Paul King
+*/
+
+class StubFor {
+
+    MockProxyMetaClass proxy
+    Demand demand
+    def expect
+    Map instanceExpectations = [:]
+    Class clazz
+
+    StubFor(Class clazz) {
+        this.clazz = clazz
+        proxy = MockProxyMetaClass.make(clazz)
+        demand = new Demand()
+        expect = new LooseExpectation(demand)
+        proxy.interceptor = new MockInterceptor(expectation: expect)
+    }
+
+    void use(Closure closure) {
+        proxy.use closure
+    }
+
+    void use(GroovyObject obj, Closure closure) {
+        proxy.use obj, closure
+    }
+
+    // TODO: remove dup with MockFor
+    void verify(GroovyObject obj) {
+        instanceExpectations[obj].verify()
+    }
+
+    Object proxyInstance() {
+        proxyInstance(null)
+    }
+
+    Object proxyInstance(args) {
+        def instance = getInstance(clazz, args)
+        def thisproxy = MockProxyMetaClass.make(clazz)
+        def thisdemand = new Demand(recorded: new ArrayList(demand.recorded))
+        def thisexpect = new LooseExpectation(thisdemand)
+        thisproxy.interceptor = new MockInterceptor(expectation: thisexpect)
+        instance.metaClass = thisproxy
+        instanceExpectations[instance] = thisexpect
+        return instance
+    }
+
+    Object proxyDelegateInstance() {
+        proxyDelegateInstance(null)
+    }
+
+    Object proxyDelegateInstance(args) {
+        def instance = getInstance(clazz, args)
+        def thisproxy = MockProxyMetaClass.make(clazz)
+        def thisdemand = new Demand(recorded: new ArrayList(demand.recorded))
+        def thisexpect = new LooseExpectation(thisdemand)
+        thisproxy.interceptor = new MockInterceptor(expectation: thisexpect)
+        instance.metaClass = thisproxy
+        def wrapped = null
+        if (clazz.isInterface()) {
+            wrapped = ProxyGenerator.instantiateDelegate([clazz], instance)
+        } else {
+            wrapped = ProxyGenerator.instantiateDelegate(instance)
+        }
+        instanceExpectations[wrapped] = thisexpect
+        return wrapped
+    }
+
+    private getInstance(Class clazz, args) {
+        def instance = null
+        if (clazz.isInterface()) {
+            instance = ProxyGenerator.instantiateAggregateFromInterface(clazz)
+        } else if (Modifier.isAbstract(clazz.modifiers)) {
+            instance = ProxyGenerator.instantiateAggregateFromBaseClass(clazz, args)
+        } else if (args != null) {
+            if (clazz instanceof GroovyObject) {
+                instance = clazz.newInstance(args)
+            } else {
+                instance = ProxyGenerator.instantiateDelegate(clazz.newInstance(args))
+            }
+        } else {
+            if (clazz instanceof GroovyObject) {
+                instance = clazz.newInstance()
+            } else {
+                instance = ProxyGenerator.instantiateDelegate(clazz.newInstance())
+            }
+        }
+        return instance
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/mock/interceptor/package.html b/groovy/src/main/groovy/mock/interceptor/package.html
new file mode 100644
index 0000000..9218ed8
--- /dev/null
+++ b/groovy/src/main/groovy/mock/interceptor/package.html
@@ -0,0 +1,55 @@
+<html>
+  <head>
+    <title>package groovy.mock.interceptor</title>
+  </head>
+  <body>
+    <p>The groovy.mock.interceptor is an all-groovy mock testing library.</p>
+    <p>Terms:</p>
+    <dl>
+        <dt>Callaborator</dt>
+        <dd>An ordinary Groovy or Java class that's instance or class methods
+        are to be called. Calling them can be time consuming or produce side effects that
+        are unwanted when testing (e.g. database operations). </dd>
+
+        <dt>Caller</dt>
+        <dd>A Groovy Object that calls methods on the Collaborator, i.e.
+        collaborates with it.</dd>
+
+        <dt>Mock</dt>
+        <dd>An object that can be used to augment the Collaborator.
+        Method calls to the Collaborator will be handled by the Mock, showing a <em>demanded</em>
+        <em>behavior</em>. Method calls are <em>expected</em> to occur <em>strictly</em> in the <em>demanded</em>
+        sequence with a given <em>range</em> of cardinality. The <em>use</em> of a Mock implicitely
+        ends with <em>verifying</em> the <em>expectations</em>.
+        </dd>
+
+        <dt>Stub</dt>
+        <dd>Much like a Mock but the <em>expectation</em> about sequences of method calls on the Collaborator is
+        <em>loose</em>, i.e. calls may occur out of the demanded order as long as the <em>ranges</em>
+        of cardinality are met. The <em>use</em> of a Stub does <em>not</em> end with an implict
+        <em>verification</em> since the stubbing effect is typically asserted on the Caller.
+        An explicit call to <em>verify</em> can be issued to assert all demanded method call
+        have been effected with the specified cardinality.</dd>
+    </dl>
+
+    <p>Features:</p>
+    <ul>
+        <li>typical mock style of <em>failing early</em></li>
+        <li>mocks instance and class methods</li>
+        <li>mocks final methods and final Collaborators</li>
+        <li>mocks Groovy and Java Collaborators (but Caller must be groovy)</li>
+        <li>can mock all objects of a given class (or a single Groovy object)</li>
+        <li>mocks even if Collaborator cannot be injected into the Caller</li>
+        <li>mocks even if Collaborator is not accesible on the Caller (no getter)</li>
+        <li>demanded calls specified via recording calls on the Demand object (EasyMock style).</li>
+        <li>cardinality specified as Ranges, default is 1..1; 'optional' can be achieved with 0..1</li>
+        <li>behavior specified via Closures, allowing static or calculated return values, throwing exceptions,
+            asserting argument values, etc. (even tricky sequence constraints
+            by sharing state in the testMethod scope between the behavior Closures)</li>
+        <li>matching parameter list specified via Closure's parameter list, supporting
+            typed or untyped params, default params, and varargs.</li>
+        <li>not dependent on any external mock library</li>
+    </ul>
+   <p>For an extensive list of usages see the unit tests in this package.</p>
+  </body>
+</html>
\ No newline at end of file
diff --git a/groovy/src/main/groovy/mock/package.html b/groovy/src/main/groovy/mock/package.html
new file mode 100644
index 0000000..db2799a
--- /dev/null
+++ b/groovy/src/main/groovy/mock/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.mock.*</title>
+  </head>
+  <body>
+    <p>GroovyMock is a mock testing library for Groovy objects.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/model/ClosureModel.java b/groovy/src/main/groovy/model/ClosureModel.java
new file mode 100644
index 0000000..e35a642
--- /dev/null
+++ b/groovy/src/main/groovy/model/ClosureModel.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.model;
+
+import groovy.lang.Closure;
+
+/**
+ * Represents a value model using a closure to extract
+ * the value from some source model and an optional write closure
+ * for updating the value.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ClosureModel implements ValueModel, NestedValueModel {
+
+    private final ValueModel sourceModel;
+    private final Closure readClosure;
+    private final Closure writeClosure;
+    private final Class type;
+
+    public ClosureModel(ValueModel sourceModel, Closure readClosure) {
+        this(sourceModel, readClosure, null);
+    }
+
+    public ClosureModel(ValueModel sourceModel, Closure readClosure, Closure writeClosure) {
+        this(sourceModel, readClosure, writeClosure, Object.class);
+    }
+
+    public ClosureModel(ValueModel sourceModel, Closure readClosure, Closure writeClosure, Class type) {
+        this.sourceModel = sourceModel;
+        this.readClosure = readClosure;
+        this.writeClosure = writeClosure;
+        this.type = type;
+    }
+
+    public ValueModel getSourceModel() {
+        return sourceModel;
+    }
+
+    public Object getValue() {
+        Object source = sourceModel.getValue();
+        if (source != null) {
+            return readClosure.call(source);
+        }
+        return null;
+    }
+
+    public void setValue(Object value) {
+        if (writeClosure != null) {
+            Object source = sourceModel.getValue();
+            if (source != null) {
+                writeClosure.call(new Object[] { source, value });
+            }
+        }
+    }
+
+    public Class getType() {
+        return type;
+    }
+
+    public boolean isEditable() {
+        return writeClosure != null;
+    }
+}
diff --git a/groovy/src/main/groovy/model/DefaultTableColumn.java b/groovy/src/main/groovy/model/DefaultTableColumn.java
new file mode 100644
index 0000000..0d13cd4
--- /dev/null
+++ b/groovy/src/main/groovy/model/DefaultTableColumn.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.model;
+
+import javax.swing.table.TableColumn;
+
+/** 
+ * Represents a column using a ValueModel to extract the value.
+ *
+ * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
+ * @version $Revision$
+ */
+public class DefaultTableColumn extends TableColumn {
+
+    private ValueModel valueModel;    
+    
+    public DefaultTableColumn(ValueModel valueModel) {
+        this.valueModel = valueModel;
+    }
+
+    public DefaultTableColumn(Object header, ValueModel valueModel) {
+        this(valueModel);
+        setHeaderValue(header);
+    }
+
+    public String toString() {
+        return super.toString() + "[header:" + getHeaderValue() + " valueModel:" + valueModel + "]";
+    }
+    
+    /**
+     * Evaluates the value of a cell
+     *
+     * @return the value
+     * @param row the row of interest
+     * @param rowIndex the index of the row of interest
+     * @param columnIndex the column of interest
+     */    
+    public Object getValue(Object row, int rowIndex, int columnIndex) {
+        if (valueModel instanceof NestedValueModel) {
+            NestedValueModel nestedModel = (NestedValueModel) valueModel;
+            nestedModel.getSourceModel().setValue(row);
+        }
+        return valueModel.getValue();
+    }
+
+    public void setValue(Object row, Object value, int rowIndex, int columnIndex) {
+        if (valueModel instanceof NestedValueModel) {
+            NestedValueModel nestedModel = (NestedValueModel) valueModel;
+            nestedModel.getSourceModel().setValue(row);
+        }
+        valueModel.setValue(value);
+    }
+
+    /**
+     * @return the column type
+     */
+    public Class getType() {
+        return valueModel.getType();
+    }
+
+    public ValueModel getValueModel() {
+        return valueModel;
+    }
+
+}
diff --git a/groovy/src/main/groovy/model/DefaultTableModel.java b/groovy/src/main/groovy/model/DefaultTableModel.java
new file mode 100644
index 0000000..7e0c913
--- /dev/null
+++ b/groovy/src/main/groovy/model/DefaultTableModel.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.model;
+
+import groovy.lang.Closure;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import javax.swing.table.AbstractTableModel;
+import javax.swing.table.DefaultTableColumnModel;
+import javax.swing.table.TableColumnModel;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A default table model made up of PropertyModels on a Value model.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DefaultTableModel extends AbstractTableModel {
+
+    private ValueModel rowModel;
+    private ValueModel rowsModel;
+    private MyTableColumnModel columnModel = new MyTableColumnModel();
+
+    public DefaultTableModel(ValueModel rowsModel) {
+        this(rowsModel, new ValueHolder());
+    }
+    
+    public DefaultTableModel(ValueModel rowsModel, ValueModel rowModel) {
+        this.rowModel = rowModel;
+        this.rowsModel = rowsModel;
+    }
+    
+    /**
+     * @return the column definitions.
+     */
+    public List getColumnList() {
+        return columnModel.getColumnList();
+    }
+
+    public TableColumnModel getColumnModel() {
+        return columnModel;
+    }
+    
+    /**
+     * Adds a property model column to the table
+     */
+    public DefaultTableColumn addPropertyColumn(Object headerValue, String property, Class type) {
+        return addColumn(headerValue, new PropertyModel(rowModel, property, type));
+    }
+    
+    /**
+     * Adds a property model column to the table
+     */
+    public DefaultTableColumn addPropertyColumn(Object headerValue, String property, Class type, boolean editable) {
+        return addColumn(headerValue, new PropertyModel(rowModel, property, type, editable));
+    }
+    
+    /**
+     * Adds a closure based column to the table
+     */
+    public DefaultTableColumn addClosureColumn(Object headerValue, Closure readClosure, Closure writeClosure, Class type) {
+        return addColumn(headerValue, new ClosureModel(rowModel, readClosure, writeClosure, type));
+    }
+    
+    public DefaultTableColumn addColumn(Object headerValue, ValueModel columnValueModel) {
+        DefaultTableColumn answer = new DefaultTableColumn(headerValue, columnValueModel);
+        addColumn(answer);
+        return answer;
+    }
+    
+    /**
+     * Adds a new column definition to the table
+     */
+    public void addColumn(DefaultTableColumn column) {
+        column.setModelIndex(columnModel.getColumnCount());
+        columnModel.addColumn(column);
+    }
+    
+    /**
+     * Removes a column definition from the table
+     */
+    public void removeColumn(DefaultTableColumn column) {
+        columnModel.removeColumn(column);
+    }
+    
+    public int getRowCount() {
+        return getRows().size();
+    }
+
+    public int getColumnCount() {
+        return columnModel.getColumnCount();
+    }
+    
+    public String getColumnName(int columnIndex) {
+        String answer = null;
+        if (columnIndex < 0 || columnIndex >= columnModel.getColumnCount()) {
+            return answer;
+        }
+        Object value = columnModel.getColumn(columnIndex).getHeaderValue();
+        if (value != null) {
+            return value.toString();
+        }
+        return answer;
+    }
+
+    public Class getColumnClass(int columnIndex) {
+        return getColumnModel(columnIndex).getType();
+    }
+
+    public boolean isCellEditable(int rowIndex, int columnIndex) {
+        return getColumnModel(columnIndex).isEditable();
+    }
+
+    public Object getValueAt(int rowIndex, int columnIndex) {
+        List rows = getRows();
+        Object answer = null;
+        if (rowIndex < 0 || rowIndex >= rows.size()) {
+            return answer;
+        }
+        if (columnIndex < 0 || columnIndex >= columnModel.getColumnCount()) {
+            return answer;
+        }
+        Object row = getRows().get(rowIndex);
+        rowModel.setValue(row);
+        DefaultTableColumn column = (DefaultTableColumn) columnModel.getColumn(columnIndex);
+        if (row == null || column == null) {
+            return answer;
+        }
+        return column.getValue(row, rowIndex, columnIndex);
+    }
+
+    public void setValueAt(Object value, int rowIndex, int columnIndex) {
+        List rows = getRows();
+        if (rowIndex < 0 || rowIndex >= rows.size()) {
+            return;
+        }
+        if (columnIndex < 0 || columnIndex >= columnModel.getColumnCount()) {
+            return;
+        }
+        Object row = getRows().get(rowIndex);
+        rowModel.setValue(row);
+        DefaultTableColumn column = (DefaultTableColumn) columnModel.getColumn(columnIndex);
+        if (row == null || column == null) {
+            return;
+        }
+        column.setValue(row, value, rowIndex, columnIndex);
+    }
+
+    protected ValueModel getColumnModel(int columnIndex) {
+        DefaultTableColumn column = (DefaultTableColumn) columnModel.getColumn(columnIndex);
+        return column.getValueModel();
+    }
+
+    protected List getRows() {
+        Object value = rowsModel.getValue();
+        if (value == null) {
+            return Collections.EMPTY_LIST;
+        }
+        return InvokerHelper.asList(value);
+    }
+
+    protected static class MyTableColumnModel extends DefaultTableColumnModel {
+        public List getColumnList() {
+            return tableColumns;
+        }
+    }
+    
+    public ValueModel getRowModel() {
+        return rowModel;
+    }
+
+    public ValueModel getRowsModel() {
+        return rowsModel;
+    }
+
+}
diff --git a/groovy/src/main/groovy/model/FormModel.java b/groovy/src/main/groovy/model/FormModel.java
new file mode 100644
index 0000000..05c88ec
--- /dev/null
+++ b/groovy/src/main/groovy/model/FormModel.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.model;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Represents a number of field models which can be ValueModel, 
+ * PropertyModel, TableModel, TreeModel or nested FormModel instances
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class FormModel {
+    private Map fieldModels;
+
+    public FormModel() {
+        this(new HashMap());
+    }
+    
+    public FormModel(Map fieldModels) {
+        this.fieldModels = fieldModels;
+    }
+
+    public void addModel(String name, Object model) {
+        fieldModels.put(name, model);
+    }
+    
+    public Object getModel(String name) {
+        return fieldModels.get(name);
+    }
+}
diff --git a/groovy/src/main/groovy/model/NestedValueModel.java b/groovy/src/main/groovy/model/NestedValueModel.java
new file mode 100644
index 0000000..f16ec05
--- /dev/null
+++ b/groovy/src/main/groovy/model/NestedValueModel.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.model;
+
+/**
+ * Represents a nested value model such as a PropertyModel
+ * or a ClosureModel
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public interface NestedValueModel {
+    ValueModel getSourceModel();
+}
diff --git a/groovy/src/main/groovy/model/PropertyModel.java b/groovy/src/main/groovy/model/PropertyModel.java
new file mode 100644
index 0000000..f16e010
--- /dev/null
+++ b/groovy/src/main/groovy/model/PropertyModel.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.model;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * Represents a property of a value as a model.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class PropertyModel implements ValueModel, NestedValueModel {
+
+    private ValueModel sourceModel;
+    private String property;
+    private Class type;
+    boolean editable;
+
+    public PropertyModel(ValueModel sourceModel, String property) {
+        this(sourceModel, property, Object.class, true);
+    }
+
+    public PropertyModel(ValueModel sourceModel, String property, Class type) {
+        this(sourceModel, property, type, true);
+    }
+
+    public PropertyModel(ValueModel sourceModel, String property, Class type, boolean editable) {
+        this.sourceModel = sourceModel;
+        this.property = property;
+        this.type = type;
+        //TODO After 1.1 we should introspect the meta property and set editable to false if the property is read only
+        this.editable = editable;
+    }
+
+    public String getProperty() {
+        return property;
+    }
+
+    public ValueModel getSourceModel() {
+        return sourceModel;
+    }
+
+    public Object getValue() {
+        Object source = sourceModel.getValue();
+        if (source != null) {
+            return InvokerHelper.getProperty(source, property);
+        }
+        return null;
+    }
+
+    public void setValue(Object value) {
+        Object source = sourceModel.getValue();
+        if (source != null) {
+            InvokerHelper.setProperty(source, property, value);
+        }
+    }
+    
+    public Class getType() {
+        return type;
+    }
+
+    public boolean isEditable() {
+        return editable;
+    }
+
+}
diff --git a/groovy/src/main/groovy/model/ValueHolder.java b/groovy/src/main/groovy/model/ValueHolder.java
new file mode 100644
index 0000000..a84db0c
--- /dev/null
+++ b/groovy/src/main/groovy/model/ValueHolder.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.model;
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+
+/**
+ * A simple ValueModel implementation which is a holder of an object value. 
+ * Used to share local variables with closures
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ValueHolder implements ValueModel {
+    private Object value;
+    private final Class type;
+    private PropertyChangeSupport propertyChangeSupport;
+    private boolean editable = true;
+
+    public ValueHolder() {
+        this(Object.class);
+    }
+    
+    public ValueHolder(Class type) {
+        this.type = type;
+    }
+    
+    public ValueHolder(Object value) {
+        this.value = value;
+        this.type = (value != null) ? value.getClass() : Object.class;
+    }
+    
+    /** 
+     * Add a PropertyChangeListener to the listener list.
+     * @param listener The listener to add.
+     */
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        if ( propertyChangeSupport == null ) {
+            propertyChangeSupport = new PropertyChangeSupport(this);
+        }
+        propertyChangeSupport.addPropertyChangeListener(listener);
+    }
+    
+    /** 
+     * Removes a PropertyChangeListener from the listener list.
+     * @param listener The listener to remove.
+     */
+    public void removePropertyChangeListener(PropertyChangeListener listener) {
+        if ( propertyChangeSupport != null ) {
+            propertyChangeSupport.removePropertyChangeListener(listener);
+        }
+    }
+    
+
+    public Object getValue() {
+        return value;
+    }
+
+    public void setValue(Object value) {
+        Object oldValue = this.value;
+        this.value = value;
+        if ( propertyChangeSupport != null ) {
+            propertyChangeSupport.firePropertyChange("value", oldValue, value);
+        }
+    }
+
+    public Class getType() {
+        return type;
+    }
+
+    public boolean isEditable() {
+        return editable;
+    }
+    
+    public void setEditable(boolean editable) {
+        this.editable = editable;
+    }
+
+}
diff --git a/groovy/src/main/groovy/model/ValueModel.java b/groovy/src/main/groovy/model/ValueModel.java
new file mode 100644
index 0000000..777c4d7
--- /dev/null
+++ b/groovy/src/main/groovy/model/ValueModel.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.model;
+
+/**
+ * Represents a model of a value
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public interface ValueModel {
+    Object getValue();
+    void setValue(Object value);
+    Class getType();
+    boolean isEditable();
+}
diff --git a/groovy/src/main/groovy/model/package.html b/groovy/src/main/groovy/model/package.html
new file mode 100644
index 0000000..718cdfb
--- /dev/null
+++ b/groovy/src/main/groovy/model/package.html
@@ -0,0 +1,9 @@
+<html>
+  <head>
+    <title>package groovy.model.*</title>
+  </head>
+  <body>
+    <p>An MVC model package for working with user interfaces and data structures and arbitrary Java and Groovy objects
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/security/GroovyCodeSourcePermission.java b/groovy/src/main/groovy/security/GroovyCodeSourcePermission.java
new file mode 100644
index 0000000..4f50cb3
--- /dev/null
+++ b/groovy/src/main/groovy/security/GroovyCodeSourcePermission.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.security;
+
+import java.security.BasicPermission;
+
+/**
+ * Permission required to explicitly specify a codebase for a groovy script whose
+ * codebase cannot be determined.  Typically this permission is only
+ * required by clients that want to associate a code source with a script which
+ * is a String or an InputStream.
+ *
+ * @author Steve Goetze
+ */
+public final class GroovyCodeSourcePermission extends BasicPermission {
+
+	public GroovyCodeSourcePermission(String name) {
+		super(name);
+	}
+
+	public GroovyCodeSourcePermission(String name, String actions) {
+		super(name, actions);
+	}
+}
diff --git a/groovy/src/main/groovy/security/package.html b/groovy/src/main/groovy/security/package.html
new file mode 100644
index 0000000..9bd0a12
--- /dev/null
+++ b/groovy/src/main/groovy/security/package.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>package groovy.security.*</title>
+  </head>
+  <body>
+    <p>
+      Security-related classes
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/servlet/AbstractHttpServlet.java b/groovy/src/main/groovy/servlet/AbstractHttpServlet.java
new file mode 100644
index 0000000..db5fb99
--- /dev/null
+++ b/groovy/src/main/groovy/servlet/AbstractHttpServlet.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.servlet;
+
+import groovy.util.ResourceConnector;
+import groovy.util.ResourceException;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * A common ground dealing with the HTTP servlet API wrinkles.
+ * 
+ * <h4>Resource name mangling (pattern replacement)</h4>
+ * 
+ * <p> 
+ * Also implements Groovy's {@link groovy.util.ResourceConnector} in dynamic
+ * manner. It allows to modifiy the resource name that is searched for with a
+ * <i>replace all</i> operation. See {@link java.util.regex.Pattern} and
+ * {@link java.util.regex.Matcher} for details.
+ * The servlet init parameter names are:
+ * <pre>
+ * resource.name.regex = empty - defaults to null
+ * resource.name.replacement = empty - defaults to null
+ * resource.name.replace.all = true (default) | false means replaceFirst()
+ * </pre>
+ * Note: If you specify a regex, you have to specify a replacement string too!
+ * Otherwise an exception gets raised.
+ *
+ * <h4>Logging and bug-hunting options</h4>
+ *
+ * <p> 
+ * This implementation provides a verbosity flag switching log statements.
+ * The servlet init parameter name is:
+ * <pre>
+ * verbose = false(default) | true
+ * </pre>
+ * 
+ * <p> 
+ * In order to support class-loading-troubles-debugging with Tomcat 4 or
+ * higher, you can log the class loader responsible for loading some classes.
+ * See <a href="http://jira.codehaus.org/browse/GROOVY-861">GROOVY-861</a> for details.
+ * The servlet init parameter name is:
+ * <pre>
+ * log.GROOVY861 = false(default) | true
+ * </pre>
+ * 
+ * <p> 
+ * If you experience class-loading-troubles with Tomcat 4 (or higher) or any
+ * other servlet container using custom class loader setups, you can fallback
+ * to use (slower) reflection in Groovy's MetaClass implementation. Please
+ * contact the dev team with your problem! Thanks.
+ * The servlet init parameter name is:
+ * <pre>
+ * reflection = false(default) | true
+ * </pre>
+ * 
+ *
+ * @author Christian Stein
+ */
+public abstract class AbstractHttpServlet extends HttpServlet implements ResourceConnector {
+
+    /**
+     * Content type of the HTTP response.
+     */
+    public static final String CONTENT_TYPE_TEXT_HTML = "text/html";
+
+    /**
+     * Servlet API include key name: path_info
+     */
+    public static final String INC_PATH_INFO = "javax.servlet.include.path_info";
+
+    /* *** Not used, yet. See comments in getScriptUri(HttpServletRequest). ***
+     * Servlet API include key name: request_uri
+     */
+    public static final String INC_REQUEST_URI = "javax.servlet.include.request_uri";
+
+    /**
+     * Servlet API include key name: servlet_path
+     */
+    public static final String INC_SERVLET_PATH = "javax.servlet.include.servlet_path";
+
+    /**
+     * Servlet (or the web application) context.
+     */
+    protected ServletContext servletContext;
+
+    /**
+     * <b>Null</b> or compiled pattern matcher read from "resource.name.regex"
+     *  and used in {@link AbstractHttpServlet#getResourceConnection(String)}.
+     */
+    protected Matcher resourceNameMatcher;
+
+    /**
+     * The replacement used by the resource name matcher.
+     */
+    protected String resourceNameReplacement;
+
+    /**
+     * The replace method to use on the matcher.
+     * <pre>
+     * true - replaceAll(resourceNameReplacement); (default)
+     * false - replaceFirst(resourceNameReplacement);
+     * </pre>
+     */
+    protected boolean resourceNameReplaceAll;
+
+    /**
+     * Controls almost all log output.
+     */
+    protected boolean verbose;
+
+    /**
+     * Mirrors the static value of the reflection flag in MetaClass.
+     * See AbstractHttpServlet#logGROOVY861
+     */
+    protected boolean reflection;
+
+    /**
+     * Debug flag logging the class the class loader of the request.
+     */
+    private boolean logGROOVY861;
+
+    /**
+     * Initializes all fields with default values.
+     */
+    public AbstractHttpServlet() {
+        this.servletContext = null;
+        this.resourceNameMatcher = null;
+        this.resourceNameReplacement = null;
+        this.resourceNameReplaceAll = true;
+        this.verbose = false;
+        this.reflection = false;
+        this.logGROOVY861 = false;
+    }
+
+    /**
+     * Interface method for ResourceContainer. This is used by the GroovyScriptEngine.
+     */
+    public URLConnection getResourceConnection(String name) throws ResourceException {
+        /*
+         * First, mangle resource name with the compiled pattern.
+         */
+        Matcher matcher = resourceNameMatcher;
+        if (matcher != null) {
+            matcher.reset(name);
+            String replaced;
+            if (resourceNameReplaceAll) {
+                replaced = resourceNameMatcher.replaceAll(resourceNameReplacement);
+            } else {
+                replaced = resourceNameMatcher.replaceFirst(resourceNameReplacement);
+            }
+            if (!name.equals(replaced)) {
+                if (verbose) {
+                    log("Replaced resource name \"" + name + "\" with \"" + replaced + "\".");
+                }
+                name = replaced;
+            }
+        }
+
+        /*
+         * Try to locate the resource and return an opened connection to it.
+         */
+        try {
+            URL url = servletContext.getResource("/" + name);
+            if (url == null) {
+                url = servletContext.getResource("/WEB-INF/groovy/" + name);
+            }
+            if (url == null) {
+                throw new ResourceException("Resource \"" + name + "\" not found!");
+            }
+            return url.openConnection();
+        } catch (IOException e) {
+            throw new ResourceException("Problems getting resource named \"" + name + "\"!", e);
+        }
+    }
+
+    /**
+     * Returns the include-aware uri of the script or template file.
+     * 
+     * @param request
+     *  the http request to analyze
+     * @return the include-aware uri either parsed from request attributes or
+     *  hints provided by the servlet container
+     */
+    protected String getScriptUri(HttpServletRequest request) {
+        /*
+         * Log some debug information for http://jira.codehaus.org/browse/GROOVY-861
+         */
+        if (logGROOVY861) {
+            log("Logging request class and its class loader:");
+            log(" c = request.getClass() :\"" + request.getClass() + "\"");
+            log(" l = c.getClassLoader() :\"" + request.getClass().getClassLoader() + "\"");
+            log(" l.getClass()           :\"" + request.getClass().getClassLoader().getClass() + "\"");
+            /*
+             * Keep logging, if we're verbose. Else turn it off.
+             */
+            logGROOVY861 = verbose;
+        }
+
+        //
+        // NOTE: This piece of code is heavily inspired by Apaches Jasper2!
+        // 
+        // http://cvs.apache.org/viewcvs.cgi/jakarta-tomcat-jasper/jasper2/ \
+        //        src/share/org/apache/jasper/servlet/JspServlet.java?view=markup
+        //
+        // Why doesn't it use request.getRequestURI() or INC_REQUEST_URI?
+        //
+
+        String uri = null;
+        String info = null;
+
+        //
+        // Check to see if the requested script/template source file has been the
+        // target of a RequestDispatcher.include().
+        //
+        uri = (String) request.getAttribute(INC_SERVLET_PATH);
+        if (uri != null) {
+            //
+            // Requested script/template file has been target of 
+            // RequestDispatcher.include(). Its path is assembled from the relevant
+            // javax.servlet.include.* request attributes and returned!
+            //
+            info = (String) request.getAttribute(INC_PATH_INFO);
+            if (info != null) {
+                uri += info;
+            }
+            return uri;
+        }
+
+        //
+        // Requested script/template file has not been the target of a 
+        // RequestDispatcher.include(). Reconstruct its path from the request's
+        // getServletPath() and getPathInfo() results.
+        //
+        uri = request.getServletPath();
+        info = request.getPathInfo();
+        if (info != null) {
+            uri += info;
+        }
+
+        /*
+         * TODO : Enable auto ".groovy" extension replacing here!
+         * http://cvs.groovy.codehaus.org/viewrep/groovy/groovy/groovy-core/src/main/groovy/servlet/GroovyServlet.java?r=1.10#l259 
+         */
+
+        return uri;
+    }
+
+    /**
+     * Parses the http request for the real script or template source file.
+     * @param request the http request to analyze
+     * @return a file object using an absolute file path name
+     */
+    protected File getScriptUriAsFile(HttpServletRequest request) {
+        String uri = getScriptUri(request);
+        String real = servletContext.getRealPath(uri);
+        File file = new File(real).getAbsoluteFile();
+        return file;
+    }
+
+    /**
+     * Overrides the generic init method to set some debug flags.
+     * 
+     * @param config
+     *  the servlet coniguration provided by the container
+     * @throws ServletException if init() method defined in super class 
+     *  javax.servlet.GenericServlet throws it
+     */
+    public void init(ServletConfig config) throws ServletException {
+        /*
+         * Never forget super.init()!
+         */
+        super.init(config);
+
+        /*
+         * Grab the servlet context.
+         */
+        this.servletContext = config.getServletContext();
+
+        /*
+         * Get verbosity hint.
+         */
+        String value = config.getInitParameter("verbose");
+        if (value != null) {
+            this.verbose = Boolean.valueOf(value).booleanValue();
+        }
+
+        /*
+         * And now the real init work...
+         */
+        if (verbose) {
+            log("Parsing init parameters...");
+        }
+
+        String regex = config.getInitParameter("resource.name.regex");
+        if (regex != null) {
+            String replacement = config.getInitParameter("resource.name.replacement");
+            if (replacement == null) {
+                Exception npex = new NullPointerException("resource.name.replacement");
+                String message = "Init-param 'resource.name.replacement' not specified!";
+                log(message, npex);
+                throw new ServletException(message, npex);
+            }
+            int flags = 0; // TODO : Parse pattern compile flags.
+            this.resourceNameMatcher = Pattern.compile(regex, flags).matcher("");
+            this.resourceNameReplacement = replacement;
+            String all = config.getInitParameter("resource.name.replace.all");
+            if (all != null) {
+                this.resourceNameReplaceAll = Boolean.valueOf(all).booleanValue();
+            }
+        }
+
+        value = config.getInitParameter("logGROOVY861");
+        if (value != null) {
+            this.logGROOVY861 = Boolean.valueOf(value).booleanValue();
+            // nothing else to do here
+        }
+
+        /*
+         * If verbose, log the parameter values.
+         */
+        if (verbose) {
+            log("(Abstract) init done. Listing some parameter name/value pairs:");
+            log("verbose = " + verbose); // this *is* verbose! ;)
+            log("reflection = " + reflection);
+            log("logGROOVY861 = " + logGROOVY861);
+            if (resourceNameMatcher != null) {
+                log("resource.name.regex = " + resourceNameMatcher.pattern().pattern());
+            }
+            else {
+                log("resource.name.regex = null");
+            }
+            log("resource.name.replacement = " + resourceNameReplacement);
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/servlet/GroovyServlet.java b/groovy/src/main/groovy/servlet/GroovyServlet.java
new file mode 100644
index 0000000..80830ad
--- /dev/null
+++ b/groovy/src/main/groovy/servlet/GroovyServlet.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.servlet;
+
+import groovy.lang.Binding;
+import groovy.lang.Closure;
+import groovy.util.GroovyScriptEngine;
+import groovy.util.ResourceException;
+import groovy.util.ScriptException;
+
+import java.io.IOException;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.codehaus.groovy.runtime.GroovyCategorySupport;
+
+/**
+ * This servlet will run Groovy scripts as Groovlets.  Groovlets are scripts
+ * with these objects implicit in their scope:
+ *
+ * <ul>
+ * 	<li>request - the HttpServletRequest</li>
+ *  <li>response - the HttpServletResponse</li>
+ *  <li>application - the ServletContext associated with the servlet</li>
+ *  <li>session - the HttpSession associated with the HttpServletRequest</li>
+ *  <li>out - the PrintWriter associated with the ServletRequest</li>
+ * </ul>
+ *
+ * <p>Your script sources can be placed either in your web application's normal
+ * web root (allows for subdirectories) or in /WEB-INF/groovy/* (also allows
+ * subdirectories).
+ *
+ * <p>To make your web application more groovy, you must add the GroovyServlet
+ * to your application's web.xml configuration using any mapping you like, so
+ * long as it follows the pattern *.* (more on this below).  Here is the
+ * web.xml entry:
+ *
+ * <pre>
+ *    <servlet>
+ *      <servlet-name>Groovy</servlet-name>
+ *      <servlet-class>groovy.servlet.GroovyServlet</servlet-class>
+ *    </servlet>
+ *
+ *    <servlet-mapping>
+ *      <servlet-name>Groovy</servlet-name>
+ *      <url-pattern>*.groovy</url-pattern>
+ *      <url-pattern>*.gdo</url-pattern>
+ *    </servlet-mapping>
+ * </pre>
+ *
+ * <p>The URL pattern does not require the "*.groovy" mapping.  You can, for
+ * example, make it more Struts-like but groovy by making your mapping "*.gdo".
+ *
+ * @author Sam Pullara
+ * @author Mark Turansky (markturansky at hotmail.com)
+ * @author Guillaume Laforge
+ * @author Christian Stein
+ * 
+ * @see groovy.servlet.ServletBinding
+ */
+public class GroovyServlet extends AbstractHttpServlet {
+
+    /**
+     * The script engine executing the Groovy scripts for this servlet
+     */
+    private static GroovyScriptEngine gse;
+
+    /**
+     * Initialize the GroovyServlet.
+     *
+     * @throws ServletException
+     *  if this method encountered difficulties
+     */
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+
+        // Set up the scripting engine
+        gse = new GroovyScriptEngine(this);
+
+        servletContext.log("Groovy servlet initialized on " + gse + ".");
+    }
+
+    /**
+     * Handle web requests to the GroovyServlet
+     */
+    public void service(HttpServletRequest request, HttpServletResponse response) throws IOException {
+
+        // Get the script path from the request - include aware (GROOVY-815)
+        final String scriptUri = getScriptUri(request);
+
+        // Set it to HTML by default
+        response.setContentType("text/html");
+
+        // Set up the script context
+        final Binding binding = new ServletBinding(request, response, servletContext);
+
+        // Run the script
+        try {
+            Closure closure = new Closure(gse) {
+
+                public Object call() {
+                    try {
+                        return ((GroovyScriptEngine) getDelegate()).run(scriptUri, binding);
+                    } catch (ResourceException e) {
+                        throw new RuntimeException(e);
+                    } catch (ScriptException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+
+            };
+            GroovyCategorySupport.use(ServletCategory.class, closure);
+            /*
+             * Set reponse code 200.
+             */
+            response.setStatus(HttpServletResponse.SC_OK);
+        } catch (RuntimeException runtimeException) {
+            StringBuffer error = new StringBuffer("GroovyServlet Error: ");
+            error.append(" script: '");
+            error.append(scriptUri);
+            error.append("': ");
+            Throwable e = runtimeException.getCause();
+            /*
+             * Null cause?!
+             */
+            if (e == null) {
+                error.append(" Script processing failed.");
+                error.append(runtimeException.getMessage());
+                if (runtimeException.getStackTrace().length > 0)
+                    error.append(runtimeException.getStackTrace()[0].toString());
+                servletContext.log(error.toString());
+                System.err.println(error.toString());
+                runtimeException.printStackTrace(System.err);
+                response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, error.toString());
+                return;
+            }
+            /*
+             * Resource not found.
+             */
+            if (e instanceof ResourceException) {
+                error.append(" Script not found, sending 404.");
+                servletContext.log(error.toString());
+                System.err.println(error.toString());
+                response.sendError(HttpServletResponse.SC_NOT_FOUND);
+                return;
+            }
+            /*
+             * Other internal error. Perhaps syntax?! 
+             */
+            servletContext.log("An error occurred processing the request", runtimeException);
+            error.append(e.getMessage());
+            if (e.getStackTrace().length > 0)
+                error.append(e.getStackTrace()[0].toString());
+            servletContext.log(e.toString());
+            System.err.println(e.toString());
+            runtimeException.printStackTrace(System.err);
+            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.toString());
+        } finally {
+            /*
+             * Finally, flush the response buffer.
+             */
+            response.flushBuffer();
+            // servletContext.log("Flushed response buffer.");
+        }
+    }
+
+}
diff --git a/groovy/src/main/groovy/servlet/ServletBinding.java b/groovy/src/main/groovy/servlet/ServletBinding.java
new file mode 100644
index 0000000..992280c
--- /dev/null
+++ b/groovy/src/main/groovy/servlet/ServletBinding.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.servlet;
+
+import groovy.lang.Binding;
+import groovy.xml.MarkupBuilder;
+
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * Servlet-specific binding extension to lazy load the writer or the output
+ * stream from the response.
+ * 
+ * <p>
+ * <h3>Default variables bound</h3>
+ * <ul>
+ * <li><tt>"request"</tt> : the HttpServletRequest object</li>
+ * <li><tt>"response"</tt> : the HttpServletResponse object</li>
+ * <li><tt>"context"</tt> : the ServletContext object </li>
+ * <li><tt>"application"</tt> : same as context</li>
+ * <li><tt>"session"</tt> : convenient for <code>request.getSession(<b>false</b>)</code> - can be null!</li>
+ * <li><tt>"params"</tt> : map of all form parameters - can be empty</li>
+ * <li><tt>"headers"</tt> : map of all <b>request</b> header fields</li>
+ * </ul>
+ * 
+ * <p>
+ * <h3>Implicite bound variables</h3>
+ * <ul>
+ * <li><tt>"out"</tt> : response.getWriter() </li>
+ * <li><tt>"sout"</tt> : response.getOutputStream() </li>
+ * <li><tt>"html"</tt> : new MarkupBuilder(response.getWriter()) </li>
+ * </ul>
+ * </p>
+ * 
+ * @author Guillaume Laforge
+ * @author Christian Stein
+ */
+public class ServletBinding extends Binding {
+
+    private final Binding binding;
+
+    private final ServletContext context;
+
+    private final HttpServletRequest request;
+
+    private final HttpServletResponse response;
+
+    private MarkupBuilder html;
+
+    /**
+     * Initializes a servlet binding.
+     */
+    public ServletBinding(HttpServletRequest request, HttpServletResponse response, ServletContext context) {
+        this.binding = new Binding();
+        this.request = request;
+        this.response = response;
+        this.context = context;
+
+        /*
+         * Bind the default variables.
+         */
+        binding.setVariable("request", request);
+        binding.setVariable("response", response);
+        binding.setVariable("context", context);
+        binding.setVariable("application", context);
+
+        /*
+         * Bind the HTTP session object - if there is one.
+         * Note: we don't create one here!
+         */
+        binding.setVariable("session", request.getSession(false));
+
+        /*
+         * Bind form parameter key-value hash map.
+         *
+         * If there are multiple, they are passed as an array.
+         */
+        Map params = new HashMap();
+        for (Enumeration names = request.getParameterNames(); names.hasMoreElements();) {
+            String name = (String) names.nextElement();
+            if (!binding.getVariables().containsKey(name)) {
+                String[] values = request.getParameterValues(name);
+                if (values.length == 1) {
+                    params.put(name, values[0]);
+                } else {
+                    params.put(name, values);
+                }
+            }
+        }
+        binding.setVariable("params", params);
+
+        /*
+         * Bind request header key-value hash map.
+         */
+        Map headers = new HashMap();
+        for (Enumeration names = request.getHeaderNames(); names.hasMoreElements();) {
+            String headerName = (String) names.nextElement();
+            String headerValue = request.getHeader(headerName);
+            headers.put(headerName, headerValue);
+        }
+        binding.setVariable("headers", headers);
+    }
+
+    public void setVariable(String name, Object value) {
+        /*
+         * Check sanity.
+         */
+        if (name == null) {
+            throw new IllegalArgumentException("Can't bind variable to null key.");
+        }
+        if (name.length() == 0) {
+            throw new IllegalArgumentException("Can't bind variable to blank key name. [length=0]");
+        }
+        /*
+         * Check implicite key names. See getVariable(String)!
+         */
+        if ("out".equals(name)) {
+            throw new IllegalArgumentException("Can't bind variable to key named '" + name + "'.");
+        }
+        if ("sout".equals(name)) {
+            throw new IllegalArgumentException("Can't bind variable to key named '" + name + "'.");
+        }
+        if ("html".equals(name)) {
+            throw new IllegalArgumentException("Can't bind variable to key named '" + name + "'.");
+        }
+        /*
+         * TODO Check default key names. See constructor(s).
+         */
+        
+        /*
+         * All checks passed, set the variable.
+         */
+        binding.setVariable(name, value);
+    }
+
+    public Map getVariables() {
+        return binding.getVariables();
+    }
+
+    /**
+     * @return a writer, an output stream, a markup builder or another requested object
+     */
+    public Object getVariable(String name) {
+        /*
+         * Check sanity.
+         */
+        if (name == null) {
+            throw new IllegalArgumentException("No variable with null key name.");
+        }
+        if (name.length() == 0) {
+            throw new IllegalArgumentException("No variable with blank key name. [length=0]");
+        }
+        /*
+         * Check implicite key names. See setVariable(String, Object)!
+         */
+        try {
+            if ("out".equals(name)) {
+                return response.getWriter();
+            }
+            if ("sout".equals(name)) {
+                return response.getOutputStream();
+            }
+            if ("html".equals(name)) {
+                if (html == null) {
+                    html = new MarkupBuilder(response.getWriter());
+                }
+                return html;
+            }
+        } catch (IOException e) {
+            String message = "Failed to get writer or output stream from response.";
+            context.log(message, e);
+            throw new RuntimeException(message, e);
+        }
+        /*
+         * Still here? Delegate to the binding object.
+         */
+        return binding.getVariable(name);
+    }
+}
diff --git a/groovy/src/main/groovy/servlet/ServletCategory.java b/groovy/src/main/groovy/servlet/ServletCategory.java
new file mode 100644
index 0000000..9f4a505
--- /dev/null
+++ b/groovy/src/main/groovy/servlet/ServletCategory.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.servlet;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletRequest;
+import javax.servlet.http.HttpSession;
+import javax.servlet.jsp.PageContext;
+
+/**
+ * Servlet support.
+ */
+public class ServletCategory {
+	
+    public static Object get(ServletContext context, String key) {
+        return context.getAttribute(key);
+    }
+    
+    public static Object get(HttpSession session, String key) {
+        return session.getAttribute(key);
+    }
+    
+    public static Object get(ServletRequest request, String key) {
+        return request.getAttribute(key);
+    }
+
+    public static Object get(PageContext context, String key) {
+        return context.getAttribute(key);
+    }
+
+    public static Object getAt(ServletContext context, String key) {
+        return context.getAttribute(key);
+    }
+
+    public static Object getAt(HttpSession session, String key) {
+        return session.getAttribute(key);
+    }
+
+    public static Object getAt(ServletRequest request, String key) {
+        return request.getAttribute(key);
+    }
+
+    public static Object getAt(PageContext context, String key) {
+        return context.getAttribute(key);
+    }
+
+    public static void set(ServletContext context, String key, Object value) {
+        context.setAttribute(key, value);
+    }
+
+    public static void set(HttpSession session, String key, Object value) {
+        session.setAttribute(key, value);
+    }
+
+    public static void set(ServletRequest request, String key, Object value) {
+        request.setAttribute(key, value);
+    }
+
+    public static void set(PageContext context, String key, Object value) {
+        context.setAttribute(key, value);
+    }
+
+    public static void putAt(ServletContext context, String key, Object value) {
+        context.setAttribute(key, value);
+    }
+
+    public static void putAt(HttpSession session, String key, Object value) {
+        session.setAttribute(key, value);
+    }
+
+    public static void putAt(ServletRequest request, String key, Object value) {
+        request.setAttribute(key, value);
+    }
+
+    public static void putAt(PageContext context, String key, Object value) {
+        context.setAttribute(key, value);
+    }
+
+}
diff --git a/groovy/src/main/groovy/servlet/TemplateServlet.java b/groovy/src/main/groovy/servlet/TemplateServlet.java
new file mode 100644
index 0000000..1e98ddb
--- /dev/null
+++ b/groovy/src/main/groovy/servlet/TemplateServlet.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.servlet;
+
+import groovy.text.SimpleTemplateEngine;
+import groovy.text.Template;
+import groovy.text.TemplateEngine;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Date;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * A generic servlet for serving (mostly HTML) templates.
+ * 
+ * <p>
+ * It delegates work to a <code>groovy.text.TemplateEngine</code> implementation 
+ * processing HTTP requests.
+ *
+ * <h4>Usage</h4>
+ * 
+ * <code>helloworld.html</code> is a headless HTML-like template
+ * <pre><code>
+ *  &lt;html&gt;
+ *    &lt;body&gt;
+ *      &lt;% 3.times { %&gt;
+ *        Hello World!
+ *      &lt;% } %&gt;
+ *      &lt;br&gt;
+ *    &lt;/body&gt;
+ *  &lt;/html&gt; 
+ * </code></pre>
+ * 
+ * Minimal <code>web.xml</code> example serving HTML-like templates
+ * <pre><code>
+ * &lt;web-app&gt;
+ *   &lt;servlet&gt;
+ *     &lt;servlet-name&gt;template&lt;/servlet-name&gt;
+ *     &lt;servlet-class&gt;groovy.servlet.TemplateServlet&lt;/servlet-class&gt;
+ *   &lt;/servlet&gt;
+ *   &lt;servlet-mapping&gt;
+ *     &lt;servlet-name&gt;template&lt;/servlet-name&gt;
+ *     &lt;url-pattern&gt;*.html&lt;/url-pattern&gt;
+ *   &lt;/servlet-mapping&gt;
+ * &lt;/web-app&gt;
+ * </code></pre>
+ * 
+ * <h4>Template engine configuration</h4>
+ * 
+ * <p>
+ * By default, the TemplateServer uses the {@link groovy.text.SimpleTemplateEngine}
+ * which interprets JSP-like templates. The init parameter <code>template.engine</code>
+ * defines the fully qualified class name of the template to use:
+ * <pre>
+ *   template.engine = [empty] - equals groovy.text.SimpleTemplateEngine
+ *   template.engine = groovy.text.SimpleTemplateEngine
+ *   template.engine = groovy.text.GStringTemplateEngine
+ *   template.engine = groovy.text.XmlTemplateEngine
+ * </pre>
+ * 
+ * <h4>Logging and extra-output options</h4>
+ *
+ * <p>
+ * This implementation provides a verbosity flag switching log statements.
+ * The servlet init parameter name is:
+ * <pre>
+ *   generate.by = true(default) | false
+ * </pre>
+ * 
+ * @see TemplateServlet#setVariables(ServletBinding)
+ * 
+ * @author Christian Stein
+ * @author Guillaume Laforge
+ * @version 2.0
+ */
+public class TemplateServlet extends AbstractHttpServlet {
+
+    /**
+     * Simple cache entry that validates against last modified and length
+     * attributes of the specified file. 
+     *
+     * @author Christian Stein
+     */
+    private static class TemplateCacheEntry {
+
+        Date date;
+        long hit;
+        long lastModified;
+        long length;
+        Template template;
+
+        public TemplateCacheEntry(File file, Template template) {
+            this(file, template, false); // don't get time millis for sake of speed
+        }
+
+        public TemplateCacheEntry(File file, Template template, boolean timestamp) {
+            if (file == null) {
+                throw new NullPointerException("file");
+            }
+            if (template == null) {
+                throw new NullPointerException("template");
+            }
+            if (timestamp) {
+                this.date = new Date(System.currentTimeMillis());
+            } else {
+                this.date = null;
+            }
+            this.hit = 0;
+            this.lastModified = file.lastModified();
+            this.length = file.length();
+            this.template = template;
+        }
+
+        /**
+         * Checks the passed file attributes against those cached ones. 
+         *
+         * @param file
+         *  Other file handle to compare to the cached values.
+         * @return <code>true</code> if all measured values match, else <code>false</code>
+         */
+        public boolean validate(File file) {
+            if (file == null) {
+                throw new NullPointerException("file");
+            }
+            if (file.lastModified() != this.lastModified) {
+                return false;
+            }
+            if (file.length() != this.length) {
+                return false;
+            }
+            hit++;
+            return true;
+        }
+
+        public String toString() {
+            if (date == null) {
+                return "Hit #" + hit;
+            }
+            return "Hit #" + hit + " since " + date;
+        }
+
+    }
+
+    /**
+     * Simple file name to template cache map.
+     */
+    private final Map cache;
+
+    /**
+     * Underlying template engine used to evaluate template source files.
+     */
+    private TemplateEngine engine;
+
+    /**
+     * Flag that controls the appending of the "Generated by ..." comment.
+     */
+    private boolean generateBy;
+
+    /**
+     * Create new TemplateSerlvet.
+     */
+    public TemplateServlet() {
+        this.cache = new WeakHashMap();
+        this.engine = null; // assigned later by init()
+        this.generateBy = true; // may be changed by init()
+    }
+
+    /**
+     * Gets the template created by the underlying engine parsing the request.
+     * 
+     * <p>
+     * This method looks up a simple (weak) hash map for an existing template
+     * object that matches the source file. If the source file didn't change in
+     * length and its last modified stamp hasn't changed compared to a precompiled
+     * template object, this template is used. Otherwise, there is no or an
+     * invalid template object cache entry, a new one is created by the underlying
+     * template engine. This new instance is put to the cache for consecutive
+     * calls.
+     * </p>
+     * 
+     * @return The template that will produce the response text.
+     * @param file
+     *            The HttpServletRequest.
+     * @throws ServletException
+     *            If the request specified an invalid template source file 
+     */
+    protected Template getTemplate(File file) throws ServletException {
+
+        String key = file.getAbsolutePath();
+        Template template = null;
+
+        /*
+         * Test cache for a valid template bound to the key.
+         */
+        if (verbose) {
+            log("Looking for cached template by key \"" + key + "\"");
+        }
+        TemplateCacheEntry entry = (TemplateCacheEntry) cache.get(key);
+        if (entry != null) {
+            if (entry.validate(file)) {
+                if (verbose) {
+                    log("Cache hit! " + entry);
+                }
+                template = entry.template;
+            } else {
+                if (verbose) {
+                    log("Cached template needs recompiliation!");
+                }
+            }
+        } else {
+            if (verbose) {
+                log("Cache miss.");
+            }
+        }
+
+        //
+        // Template not cached or the source file changed - compile new template!
+        //
+        if (template == null) {
+            if (verbose) {
+                log("Creating new template from file " + file + "...");
+            }
+            FileReader reader = null;
+            try {
+                reader = new FileReader(file);
+                template = engine.createTemplate(reader);
+            } catch (Exception e) {
+                throw new ServletException("Creation of template failed: " + e, e);
+            } finally {
+                if (reader != null) {
+                    try {
+                        reader.close();
+                    } catch (IOException ignore) {
+                        // e.printStackTrace();
+                    }
+                }
+            }
+            cache.put(key, new TemplateCacheEntry(file, template, verbose));
+            if (verbose) {
+                log("Created and added template to cache. [key=" + key + "]");
+            }
+        }
+
+        //
+        // Last sanity check.
+        //
+        if (template == null) {
+            throw new ServletException("Template is null? Should not happen here!");
+        }
+
+        return template;
+
+    }
+
+    /**
+     * Initializes the servlet from hints the container passes.
+     * <p>
+     * Delegates to sub-init methods and parses the following parameters:
+     * <ul>
+     * <li> <tt>"generatedBy"</tt> : boolean, appends "Generated by ..." to the
+     *     HTML response text generated by this servlet.
+     *     </li>
+     * </ul>
+     * @param config
+     *  Passed by the servlet container.
+     * @throws ServletException
+     *  if this method encountered difficulties 
+     *  
+     * @see TemplateServlet#initTemplateEngine(ServletConfig)
+     */
+    public void init(ServletConfig config) throws ServletException {
+        super.init(config);
+        this.engine = initTemplateEngine(config);
+        if (engine == null) {
+            throw new ServletException("Template engine not instantiated.");
+        }
+        String value = config.getInitParameter("generated.by");
+        if (value != null) {
+            this.generateBy = Boolean.valueOf(value).booleanValue();
+        }
+        log("Servlet " + getClass().getName() + " initialized on " + engine.getClass());
+    }
+
+    /**
+     * Creates the template engine.
+     * 
+     * Called by {@link TemplateServlet#init(ServletConfig)} and returns just 
+     * <code>new groovy.text.SimpleTemplateEngine()</code> if the init parameter
+     * <code>template.engine</code> is not set by the container configuration.
+     * 
+     * @param config 
+     *  Current serlvet configuration passed by the container.
+     * 
+     * @return The underlying template engine or <code>null</code> on error.
+     */
+    protected TemplateEngine initTemplateEngine(ServletConfig config) {
+        String name = config.getInitParameter("template.engine");
+        if (name == null) {
+            return new SimpleTemplateEngine();
+        }
+        try {
+            return (TemplateEngine) Class.forName(name).newInstance();
+        } catch (InstantiationException e) {
+            log("Could not instantiate template engine: " + name, e);
+        } catch (IllegalAccessException e) {
+            log("Could not access template engine class: " + name, e);
+        } catch (ClassNotFoundException e) {
+            log("Could not find template engine class: " + name, e);
+        }
+        return null;
+    }
+
+    /**
+     * Services the request with a response.
+     * <p>
+     * First the request is parsed for the source file uri. If the specified file
+     * could not be found or can not be read an error message is sent as response.
+     * 
+     * </p>
+     * @param request
+     *            The http request.
+     * @param response
+     *            The http response.
+     * @throws IOException 
+     *            if an input or output error occurs while the servlet is
+     *            handling the HTTP request
+     * @throws ServletException
+     *            if the HTTP request cannot be handled
+     */
+    public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+
+        if (verbose) {
+            log("Creating/getting cached template...");
+        }
+
+        //
+        // Get the template source file handle.
+        //
+        File file = super.getScriptUriAsFile(request);
+        String name = file.getName();
+        if (!file.exists()) {
+            response.sendError(HttpServletResponse.SC_NOT_FOUND);
+            return; // throw new IOException(file.getAbsolutePath());
+        }
+        if (!file.canRead()) {
+            response.sendError(HttpServletResponse.SC_FORBIDDEN, "Can not read \"" + name + "\"!");
+            return; // throw new IOException(file.getAbsolutePath());
+        }
+
+        //
+        // Get the requested template.
+        //
+        long getMillis = System.currentTimeMillis();
+        Template template = getTemplate(file);
+        getMillis = System.currentTimeMillis() - getMillis;
+
+        //
+        // Create new binding for the current request.
+        //
+        ServletBinding binding = new ServletBinding(request, response, servletContext);
+        setVariables(binding);
+
+        //
+        // Prepare the response buffer content type _before_ getting the writer.
+        // and set status code to ok
+        //
+        response.setContentType(CONTENT_TYPE_TEXT_HTML);
+        response.setStatus(HttpServletResponse.SC_OK);
+
+        //
+        // Get the output stream writer from the binding.
+        //
+        Writer out = (Writer) binding.getVariable("out");
+        if (out == null) {
+            out = response.getWriter();
+        }
+
+        //
+        // Evaluate the template.
+        //
+        if (verbose) {
+            log("Making template \"" + name + "\"...");
+        }
+        // String made = template.make(binding.getVariables()).toString();
+        // log(" = " + made);
+        long makeMillis = System.currentTimeMillis();
+        template.make(binding.getVariables()).writeTo(out);
+        makeMillis = System.currentTimeMillis() - makeMillis;
+
+        if (generateBy) {
+            StringBuffer sb = new StringBuffer(100);
+            sb.append("\n<!-- Generated by Groovy TemplateServlet [create/get=");
+            sb.append(Long.toString(getMillis));
+            sb.append(" ms, make=");
+            sb.append(Long.toString(makeMillis));
+            sb.append(" ms] -->\n");
+            out.write(sb.toString());
+        }
+
+        //
+        // flush the response buffer.
+        //
+        response.flushBuffer();
+
+        if (verbose) {
+            log("Template \"" + name + "\" request responded. [create/get=" + getMillis + " ms, make=" + makeMillis + " ms]");
+        }
+
+    }
+
+    /**
+     * Override this method to set your variables to the Groovy binding.
+     * <p>
+     * All variables bound the binding are passed to the template source text, 
+     * e.g. the HTML file, when the template is merged.
+     * </p>
+     * <p>
+     * The binding provided by TemplateServlet does already include some default
+     * variables. As of this writing, they are (copied from 
+     * {@link groovy.servlet.ServletBinding}):
+     * <ul>
+     * <li><tt>"request"</tt> : HttpServletRequest </li>
+     * <li><tt>"response"</tt> : HttpServletResponse </li>
+     * <li><tt>"context"</tt> : ServletContext </li>
+     * <li><tt>"application"</tt> : ServletContext </li>
+     * <li><tt>"session"</tt> : request.getSession(<b>false</b>) </li>
+     * </ul>
+     * </p>
+     * <p>
+     * And via implicite hard-coded keywords:
+     * <ul>
+     * <li><tt>"out"</tt> : response.getWriter() </li>
+     * <li><tt>"sout"</tt> : response.getOutputStream() </li>
+     * <li><tt>"html"</tt> : new MarkupBuilder(response.getWriter()) </li>
+     * </ul>
+     * </p>
+     *
+     * <p>Example binding all servlet context variables:
+     * <pre><code>
+     * class Mytlet extends TemplateServlet {
+     * 
+     *   protected void setVariables(ServletBinding binding) {
+     *     // Bind a simple variable
+     *     binding.setVariable("answer", new Long(42));
+     *   
+     *     // Bind all servlet context attributes...
+     *     ServletContext context = (ServletContext) binding.getVariable("context");
+     *     Enumeration enumeration = context.getAttributeNames();
+     *     while (enumeration.hasMoreElements()) {
+     *       String name = (String) enumeration.nextElement();
+     *       binding.setVariable(name, context.getAttribute(name));
+     *     }
+     *   }
+     * 
+     * }
+     * <code></pre>
+     * </p>
+     * 
+     * @param binding
+     *  to be modified
+     */
+    protected void setVariables(ServletBinding binding) {
+        // empty
+    }
+
+}
diff --git a/groovy/src/main/groovy/servlet/package.html b/groovy/src/main/groovy/servlet/package.html
new file mode 100644
index 0000000..134bc7c
--- /dev/null
+++ b/groovy/src/main/groovy/servlet/package.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>package groovy.servlet.*</title>
+  </head>
+  <body>
+    <p>
+      Support for Groovlets which are Servlets written as a simple Groovy script.
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/sql/CallResultSet.java b/groovy/src/main/groovy/sql/CallResultSet.java
new file mode 100644
index 0000000..1c8a2ff
--- /dev/null
+++ b/groovy/src/main/groovy/sql/CallResultSet.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.sql;
+
+
+import java.sql.CallableStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+/**
+ * @author rfuller
+ *
+ * Represents a ResultSet retrieved as a callable statement out parameter.
+ */
+class CallResultSet extends GroovyResultSetExtension {
+	int indx;
+	CallableStatement call;
+	ResultSet resultSet;
+	boolean firstCall = true;
+	
+	CallResultSet(CallableStatement call, int indx){
+        super(null);
+		this.call = call;
+		this.indx = indx;
+	}
+	
+	protected ResultSet getResultSet() throws SQLException{
+		if(firstCall){
+		    resultSet = (ResultSet) call.getObject(indx+1);
+			firstCall = false;
+		}
+		return resultSet;
+	}
+    
+    protected static GroovyResultSet getImpl(CallableStatement call, int idx) {
+        GroovyResultSetProxy proxy = new GroovyResultSetProxy(new CallResultSet(call,idx));
+        return proxy.getImpl();
+    }
+}
diff --git a/groovy/src/main/groovy/sql/DataSet.java b/groovy/src/main/groovy/sql/DataSet.java
new file mode 100644
index 0000000..ccb6372
--- /dev/null
+++ b/groovy/src/main/groovy/sql/DataSet.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.sql;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyRuntimeException;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.stmt.Statement;
+
+/**
+ * Represents an extent of objects
+ * 
+ * @author Chris Stevenson
+ * @author Paul King
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DataSet extends Sql {
+
+    private Closure where;
+    private DataSet parent;
+    private String table;
+    private SqlWhereVisitor visitor;
+    private String sql;
+    private List params;
+
+    public DataSet(Sql sql, Class type) {
+        super(sql);
+        String table = type.getName();
+        int idx = table.lastIndexOf('.');
+        if (idx > 0) {
+            table = table.substring(idx + 1);
+        }
+        this.table = table.toLowerCase();
+    }
+
+    public DataSet(Sql sql, String table) {
+        super(sql);
+        this.table = table;
+    }
+
+    public DataSet(DataSet parent, Closure where) {
+        super(parent);
+        this.table = parent.table;
+        this.parent = parent;
+        this.where = where;
+    }
+
+    public void add(Map values) throws SQLException {
+        StringBuffer buffer = new StringBuffer("insert into ");
+        buffer.append(table);
+        buffer.append(" (");
+        StringBuffer paramBuffer = new StringBuffer();
+        boolean first = true;
+        for (Iterator iter = values.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            String column = entry.getKey().toString();
+            if (first) {
+                first = false;
+                paramBuffer.append("?");
+            }
+            else {
+                buffer.append(", ");
+                paramBuffer.append(", ?");
+            }
+            buffer.append(column);
+        }
+        buffer.append(") values (");
+        buffer.append(paramBuffer.toString());
+        buffer.append(")");
+
+        Connection connection = createConnection();
+        PreparedStatement statement = null;
+        try {
+            statement = connection.prepareStatement(buffer.toString());
+            int i = 1;
+            for (Iterator iter = values.entrySet().iterator(); iter.hasNext();) {
+                Map.Entry entry = (Map.Entry) iter.next();
+                setObject(statement, i++, entry.getValue());
+            }
+            int answer = statement.executeUpdate();
+            if (answer != 1) {
+                log.log(Level.WARNING, "Should have updated 1 row not " + answer + " when trying to add: " + values);
+            }
+        }
+        catch (SQLException e) {
+            log.log(Level.WARNING, "Failed to add row for: " + values, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement);
+        }
+    }
+
+    public DataSet findAll(Closure where) {
+        return new DataSet(this, where);
+    }
+
+    public void each(Closure closure) throws SQLException {
+        eachRow(getSql(), getParameters(), closure);
+    }
+
+    public String getSql() {
+        if (sql == null) {
+            sql = "select * from " + table;
+            if (where != null) {
+                String clause = "";
+                if (parent != null && parent.where != null) {
+                    clause += parent.getSqlVisitor().getWhere() + " and ";
+                }
+                clause += getSqlVisitor().getWhere();
+                if (clause.length() > 0) {
+                    sql += " where " + clause;
+                }
+            }
+        }
+        return sql;
+    }
+
+    public List getParameters() {
+        if (params == null) {
+            params = new ArrayList();
+            if (parent != null && parent.where != null) {
+                params.addAll(parent.getParameters());
+            }
+            params.addAll(getSqlVisitor().getParameters());
+        }
+        return params;
+    }
+
+    protected SqlWhereVisitor getSqlVisitor() {
+        if (visitor == null) {
+            visitor = new SqlWhereVisitor();
+            if (where != null) {
+                ClassNode classNode = where.getMetaClass().getClassNode();
+                if (classNode == null) {
+                    throw new GroovyRuntimeException(
+                        "Could not find the ClassNode for MetaClass: " + where.getMetaClass());
+                }
+                List methods = classNode.getDeclaredMethods("doCall");
+                if (!methods.isEmpty()) {
+                    MethodNode method = (MethodNode) methods.get(0);
+                    if (method != null) {
+                        Statement statement = method.getCode();
+                        if (statement != null) {
+                            statement.visit(visitor);
+                        }
+                    }
+                }
+            }
+        }
+        return visitor;
+    }
+    /*
+     * create a subset of the original dataset
+     */
+    public DataSet createView(Closure criteria) {
+    	return new DataSet(this, criteria);
+    }
+    
+    /**
+     * Returns a List of all of the rows from the table a DataSet
+     * represents
+     * @return  Returns a list of GroovyRowResult objects from the dataset
+     * @throws SQLException if a database error occurs
+     */
+    public List rows() throws SQLException {
+        return rows(getSql(), getParameters());
+    }
+
+    /**
+     * Returns the first row from a DataSet's underlying table
+     * 
+     * @return  Returns the first GroovyRowResult object from the dataset
+     * @throws SQLException if a database error occurs
+     */
+    public Object firstRow() throws SQLException{
+        List rows = rows();
+        if (rows.isEmpty()) return null;
+        return(rows.get(0));
+    }
+}
diff --git a/groovy/src/main/groovy/sql/ExpandedVariable.java b/groovy/src/main/groovy/sql/ExpandedVariable.java
new file mode 100644
index 0000000..5878b73
--- /dev/null
+++ b/groovy/src/main/groovy/sql/ExpandedVariable.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.sql;
+
+/**
+ * Identifies a variable to be expanded into the
+ * sql string rather than representing a placeholder.
+ *
+ * @author rfuller
+ */
+public interface ExpandedVariable {
+    Object getObject();
+}
diff --git a/groovy/src/main/groovy/sql/GroovyResultSet.java b/groovy/src/main/groovy/sql/GroovyResultSet.java
new file mode 100644
index 0000000..f9a08c7
--- /dev/null
+++ b/groovy/src/main/groovy/sql/GroovyResultSet.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.sql;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Map;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
+
+/**
+ * Represents an extent of objects
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:ivan_ganza@yahoo.com">Ivan Ganza</a>
+ * @author Chris Stevenson
+ * @version $Revision$
+ */
+public interface GroovyResultSet extends GroovyObject, ResultSet {
+    /**
+     * Supports integer based subscript operators for accessing at numbered columns
+     * starting at zero. Negative indices are supported, they will count from the last column backwards.
+     *
+     * @param index is the number of the column to look at starting at 1
+     * @throws SQLException if a database error occurs
+     * @return the object for this index in the current result set
+     */
+    Object getAt(int index) throws SQLException;
+
+    /**
+     * Supports integer based subscript operators for updating the values of numbered columns
+     * starting at zero. Negative indices are supported, they will count from the last column backwards.
+     *
+     * @param index is the number of the column to look at starting at 1
+     * @param newValue the new value for this index
+     * @throws SQLException if a database error occurs
+     */
+    void putAt(int index, Object newValue) throws SQLException;
+
+    /**
+     * Adds a new row to this result set
+     *
+     * @param values teh new values to add
+     * @throws SQLException if a database error occurs
+     */
+    void add(Map values) throws SQLException;
+
+    /**
+     * Call the closure once for each row in the result set.
+     *
+     * @param closure the closure to call for each row
+     * @throws SQLException if a database error occurs
+     */
+    void eachRow(Closure closure) throws SQLException;
+
+}
diff --git a/groovy/src/main/groovy/sql/GroovyResultSetExtension.java b/groovy/src/main/groovy/sql/GroovyResultSetExtension.java
new file mode 100644
index 0000000..20a83ed
--- /dev/null
+++ b/groovy/src/main/groovy/sql/GroovyResultSetExtension.java
@@ -0,0 +1,255 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.sql;

+

+import groovy.lang.Closure;

+import groovy.lang.GroovyObjectSupport;

+import groovy.lang.MissingPropertyException;

+

+import java.sql.ResultSet;

+import java.sql.SQLException;

+import java.sql.ResultSetMetaData;

+import java.util.Iterator;

+import java.util.Map;

+

+import org.codehaus.groovy.runtime.InvokerHelper;

+import org.codehaus.groovy.runtime.InvokerInvocationException;

+

+/**

+ * GroovyResultSetExtension implements additional logic for ResultSet. Due to 

+ * the version incompatibility between java6 and java5 this methods are moved 

+ * here from the original GroovyResultSet class. The methods in this class are

+ * used by the proxy GroovyResultSetProxy, which will try to invoke methods

+ * on this class before invokeing it on ResultSet. 

+ * 

+ * <p><b>This class is not intended to be used directly. Should be used through

+ *  GroovyResultSetProxy only!</b></p>

+ * 

+ * @see GroovyResultSet

+ * @see GroovyResultSetProxy

+ *  

+ * @author Jochen Theodorou

+ */

+public class GroovyResultSetExtension extends GroovyObjectSupport{

+

+    private boolean updated;

+    private final ResultSet resultSet;

+    

+    /**

+     * Gets the current result set.

+     * @return the result set

+     * @throws SQLException if the result set can not be returned

+     */

+    protected ResultSet getResultSet() throws SQLException{

+        return resultSet;

+    }

+    

+    /**

+     * Creats a GroovyResultSet implementation-

+     *  

+     * @param set the result set 

+     */

+    public GroovyResultSetExtension(ResultSet set) {

+        updated = false;

+        resultSet = set;

+    }

+

+    public String toString() {

+        try {

+            StringBuffer sb = new StringBuffer("[");

+            ResultSetMetaData metaData = resultSet.getMetaData();

+            int count = metaData.getColumnCount();

+            for (int i = 1; i <= count; i++) {

+                sb.append(metaData.getColumnName(i));

+                sb.append(":");

+                sb.append(resultSet.getObject(i).toString());

+                if (i < count) {

+                    sb.append(", ");

+                }

+            }

+            sb.append("]");

+            return sb.toString();

+        } catch (SQLException e) {

+//            System.err.println("e.getMessage() = " + e.getMessage());

+            return super.toString();

+        }

+    }

+

+    public Object invokeMethod(String name, Object args) {

+        try {

+            return InvokerHelper.invokeMethod(getResultSet(), name, args);

+        } catch (SQLException se) {

+            throw new InvokerInvocationException(se);

+        }

+    }

+    

+    /**

+     * Gets the value of the designated column in the current row 

+     * of as an <code>Object</code>.

+     * @param columnName the SQL name of the column

+     * @throws MissingPropertyException 

+     *   if an SQLException happens while getting the object

+     * @see groovy.lang.GroovyObject#getProperty(java.lang.String)

+     * @see ResultSet#getObject(java.lang.String)

+     */

+    public Object getProperty(String columnName) {

+        try {

+            return getResultSet().getObject(columnName);

+        }

+        catch (SQLException e) {

+            throw new MissingPropertyException(columnName, GroovyResultSetProxy.class, e);

+        }

+    }

+

+

+    /**

+     * Updates the designated column with an <code>Object</code> value.

+     * @param columnName the SQL name of the column

+     * @throws MissingPropertyException 

+     *   if an SQLException happens while getting the object 

+     * @see groovy.lang.GroovyObject#setProperty(java.lang.String, java.lang.Object)

+     * @see ResultSet#updateObject(java.lang.String, java.lang.Object)

+     */

+    public void setProperty(String columnName, Object newValue) {

+        try {

+            getResultSet().updateObject(columnName, newValue);

+            updated = true;

+        }

+        catch (SQLException e) {

+            throw new MissingPropertyException(columnName, GroovyResultSetProxy.class, e);

+        }

+    }

+    

+    /**

+     * Supports integer based subscript operators for accessing at numbered columns

+     * starting at zero. Negative indices are supported, they will count from the last column backwards.

+     *

+     * @param index is the number of the column to look at starting at 1

+     * @see ResultSet#getObject(int)

+     */

+    public Object getAt(int index) throws SQLException {

+        index = normalizeIndex(index);

+        return getResultSet().getObject(index);

+    }

+    

+    /**

+     * Supports integer based subscript operators for updating the values of numbered columns

+     * starting at zero. Negative indices are supported, they will count from the last column backwards.

+     *

+     * @param index is the number of the column to look at starting at 1

+     * @see ResultSet#updateObject(java.lang.String, java.lang.Object)

+     */

+    public void putAt(int index, Object newValue) throws SQLException {

+        index = normalizeIndex(index);

+        getResultSet().updateObject(index, newValue);

+    }

+

+    /**

+     * Adds a new row to the result set

+     *

+     * @param values a map containing the mappings for column names and values

+     * @see ResultSet#insertRow()

+     * @see ResultSet#updateObject(java.lang.String, java.lang.Object)

+     * @see ResultSet#moveToInsertRow()

+     */

+    public void add(Map values) throws SQLException {

+        getResultSet().moveToInsertRow();

+        for (Iterator iter = values.entrySet().iterator(); iter.hasNext();) {

+            Map.Entry entry = (Map.Entry) iter.next();

+            getResultSet().updateObject(entry.getKey().toString(), entry.getValue());

+        }

+        getResultSet().insertRow();

+    }

+

+    /**

+     * Takes a zero based index and convert it into an SQL based 1 based index.

+     * A negative index will count backwards from the last column.

+     *

+     * @param index

+     * @return a JDBC index

+     * @throws SQLException if some exception occurs finding out the column count

+     */

+    protected int normalizeIndex(int index) throws SQLException {

+        if (index < 0) {

+            int columnCount = getResultSet().getMetaData().getColumnCount();

+            do {

+                index += columnCount;

+            }

+            while (index < 0);

+        }

+        return index + 1;

+    }

+

+

+    /**

+     * Call the closure once for each row in the result set.

+     *

+     * @param closure

+     * @throws SQLException

+     */

+    public void eachRow(Closure closure) throws SQLException {

+        while (next()) {

+            closure.call(this);

+        }

+    }

+    // Implementation of java.sql.getResultSet()

+    // ------------------------------------------------------------

+

+    /**

+     * Moves the cursor down one row from its current position.

+     * A <code>getResultSet()</code> cursor is initially positioned

+     * before the first row; the first call to the method

+     * <code>next</code> makes the first row the current row; the

+     * second call makes the second row the current row, and so on.

+     * <p/>

+     * <P>If an input stream is open for the current row, a call

+     * to the method <code>next</code> will

+     * implicitly close it. A <code>getResultSet()</code> object's

+     * warning chain is cleared when a new row is read.

+     *

+     * @return <code>true</code> if the new current row is valid;

+     *         <code>false</code> if there are no more rows

+     * @throws SQLException if a database access error occurs

+     */

+    public boolean next() throws SQLException {

+        if (updated) {

+            getResultSet().updateRow();

+            updated = false;

+        }

+        return getResultSet().next();

+    }

+    

+    /**

+     * Moves the cursor to the previous row in this

+     * <code>getResultSet()</code> object.

+     *

+     * @return <code>true</code> if the cursor is on a valid row;

+     *         <code>false</code> if it is off the result set

+     * @throws SQLException if a database access error

+     *                      occurs or the result set type is <code>TYPE_FORWARD_ONLY</code>

+     * @since 1.2

+     */

+    public boolean previous() throws SQLException {

+        if (updated) {

+            getResultSet().updateRow();

+            updated = false;

+        }

+        return getResultSet().previous();

+    }

+

+

+

+}

diff --git a/groovy/src/main/groovy/sql/GroovyResultSetProxy.java b/groovy/src/main/groovy/sql/GroovyResultSetProxy.java
new file mode 100644
index 0000000..c448c2f
--- /dev/null
+++ b/groovy/src/main/groovy/sql/GroovyResultSetProxy.java
@@ -0,0 +1,120 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.sql;

+

+import groovy.lang.GroovyObject;

+import groovy.lang.GroovySystem;

+import groovy.lang.MetaClass;

+

+import java.lang.reflect.InvocationHandler;

+import java.lang.reflect.Method;

+import java.lang.reflect.Proxy;

+import java.sql.ResultSet;

+

+import org.codehaus.groovy.runtime.InvokerHelper;

+

+/**

+ * GroovyResultSetProxy is used to create a proxy for GroovyResultSet.

+ * Due to the version incompatibility between java 6 and older versions

+ * methods with additional logic were moved into an extension class. When

+ * getting properties or calling methods, the runtime will try to first

+ * execute these on the extension and then on the ResultSet itself.

+ * This way it is possible to replace and add methods. To overload methods

+ * from ResultSet all methods have to be implemented on the extension

+ * class.

+ *

+ * @author Jochen Theodorou

+ */

+public final class GroovyResultSetProxy implements InvocationHandler {

+

+    private GroovyResultSetExtension extension;

+

+    /**

+     * Creates a new procy instance.

+     * This will create the extension automatically using

+     * GroovyResultSetExtension

+     *

+     * @param set the result set to delegate to

+     * @see GroovyResultSetExtension

+     */

+    public GroovyResultSetProxy(ResultSet set) {

+        extension = new GroovyResultSetExtension(set);

+    }

+

+    /**

+     * Creates a new proxy instance with a custom extension.

+     *

+     * @param ext the extension

+     * @see GroovyResultSetExtension

+     */

+    public GroovyResultSetProxy(GroovyResultSetExtension ext) {

+        extension = ext;

+    }

+

+    /**

+     * Invokes a method for the GroovyResultSet.

+     * This will try to invoke the given method first on the extension

+     * and then on the result set given as proxy parameter.

+     *

+     * @param proxy  the result set

+     * @param method the method name of this method will be used

+     *               to make a call on the extension. If this fails the call will be

+     *               done on the proxy instead

+     * @param args   for the call

+     * @see ResultSet

+     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])

+     */

+    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

+        String name = method.getName();

+        if (method.getDeclaringClass() == GroovyObject.class) {

+            if (name.equals("getMetaClass")) {

+                return getMetaClass();

+            } else if (name.equals("setMetaClass")) {

+                return setMetaClass((MetaClass) args[0]);

+            }

+        }

+

+        return InvokerHelper.invokeMethod(extension, method.getName(), args);

+    }

+

+    private MetaClass metaClass;

+

+    private MetaClass setMetaClass(MetaClass mc) {

+        metaClass = mc;

+        return mc;

+    }

+

+    private MetaClass getMetaClass() {

+        if (metaClass == null) {

+            metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(GroovyResultSet.class);

+        }

+        return metaClass;

+    }

+

+    /**

+     * Gets a proxy instance that can be used as GroovyResultSet.

+     *

+     * @return the proxy

+     */

+    public GroovyResultSet getImpl() {

+        return (GroovyResultSet)

+                Proxy.newProxyInstance(

+                        this.getClass().getClassLoader(),

+                        new Class[]{GroovyResultSet.class},

+                        this

+                );

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/sql/GroovyRowResult.java b/groovy/src/main/groovy/sql/GroovyRowResult.java
new file mode 100644
index 0000000..e0cb6a1
--- /dev/null
+++ b/groovy/src/main/groovy/sql/GroovyRowResult.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.sql;
+
+import groovy.lang.GroovyObjectSupport;
+import groovy.lang.MissingPropertyException;
+
+import java.util.*;
+
+/**
+ * Represents an extent of objects.
+ * It's used in the oneRow method to be able to access the result
+ * of a SQL query by the name of the column, or by the column number.
+ *
+ * @version $Revision$
+ * @author Jean-Louis Berliet
+ */
+public class GroovyRowResult extends GroovyObjectSupport implements Map {
+
+    private final LinkedHashMap result;
+
+    public GroovyRowResult(LinkedHashMap result) {
+        this.result = result;
+    }
+
+    /**
+     * Retrieve the value of the property by its name    *
+     *
+     * @param property is the name of the property to look at
+     * @return the value of the property
+     */
+    public Object getProperty(String property) {
+        try {
+            Object value = result.get(property);
+            if (value != null)
+                return value;
+            // if property exists and value is null, return null
+            if (result.containsKey(property))
+                return null;
+            // with some databases/drivers, the columns names are stored uppercase.
+            String propertyUpper = property.toUpperCase();
+            value = result.get(propertyUpper);
+            if (value != null)
+                return value;
+            // if property exists and value is null, return null
+            if (result.containsKey(propertyUpper)) 
+                return null;
+            throw new MissingPropertyException(property, GroovyRowResult.class);
+        }
+        catch (Exception e) {
+            throw new MissingPropertyException(property, GroovyRowResult.class, e);
+        }
+    }
+
+    /**
+     * Retrieve the value of the property by its index.
+     * A negative index will count backwards from the last column.
+     *
+     * @param index is the number of the column to look at
+     * @return the value of the property
+     */
+    public Object getAt(int index) {
+        try {
+            // a negative index will count backwards from the last column.
+            if (index < 0)
+                index += result.size();
+            Iterator it = result.values().iterator();
+            int i = 0;
+            Object obj = null;
+            while ((obj == null) && (it.hasNext())) {
+                if (i == index)
+                    obj = it.next();
+                else
+                    it.next();
+                i++;
+            }
+            return (obj);
+        }
+        catch (Exception e) {
+            throw new MissingPropertyException(Integer.toString(index), GroovyRowResult.class, e);
+        }
+    }
+
+    public String toString() {
+        return (result.toString());
+    }
+
+    /*
+     * The following methods are needed for implementing the Map interface.
+     * They are just delegating the request to the internal LinkedHashMap
+     */
+     
+    public void clear() {
+        result.clear();
+    }
+
+    public boolean containsKey(Object key) {
+        return result.containsKey(key);
+    }
+
+    public boolean containsValue(Object value) {
+        return result.containsValue(value);
+    }
+
+    public Set entrySet() {
+        return result.entrySet();
+    }
+
+    public boolean equals(Object o) {
+        return result.equals(o);
+    }
+
+    public Object get(Object property) {
+        if (property instanceof String)
+            return getProperty((String)property);
+        else
+            return null;
+    }
+
+    public int hashCode() {
+        return result.hashCode();
+    }
+
+    public boolean isEmpty() {
+        return result.isEmpty();
+    }
+
+    public Set keySet() {
+        return result.keySet();
+    }
+
+    public Object put(Object key, Object value) {
+        return result.put(key, value);
+    }
+
+    public void putAll(Map t) {
+        result.putAll(t);
+    }
+
+    public Object remove(Object key) {
+        return result.remove(key);
+    }
+
+    public int size() {
+        return result.size();
+    }
+
+    public Collection values() {
+        return result.values();
+    }
+}
diff --git a/groovy/src/main/groovy/sql/InOutParameter.java b/groovy/src/main/groovy/sql/InOutParameter.java
new file mode 100644
index 0000000..9149b86
--- /dev/null
+++ b/groovy/src/main/groovy/sql/InOutParameter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.sql;
+
+/**
+ * @author rfuller
+ *
+ * A typed parameter passed to, and returned from a CallableStatement.
+ */
+public interface InOutParameter extends InParameter, OutParameter {
+
+}
diff --git a/groovy/src/main/groovy/sql/InParameter.java b/groovy/src/main/groovy/sql/InParameter.java
new file mode 100644
index 0000000..3ba6830
--- /dev/null
+++ b/groovy/src/main/groovy/sql/InParameter.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.sql;
+
+/**
+ * A typed parameter to pass to a query
+ *
+ * @author rfuller
+ */
+public interface InParameter {
+
+    /**
+     * The JDBC data type.
+     * @return the type
+     */
+    int getType();
+
+    /**
+     * The object holding the data value.
+     * @return the value
+     */
+    Object getValue();
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/sql/OutParameter.java b/groovy/src/main/groovy/sql/OutParameter.java
new file mode 100644
index 0000000..9900a0c
--- /dev/null
+++ b/groovy/src/main/groovy/sql/OutParameter.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.sql;
+
+/**
+ * A parameter to be returned from a CallableStatement.
+ *
+ * @author rfuller
+ */
+public interface OutParameter {
+    /**
+     * Get the JDBC datatype for this parameter.
+     * @return the type
+     */
+    int getType();
+}
diff --git a/groovy/src/main/groovy/sql/ResultSetOutParameter.java b/groovy/src/main/groovy/sql/ResultSetOutParameter.java
new file mode 100644
index 0000000..4664b1d
--- /dev/null
+++ b/groovy/src/main/groovy/sql/ResultSetOutParameter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.sql;
+
+/**
+ * A ResultSet out parameter.
+ * @author rfuller
+ *
+ */
+public interface ResultSetOutParameter extends OutParameter{
+
+}
diff --git a/groovy/src/main/groovy/sql/Sql.java b/groovy/src/main/groovy/sql/Sql.java
new file mode 100644
index 0000000..1320281
--- /dev/null
+++ b/groovy/src/main/groovy/sql/Sql.java
@@ -0,0 +1,1260 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.sql;
+
+import groovy.lang.Closure;
+import groovy.lang.GString;
+
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedHashMap;
+import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.sql.DataSource;
+
+/**
+ * Represents an extent of objects
+ *
+ * @author Chris Stevenson
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan </a>
+ * @version $Revision$
+ */
+public class Sql {
+
+    protected Logger log = Logger.getLogger(getClass().getName());
+
+    private DataSource dataSource;
+
+    private Connection useConnection;
+
+    /** lets only warn of using deprecated methods once */
+    private boolean warned;
+
+    // store the last row count for executeUpdate
+    int updateCount = 0;
+
+    /** allows a closure to be used to configure the statement before its use */
+    private Closure configureStatement;
+
+    /**
+     * A helper method which creates a new Sql instance from a JDBC connection
+     * URL
+     *
+     * @param url
+     * @return a new Sql instance with a connection
+     */
+    public static Sql newInstance(String url) throws SQLException {
+        Connection connection = DriverManager.getConnection(url);
+        return new Sql(connection);
+    }
+
+    /**
+     * A helper method which creates a new Sql instance from a JDBC connection
+     * URL
+     *
+     * @param url
+     * @return a new Sql instance with a connection
+     */
+    public static Sql newInstance(String url, Properties properties) throws SQLException {
+        Connection connection = DriverManager.getConnection(url, properties);
+        return new Sql(connection);
+    }
+
+    /**
+     * A helper method which creates a new Sql instance from a JDBC connection
+     * URL and driver class name
+     *
+     * @param url
+     * @return a new Sql instance with a connection
+     */
+    public static Sql newInstance(String url, Properties properties, String driverClassName) throws SQLException, ClassNotFoundException {
+        loadDriver(driverClassName);
+        return newInstance(url, properties);
+    }
+
+    /**
+     * A helper method which creates a new Sql instance from a JDBC connection
+     * URL, username and password
+     *
+     * @param url
+     * @return a new Sql instance with a connection
+     */
+    public static Sql newInstance(String url, String user, String password) throws SQLException {
+        Connection connection = DriverManager.getConnection(url, user, password);
+        return new Sql(connection);
+    }
+
+    /**
+     * A helper method which creates a new Sql instance from a JDBC connection
+     * URL, username, password and driver class name
+     *
+     * @param url
+     * @return a new Sql instance with a connection
+     */
+    public static Sql newInstance(String url, String user, String password, String driverClassName) throws SQLException,
+            ClassNotFoundException {
+        loadDriver(driverClassName);
+        return newInstance(url, user, password);
+    }
+
+    /**
+     * A helper method which creates a new Sql instance from a JDBC connection
+     * URL and driver class name
+     *
+     * @param url
+     * @param driverClassName
+     *            the class name of the driver
+     * @return a new Sql instance with a connection
+     */
+    public static Sql newInstance(String url, String driverClassName) throws SQLException, ClassNotFoundException {
+        loadDriver(driverClassName);
+        return newInstance(url);
+    }
+
+    /**
+     * Attempts to load the JDBC driver on the thread, current or system class
+     * loaders
+     *
+     * @param driverClassName
+     * @throws ClassNotFoundException
+     */
+    public static void loadDriver(String driverClassName) throws ClassNotFoundException {
+        // lets try the thread context class loader first
+        // lets try to use the system class loader
+        try {
+            Class.forName(driverClassName);
+        }
+        catch (ClassNotFoundException e) {
+            try {
+                Thread.currentThread().getContextClassLoader().loadClass(driverClassName);
+            }
+            catch (ClassNotFoundException e2) {
+                // now lets try the classloader which loaded us
+                try {
+                    Sql.class.getClassLoader().loadClass(driverClassName);
+                }
+                catch (ClassNotFoundException e3) {
+                    throw e;
+                }
+            }
+        }
+    }
+
+    public static final OutParameter ARRAY         = new OutParameter(){ public int getType() { return Types.ARRAY; }};
+    public static final OutParameter BIGINT        = new OutParameter(){ public int getType() { return Types.BIGINT; }};
+    public static final OutParameter BINARY        = new OutParameter(){ public int getType() { return Types.BINARY; }};
+    public static final OutParameter BIT           = new OutParameter(){ public int getType() { return Types.BIT; }};
+    public static final OutParameter BLOB          = new OutParameter(){ public int getType() { return Types.BLOB; }};
+    public static final OutParameter BOOLEAN       = new OutParameter(){ public int getType() { return Types.BOOLEAN; }};
+    public static final OutParameter CHAR          = new OutParameter(){ public int getType() { return Types.CHAR; }};
+    public static final OutParameter CLOB          = new OutParameter(){ public int getType() { return Types.CLOB; }};
+    public static final OutParameter DATALINK      = new OutParameter(){ public int getType() { return Types.DATALINK; }};
+    public static final OutParameter DATE          = new OutParameter(){ public int getType() { return Types.DATE; }};
+    public static final OutParameter DECIMAL       = new OutParameter(){ public int getType() { return Types.DECIMAL; }};
+    public static final OutParameter DISTINCT      = new OutParameter(){ public int getType() { return Types.DISTINCT; }};
+    public static final OutParameter DOUBLE        = new OutParameter(){ public int getType() { return Types.DOUBLE; }};
+    public static final OutParameter FLOAT         = new OutParameter(){ public int getType() { return Types.FLOAT; }};
+    public static final OutParameter INTEGER       = new OutParameter(){ public int getType() { return Types.INTEGER; }};
+    public static final OutParameter JAVA_OBJECT   = new OutParameter(){ public int getType() { return Types.JAVA_OBJECT; }};
+    public static final OutParameter LONGVARBINARY = new OutParameter(){ public int getType() { return Types.LONGVARBINARY; }};
+    public static final OutParameter LONGVARCHAR   = new OutParameter(){ public int getType() { return Types.LONGVARCHAR; }};
+    public static final OutParameter NULL          = new OutParameter(){ public int getType() { return Types.NULL; }};
+    public static final OutParameter NUMERIC       = new OutParameter(){ public int getType() { return Types.NUMERIC; }};
+    public static final OutParameter OTHER         = new OutParameter(){ public int getType() { return Types.OTHER; }};
+    public static final OutParameter REAL          = new OutParameter(){ public int getType() { return Types.REAL; }};
+    public static final OutParameter REF           = new OutParameter(){ public int getType() { return Types.REF; }};
+    public static final OutParameter SMALLINT      = new OutParameter(){ public int getType() { return Types.SMALLINT; }};
+    public static final OutParameter STRUCT        = new OutParameter(){ public int getType() { return Types.STRUCT; }};
+    public static final OutParameter TIME          = new OutParameter(){ public int getType() { return Types.TIME; }};
+    public static final OutParameter TIMESTAMP     = new OutParameter(){ public int getType() { return Types.TIMESTAMP; }};
+    public static final OutParameter TINYINT       = new OutParameter(){ public int getType() { return Types.TINYINT; }};
+    public static final OutParameter VARBINARY     = new OutParameter(){ public int getType() { return Types.VARBINARY; }};
+    public static final OutParameter VARCHAR       = new OutParameter(){ public int getType() { return Types.VARCHAR; }};
+
+    public static InParameter ARRAY(Object value) { return in(Types.ARRAY, value); }
+    public static InParameter BIGINT(Object value) { return in(Types.BIGINT, value); }
+    public static InParameter BINARY(Object value) { return in(Types.BINARY, value); }
+    public static InParameter BIT(Object value) { return in(Types.BIT, value); }
+    public static InParameter BLOB(Object value) { return in(Types.BLOB, value); }
+    public static InParameter BOOLEAN(Object value) { return in(Types.BOOLEAN, value); }
+    public static InParameter CHAR(Object value) { return in(Types.CHAR, value); }
+    public static InParameter CLOB(Object value) { return in(Types.CLOB, value); }
+    public static InParameter DATALINK(Object value) { return in(Types.DATALINK, value); }
+    public static InParameter DATE(Object value) { return in(Types.DATE, value); }
+    public static InParameter DECIMAL(Object value) { return in(Types.DECIMAL, value); }
+    public static InParameter DISTINCT(Object value) { return in(Types.DISTINCT, value); }
+    public static InParameter DOUBLE(Object value) { return in(Types.DOUBLE, value); }
+    public static InParameter FLOAT(Object value) { return in(Types.FLOAT, value); }
+    public static InParameter INTEGER(Object value) { return in(Types.INTEGER, value); }
+    public static InParameter JAVA_OBJECT(Object value) { return in(Types.JAVA_OBJECT, value); }
+    public static InParameter LONGVARBINARY(Object value) { return in(Types.LONGVARBINARY, value); }
+    public static InParameter LONGVARCHAR(Object value) { return in(Types.LONGVARCHAR, value); }
+    public static InParameter NULL(Object value) { return in(Types.NULL, value); }
+    public static InParameter NUMERIC(Object value) { return in(Types.NUMERIC, value); }
+    public static InParameter OTHER(Object value) { return in(Types.OTHER, value); }
+    public static InParameter REAL(Object value) { return in(Types.REAL, value); }
+    public static InParameter REF(Object value) { return in(Types.REF, value); }
+    public static InParameter SMALLINT(Object value) { return in(Types.SMALLINT, value); }
+    public static InParameter STRUCT(Object value) { return in(Types.STRUCT, value); }
+    public static InParameter TIME(Object value) { return in(Types.TIME, value); }
+    public static InParameter TIMESTAMP(Object value) { return in(Types.TIMESTAMP, value); }
+    public static InParameter TINYINT(Object value) { return in(Types.TINYINT, value); }
+    public static InParameter VARBINARY(Object value) { return in(Types.VARBINARY, value); }
+    public static InParameter VARCHAR(Object value) { return in(Types.VARCHAR, value); }
+
+    /**
+     * Create a new InParameter
+     * @param type the JDBC data type
+     * @param value the object value
+     * @return an InParameter
+     */
+    public static InParameter in(final int type, final Object value) {
+        return new InParameter() {
+            public int getType() {
+                return type;
+            }
+            public Object getValue() {
+                return value;
+            }
+        };
+    }
+    
+    /**
+     * Create a new OutParameter
+     * @param type the JDBC data type.
+     * @return an OutParameter
+     */
+    public static OutParameter out(final int type){
+        return new OutParameter(){
+            public int getType() {
+                return type;
+            }
+        };
+    }
+    
+    /**
+     * Create an inout parameter using this in parameter.
+     * @param in
+     */
+    public static InOutParameter inout(final InParameter in){
+        return new InOutParameter(){
+            public int getType() {
+                return in.getType();
+            }
+            public Object getValue() {
+                return in.getValue();
+            }            
+        };
+    }
+    
+    /**
+     * Create a new ResultSetOutParameter
+     * @param type the JDBC data type.
+     * @return a ResultSetOutParameter
+     */
+    public static ResultSetOutParameter resultSet(final int type){
+        return new ResultSetOutParameter(){
+            public int getType() {
+                return type;
+            }
+        };
+    }
+        
+    /**
+     * Creates a variable to be expanded in the Sql string rather
+     * than representing an sql parameter.
+     * @param object
+     */
+    public static ExpandedVariable expand(final Object object){
+        return new ExpandedVariable(){
+            public Object getObject() {
+                return object;
+            }};
+    }
+    
+    /**
+     * Constructs an SQL instance using the given DataSource. Each operation
+     * will use a Connection from the DataSource pool and close it when the
+     * operation is completed putting it back into the pool.
+     *
+     * @param dataSource
+     */
+    public Sql(DataSource dataSource) {
+        this.dataSource = dataSource;
+    }
+
+    /**
+     * Constructs an SQL instance using the given Connection. It is the caller's
+     * responsibility to close the Connection after the Sql instance has been
+     * used. You can do this on the connection object directly or by calling the
+     * {@link java.sql.Connection#close()}  method.
+     *
+     * @param connection
+     */
+    public Sql(Connection connection) {
+        if (connection == null) {
+            throw new NullPointerException("Must specify a non-null Connection");
+        }
+        this.useConnection = connection;
+    }
+
+    public Sql(Sql parent) {
+        this.dataSource = parent.dataSource;
+        this.useConnection = parent.useConnection;
+    }
+
+    public DataSet dataSet(String table) {
+        return new DataSet(this, table);
+    }
+
+    public DataSet dataSet(Class type) {
+        return new DataSet(this, type);
+    }
+
+    /**
+     * Performs the given SQL query calling the closure with the result set
+     */
+    public void query(String sql, Closure closure) throws SQLException {
+        Connection connection = createConnection();
+        Statement statement = connection.createStatement();
+        configure(statement);
+        ResultSet results = null;
+        try {
+            log.fine(sql);
+            results = statement.executeQuery(sql);
+            closure.call(results);
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement, results);
+        }
+    }
+
+    /**
+     * Performs the given SQL query with parameters calling the closure with the
+     * result set
+     */
+    public void query(String sql, List params, Closure closure) throws SQLException {
+        Connection connection = createConnection();
+        PreparedStatement statement = null;
+        ResultSet results = null;
+        try {
+            log.fine(sql);
+            statement = connection.prepareStatement(sql);
+            setParameters(params, statement);
+            configure(statement);
+            results = statement.executeQuery();
+            closure.call(results);
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement, results);
+        }
+    }
+
+    /**
+     * Performs the given SQL query calling the closure with the result set
+     */
+    public void query(GString gstring, Closure closure) throws SQLException {
+        List params = getParameters(gstring);
+        String sql = asSql(gstring, params);
+        query(sql, params, closure);
+    }
+
+    /**
+     * @deprecated please use eachRow instead
+     */
+    public void queryEach(String sql, Closure closure) throws SQLException {
+        warnDeprecated();
+        eachRow(sql, closure);
+    }
+
+    /**
+     * Performs the given SQL query calling the closure with each row of the
+     * result set
+     */
+    public void eachRow(String sql, Closure closure) throws SQLException {
+        eachRow(sql,(Closure) null,closure);
+    }
+
+    /**
+     * Performs the given SQL query calling closures for metadata and each row
+     * @param sql the sql statement
+     * @param metaClosure called for meta data (only once after sql execution)
+     * @param rowClosure called for each row with a GroovyResultSet
+     */
+    public void eachRow(String sql, Closure metaClosure, Closure rowClosure) throws SQLException {
+        Connection connection = createConnection();
+        Statement statement = connection.createStatement();
+        configure(statement);
+        ResultSet results = null;
+        try {
+            log.fine(sql);
+            results = statement.executeQuery(sql);
+            
+            if (metaClosure!=null) metaClosure.call( results.getMetaData() );
+            
+            GroovyResultSet groovyRS = new GroovyResultSetProxy(results).getImpl();
+            while (groovyRS.next()) {
+                rowClosure.call(groovyRS);
+            }
+        } catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        } finally {
+            closeResources(connection, statement, results);
+        }
+    }
+
+    /**
+     * @deprecated please use eachRow instead
+     */
+    public void queryEach(String sql, List params, Closure closure) throws SQLException {
+        warnDeprecated();
+        eachRow(sql, params, closure);
+    }
+
+    /**
+     * Performs the given SQL query calling the closure with the result set
+     */
+    public void eachRow(String sql, List params, Closure closure) throws SQLException {
+        Connection connection = createConnection();
+        PreparedStatement statement = null;
+        ResultSet results = null;
+        try {
+            log.fine(sql);
+            statement = connection.prepareStatement(sql);
+            setParameters(params, statement);
+            configure(statement);
+            results = statement.executeQuery();
+
+            GroovyResultSet groovyRS = new GroovyResultSetProxy(results).getImpl();
+            while (groovyRS.next()) {
+                closure.call(groovyRS);
+            }
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement, results);
+        }
+    }
+
+    /**
+     * Performs the given SQL query calling the closure with the result set
+     */
+    public void eachRow(GString gstring, Closure closure) throws SQLException {
+        List params = getParameters(gstring);
+        String sql = asSql(gstring, params);
+        eachRow(sql, params, closure);
+    }
+
+    /**
+     * @deprecated please use eachRow instead
+     */
+    public void queryEach(GString gstring, Closure closure) throws SQLException {
+        warnDeprecated();
+        eachRow(gstring, closure);
+    }
+
+    /**
+     * Performs the given SQL query and return the rows of the result set
+     */
+     public List rows(String sql) throws SQLException {
+        return rows(sql,(Closure) null);
+    }
+
+    /**
+     * Performs the given SQL query and return the rows of the result set
+     * @param sql the SQL statement
+     * @param metaClosure called with meta data of the ResultSet
+     */
+     public List rows(String sql, Closure metaClosure) throws SQLException {
+         List results = new ArrayList();
+         Connection connection = createConnection();
+         Statement statement = connection.createStatement();
+         configure(statement);
+         ResultSet rs = null;
+         try {
+             log.fine(sql);
+             rs = statement.executeQuery(sql);
+
+             if (metaClosure!=null) metaClosure.call( rs.getMetaData() );
+
+             while (rs.next()) {
+                 ResultSetMetaData metadata = rs.getMetaData();
+                 LinkedHashMap lhm = new LinkedHashMap(metadata.getColumnCount(),1,true);
+                 for(int i=1 ; i<=metadata.getColumnCount() ; i++) {
+                     lhm.put(metadata.getColumnName(i),rs.getObject(i));
+                 }
+                 GroovyRowResult row = new GroovyRowResult(lhm);
+                 results.add(row);
+             }
+             return(results);
+         } catch (SQLException e) {
+             log.log(Level.FINE, "Failed to execute: " + sql, e);
+             throw e;
+         } finally {
+             closeResources(connection, statement, rs);
+         }
+    }
+      
+    /**
+     * Performs the given SQL query and return the first row of the result set
+     */
+    public Object firstRow(String sql) throws SQLException {
+        List rows = rows(sql);
+        if (rows.isEmpty()) return null;
+        return(rows.get(0));
+    }
+
+    /**
+     * Performs the given SQL query with the list of params and return
+     * the rows of the result set
+     */
+    public List rows(String sql, List params) throws SQLException {
+        List results = new ArrayList();
+        Connection connection = createConnection();
+        PreparedStatement statement = null;
+        ResultSet rs = null;
+        try {
+            log.fine(sql);
+            statement = connection.prepareStatement(sql);
+            setParameters(params, statement);
+            configure(statement);
+            rs = statement.executeQuery();
+            while (rs.next()) {
+                ResultSetMetaData metadata = rs.getMetaData();
+                LinkedHashMap lhm = new LinkedHashMap(metadata.getColumnCount(),1,true);
+                for(int i=1 ; i<=metadata.getColumnCount() ; i++) {
+                    lhm.put(metadata.getColumnName(i),rs.getObject(i));
+                }
+                GroovyRowResult row = new GroovyRowResult(lhm);
+                results.add(row);
+            }
+            return(results);
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement, rs);
+        }
+    }
+
+     /**
+      * Performs the given SQL query with the list of params and return
+      * the first row of the result set
+      */
+    public Object firstRow(String sql, List params) throws SQLException {
+         List rows = rows(sql, params);
+         if (rows.isEmpty()) return null;
+         return rows.get(0);
+     }
+
+    /**
+     * Executes the given piece of SQL
+     */
+    public boolean execute(String sql) throws SQLException {
+        Connection connection = createConnection();
+        Statement statement = null;
+        try {
+            log.fine(sql);
+            statement = connection.createStatement();
+            configure(statement);
+            boolean isResultSet = statement.execute(sql);
+            this.updateCount = statement.getUpdateCount();
+            return isResultSet;
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement);
+        }
+    }
+
+    /**
+     * Executes the given SQL update
+     * 
+     * @return the number of rows updated
+     */
+    public int executeUpdate(String sql) throws SQLException {
+        Connection connection = createConnection();
+        Statement statement = null;
+        try {
+            log.fine(sql);
+            statement = connection.createStatement();
+            configure(statement);
+            this.updateCount = statement.executeUpdate(sql);
+            return this.updateCount;
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement);
+        }
+    }
+
+    /**
+     * Executes the given SQL statement. See {@link #executeInsert(GString)}
+     * for more details. 
+     * @param sql The SQL statement to execute.
+     * @return A list of the auto-generated column values for each
+     * inserted row.
+     */
+    public List executeInsert(String sql) throws SQLException {
+        Connection connection = createConnection();
+        Statement statement = null;
+        try {
+            log.fine(sql);
+            statement = connection.createStatement();
+            configure(statement);
+            boolean hasResultSet = statement.execute(sql, Statement.RETURN_GENERATED_KEYS);
+
+            // Prepare a list to contain the auto-generated column
+            // values, and then fetch them from the statement.
+            List autoKeys = new ArrayList();
+        	ResultSet keys = statement.getGeneratedKeys();
+        	int count = keys.getMetaData().getColumnCount();
+
+        	// Copy the column values into a list of a list.
+        	while (keys.next()) {
+        		List rowKeys = new ArrayList(count);
+        		for (int i = 1; i <= count; i++) {
+        			rowKeys.add(keys.getObject(i));
+        		}
+
+        		autoKeys.add(rowKeys);
+        	}
+
+        	// Store the update count so that it can be retrieved by
+        	// clients, and then return the list of auto-generated
+        	// values.
+        	this.updateCount = statement.getUpdateCount();
+        	return autoKeys;
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement);
+        }
+    }
+
+    /**
+     * Executes the given piece of SQL with parameters
+     */
+    public boolean execute(String sql, List params) throws SQLException {
+        Connection connection = createConnection();
+        PreparedStatement statement = null;
+        try {
+            log.fine(sql);
+            statement = connection.prepareStatement(sql);
+            setParameters(params, statement);
+            configure(statement);
+            boolean isResultSet = statement.execute();
+            this.updateCount = statement.getUpdateCount();
+            return isResultSet;
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement);
+        }
+    }
+
+    /**
+     * Executes the given SQL update with parameters
+     * 
+     * @return the number of rows updated
+     */
+    public int executeUpdate(String sql, List params) throws SQLException {
+        Connection connection = createConnection();
+        PreparedStatement statement = null;
+        try {
+            log.fine(sql);
+            statement = connection.prepareStatement(sql);
+            setParameters(params, statement);
+            configure(statement);
+            this.updateCount = statement.executeUpdate();
+            return this.updateCount;
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement);
+        }
+    }
+
+    /**
+     * Executes the given SQL statement with a particular list of
+     * parameter values. See {@link #executeInsert(GString)} for
+     * more details. 
+     * @param sql The SQL statement to execute.
+     * @param params The parameter values that will be substituted
+     * into the SQL statement's parameter slots.
+     * @return A list of the auto-generated column values for each
+     * inserted row.
+     */
+    public List executeInsert(String sql, List params) throws SQLException {
+        // Now send the SQL to the database.
+        Connection connection = createConnection();
+        PreparedStatement statement = null;
+        try {
+            log.fine(sql);
+
+            // Prepare a statement for the SQL and then execute it.
+            statement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
+            setParameters(params, statement);
+            configure(statement);
+            this.updateCount = statement.executeUpdate();
+
+            // Prepare a list to contain the auto-generated column
+            // values, and then fetch them from the statement.
+            List autoKeys = new ArrayList();
+        	ResultSet keys = statement.getGeneratedKeys();
+        	int count = keys.getMetaData().getColumnCount();
+
+        	// Copy the column values into a list of a list.
+        	while (keys.next()) {
+        		List rowKeys = new ArrayList(count);
+        		for (int i = 1; i <= count; i++) {
+        			rowKeys.add(keys.getObject(i));
+        		}
+
+        		autoKeys.add(rowKeys);
+        	}
+
+        	return autoKeys;
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement);
+        }
+    }
+
+    /**
+     * Executes the given SQL with embedded expressions inside
+     */
+    public boolean execute(GString gstring) throws SQLException {
+        List params = getParameters(gstring);
+        String sql = asSql(gstring, params);
+        return execute(sql, params);
+    }
+
+    /**
+     * Executes the given SQL update with embedded expressions inside
+     * 
+     * @return the number of rows updated
+     */
+    public int executeUpdate(GString gstring) throws SQLException {
+        List params = getParameters(gstring);
+        String sql = asSql(gstring, params);
+        return executeUpdate(sql, params);
+    }
+
+    /**
+     * <p>Executes the given SQL with embedded expressions inside, and
+     * returns the values of any auto-generated colums, such as an
+     * autoincrement ID field. These values can be accessed using
+     * array notation. For example, to return the second auto-generated
+     * column value of the third row, use <code>keys[3][1]</code>. The
+     * method is designed to be used with SQL INSERT statements, but is
+     * not limited to them.</p>
+     * <p>The standard use for this method is when a table has an
+     * autoincrement ID column and you want to know what the ID is for
+     * a newly inserted row. In this example, we insert a single row
+     * into a table in which the first column contains the autoincrement
+     * ID:</p>
+     * <pre>
+     *     def sql = Sql.newInstance("jdbc:mysql://localhost:3306/groovy",
+     *                               "user", 
+     *                               "password",
+     *                               "com.mysql.jdbc.Driver")
+     *
+     *     def keys = sql.insert("insert into test_table (INT_DATA, STRING_DATA) "
+     *                           + "VALUES (1, 'Key Largo')")
+     *
+     *     def id = keys[0][0]
+     *
+     *     // 'id' now contains the value of the new row's ID column.
+     *     // It can be used to update an object representation's
+     *     // id attribute for example.
+     *     ...
+     * </pre>
+     * @return A list of column values representing each row's
+     * auto-generated keys.
+     */
+    public List executeInsert(GString gstring) throws SQLException {
+        List params = getParameters(gstring);
+        String sql = asSql(gstring, params);
+        return executeInsert(sql, params);
+    }
+
+    /**
+     * Performs a stored procedure call
+     */
+    public int call(String sql) throws Exception {
+        return call(sql, Collections.EMPTY_LIST);
+    }
+
+    /**
+     * Performs a stored procedure call with the given parameters
+     */
+    public int call(String sql, List params) throws Exception {
+        Connection connection = createConnection();
+        CallableStatement statement = connection.prepareCall(sql);
+        try {
+            log.fine(sql);
+            setParameters(params, statement);
+            configure(statement);
+            return statement.executeUpdate();
+        }
+        catch (SQLException e) {
+            log.log(Level.FINE, "Failed to execute: " + sql, e);
+            throw e;
+        }
+        finally {
+            closeResources(connection, statement);
+        }
+    }
+
+    /**
+     * Performs a stored procedure call with the given parameters.  The closure
+     * is called once with all the out parameters.
+     */
+    public void call(String sql, List params, Closure closure) throws Exception {
+        Connection connection = createConnection();
+        CallableStatement statement = connection.prepareCall(sql);
+        try {
+            log.fine(sql);
+            setParameters(params, statement);
+            statement.execute();
+            List results = new ArrayList();
+            int indx = 0;
+            int inouts = 0;
+            for (Iterator iter = params.iterator(); iter.hasNext();) {
+                Object value = iter.next();
+                if(value instanceof OutParameter){
+                    if(value instanceof ResultSetOutParameter){
+                        results.add(CallResultSet.getImpl(statement,indx));
+                    }else{
+                        Object o = statement.getObject(indx+1);
+                        if(o instanceof ResultSet){
+                            results.add(new GroovyResultSetProxy((ResultSet)o).getImpl());
+                        }else{
+                            results.add(o);
+                        }
+                    }
+                    inouts++;
+                }
+                indx++;
+            }
+            closure.call(results.toArray(new Object[inouts]));
+        } catch (SQLException e) {
+            log.log(Level.WARNING, "Failed to execute: " + sql, e);
+            throw e;
+        } finally {
+            closeResources(connection, statement);
+        }
+    }
+    
+    /**
+     * Performs a stored procedure call with the given parameters
+     */
+    public int call(GString gstring) throws Exception {
+        List params = getParameters(gstring);
+        String sql = asSql(gstring, params);
+        return call(sql, params);
+    }
+
+
+    /**
+     * Performs a stored procedure call with the given parameters,
+     * calling the closure once with all result objects.
+     */
+    public void call(GString gstring, Closure closure) throws Exception {
+        List params = getParameters(gstring);
+        String sql = asSql(gstring,params);
+        call(sql, params,closure);
+    }
+    
+    /**
+     * If this SQL object was created with a Connection then this method closes
+     * the connection. If this SQL object was created from a DataSource then
+     * this method does nothing.
+     * 
+     * @throws SQLException
+     */
+    public void close() throws SQLException {
+        if (useConnection != null) {
+            useConnection.close();
+        }
+    }
+
+    public DataSource getDataSource() {
+        return dataSource;
+    }
+
+
+    public void commit() {
+        try {
+            this.useConnection.commit();
+        }
+        catch (SQLException e) {
+            log.log(Level.SEVERE, "Caught exception commiting connection: " + e, e);
+        }
+    }
+
+    public void rollback() {
+        try {
+            this.useConnection.rollback();
+        }
+        catch (SQLException e) {
+            log.log(Level.SEVERE, "Caught exception rollbacking connection: " + e, e);
+        }
+    }
+
+    /**
+     * @return Returns the updateCount.
+     */
+    public int getUpdateCount() {
+        return updateCount;
+    }
+
+    /**
+     * If this instance was created with a single Connection then the connection
+     * is returned. Otherwise if this instance was created with a DataSource
+     * then this method returns null
+     *
+     * @return the connection wired into this object, or null if this object
+     *         uses a DataSource
+     */
+    public Connection getConnection() {
+        return useConnection;
+    }
+
+
+    /**
+     * Allows a closure to be passed in to configure the JDBC statements before they are executed
+     * to do things like set the query size etc.
+     *
+     * @param configureStatement
+     */
+    public void withStatement(Closure configureStatement) {
+        this.configureStatement = configureStatement;
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * @return the SQL version of the given query using ? instead of any
+     *         parameter
+     */
+    protected String asSql(GString gstring, List values) {
+        String[] strings = gstring.getStrings();
+        if (strings.length <= 0) {
+            throw new IllegalArgumentException("No SQL specified in GString: " + gstring);
+        }
+        boolean nulls = false;
+        StringBuffer buffer = new StringBuffer();
+        boolean warned = false;
+        Iterator iter = values.iterator();
+        for (int i = 0; i < strings.length; i++) {
+            String text = strings[i];
+            if (text != null) {
+                buffer.append(text);
+            }
+            if (iter.hasNext()) {
+                Object value = iter.next();
+                if (value != null) {
+                    if(value instanceof ExpandedVariable){
+                        buffer.append(((ExpandedVariable)value).getObject());
+                        iter.remove();
+                    }else{
+                        boolean validBinding = true;
+                        if (i < strings.length - 1) {
+                            String nextText = strings[i + 1];
+                            if ((text.endsWith("\"") || text.endsWith("'")) && (nextText.startsWith("'") || nextText.startsWith("\""))) {
+                                if (!warned) {
+                                    log.warning("In Groovy SQL please do not use quotes around dynamic expressions " +
+                                            "(which start with $) as this means we cannot use a JDBC PreparedStatement " +
+                                            "and so is a security hole. Groovy has worked around your mistake but the security hole is still there. " +
+                                            "The expression so far is: " + buffer.toString() + "?" + nextText);
+                                    warned = true;
+                                }
+                                buffer.append(value);
+                                iter.remove();
+                                validBinding = false;
+                            }
+                        }
+                        if (validBinding) {
+                            buffer.append("?");
+                        }
+                    }
+                }
+                else {
+                    nulls = true;
+                    buffer.append("?'\"?"); // will replace these with nullish
+                    // values
+                }
+            }
+        }
+        String sql = buffer.toString();
+        if (nulls) {
+            sql = nullify(sql);
+        }
+        return sql;
+    }
+
+    /**
+     * replace ?'"? references with NULLish
+     * 
+     * @param sql
+     */
+    protected String nullify(String sql) {
+        /*
+         * Some drivers (Oracle classes12.zip) have difficulty resolving data
+         * type if setObject(null). We will modify the query to pass 'null', 'is
+         * null', and 'is not null'
+         */
+        //could be more efficient by compiling expressions in advance.
+        int firstWhere = findWhereKeyword(sql);
+        if (firstWhere >= 0) {
+            Pattern[] patterns = { Pattern.compile("(?is)^(.{" + firstWhere + "}.*?)!=\\s{0,1}(\\s*)\\?'\"\\?(.*)"),
+                    Pattern.compile("(?is)^(.{" + firstWhere + "}.*?)<>\\s{0,1}(\\s*)\\?'\"\\?(.*)"),
+                    Pattern.compile("(?is)^(.{" + firstWhere + "}.*?[^<>])=\\s{0,1}(\\s*)\\?'\"\\?(.*)"), };
+            String[] replacements = { "$1 is not $2null$3", "$1 is not $2null$3", "$1 is $2null$3", };
+            for (int i = 0; i < patterns.length; i++) {
+                Matcher matcher = patterns[i].matcher(sql);
+                while (matcher.matches()) {
+                    sql = matcher.replaceAll(replacements[i]);
+                    matcher = patterns[i].matcher(sql);
+                }
+            }
+        }
+        return sql.replaceAll("\\?'\"\\?", "null");
+    }
+
+    /**
+     * Find the first 'where' keyword in the sql.
+     * 
+     * @param sql
+     */
+    protected int findWhereKeyword(String sql) {
+        char[] chars = sql.toLowerCase().toCharArray();
+        char[] whereChars = "where".toCharArray();
+        int i = 0;
+        boolean inString = false; //TODO: Cater for comments?
+        boolean noWhere = true;
+        int inWhere = 0;
+        while (i < chars.length && noWhere) {
+            switch (chars[i]) {
+                case '\'':
+                    if (inString) {
+                        inString = false;
+                    }
+                    else {
+                        inString = true;
+                    }
+                    break;
+                default:
+                    if (!inString && chars[i] == whereChars[inWhere]) {
+                        inWhere++;
+                        if (inWhere == whereChars.length) {
+                            return i;
+                        }
+                    }
+            }
+            i++;
+        }
+        return -1;
+    }
+
+    /**
+     * @return extracts the parameters from the expression as a List
+     */
+    protected List getParameters(GString gstring) {
+        Object[] values = gstring.getValues();
+        List answer = new ArrayList(values.length);
+        for (int i = 0; i < values.length; i++) {
+            if (values[i] != null) {
+                answer.add(values[i]);
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Appends the parameters to the given statement
+     */
+    protected void setParameters(List params, PreparedStatement statement) throws SQLException {
+        int i = 1;
+        for (Iterator iter = params.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            setObject(statement, i++, value);
+        }
+    }
+
+    /**
+     * Strategy method allowing derived classes to handle types differently
+     * such as for CLOBs etc.
+     */
+    protected void setObject(PreparedStatement statement, int i, Object value)
+        throws SQLException {
+        if (value instanceof InParameter  || value instanceof OutParameter) {
+            if(value instanceof InParameter){
+                InParameter in = (InParameter) value;
+                Object val = in.getValue();
+                if (null == val) {
+                    statement.setNull(i, in.getType());
+                } else {
+                    statement.setObject(i, val, in.getType());
+                }
+            }
+            if(value instanceof OutParameter){
+                try{
+                    OutParameter out = (OutParameter)value;
+                    ((CallableStatement)statement).registerOutParameter(i,out.getType());
+                }catch(ClassCastException e){
+                    throw new SQLException("Cannot register out parameter.");
+                }
+            }
+        } else {
+            statement.setObject(i, value);
+        }
+    }
+
+    protected Connection createConnection() throws SQLException {
+        if (dataSource != null) {
+            //Use a doPrivileged here as many different properties need to be
+            // read, and the policy
+            //shouldn't have to list them all.
+            Connection con = null;
+            try {
+                con = (Connection) AccessController.doPrivileged(new PrivilegedExceptionAction() {
+                    public Object run() throws SQLException {
+                        return dataSource.getConnection();
+                    }
+                });
+            }
+            catch (PrivilegedActionException pae) {
+                Exception e = pae.getException();
+                if (e instanceof SQLException) {
+                    throw (SQLException) e;
+                }
+                else {
+                    throw (RuntimeException) e;
+                }
+            }
+            return con;
+        }
+        else {
+            //System.out.println("createConnection returning: " +
+            // useConnection);
+            return useConnection;
+        }
+    }
+
+    protected void closeResources(Connection connection, Statement statement, ResultSet results) {
+        if (results != null) {
+            try {
+                results.close();
+            }
+            catch (SQLException e) {
+                log.log(Level.SEVERE, "Caught exception closing resultSet: " + e, e);
+            }
+        }
+        closeResources(connection, statement);
+    }
+
+    protected void closeResources(Connection connection, Statement statement) {
+        if (statement != null) {
+            try {
+                statement.close();
+            }
+            catch (SQLException e) {
+                log.log(Level.SEVERE, "Caught exception closing statement: " + e, e);
+            }
+        }
+        if (dataSource != null) {
+            try {
+                connection.close();
+            }
+            catch (SQLException e) {
+                log.log(Level.SEVERE, "Caught exception closing connection: " + e, e);
+            }
+        }
+    }
+
+    private void warnDeprecated() {
+        if (!warned) {
+            warned = true;
+            log.warning("queryEach() is deprecated, please use eachRow() instead");
+        }
+    }
+
+    /**
+     * Provides a hook to be able to configure JDBC statements, such as to configure
+     *
+     * @param statement
+     */
+    protected void configure(Statement statement) {
+        if (configureStatement != null) {
+            configureStatement.call(statement);
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/sql/SqlWhereVisitor.java b/groovy/src/main/groovy/sql/SqlWhereVisitor.java
new file mode 100644
index 0000000..2fd4cc0
--- /dev/null
+++ b/groovy/src/main/groovy/sql/SqlWhereVisitor.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.sql;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+
+/**
+ * @author James Strachan
+ * @version $Revision$
+ */
+public class SqlWhereVisitor extends CodeVisitorSupport {
+
+    private final StringBuffer buffer = new StringBuffer();
+    private final List parameters = new ArrayList();
+
+    public String getWhere() {
+        return buffer.toString();
+    }
+
+    public void visitReturnStatement(ReturnStatement statement) {
+        statement.getExpression().visit(this);
+    }
+
+    public void visitBinaryExpression(BinaryExpression expression) {
+        Expression left = expression.getLeftExpression();
+        Expression right = expression.getRightExpression();
+        boolean leaf = (right instanceof ConstantExpression);
+
+        if (!leaf) buffer.append("(");
+        left.visit(this);
+        buffer.append(" ");
+
+        Token token = expression.getOperation();
+        buffer.append(tokenAsSql(token));
+
+        buffer.append(" ");
+        right.visit(this);
+        if (!leaf) buffer.append(")");
+    }
+
+    public void visitBooleanExpression(BooleanExpression expression) {
+        expression.getExpression().visit(this);
+    }
+
+    public void visitConstantExpression(ConstantExpression expression) {
+        getParameters().add(expression.getValue());
+        buffer.append("?");
+        
+    }
+
+    public void visitPropertyExpression(PropertyExpression expression) {
+        buffer.append(expression.getPropertyAsString());
+    }
+    
+    public List getParameters() {
+        return parameters;
+    }
+    
+    protected String tokenAsSql(Token token) {
+        switch (token.getType()) {
+            case Types.COMPARE_EQUAL :
+                return "=";
+            case Types.LOGICAL_AND :
+                return "and";
+            case Types.LOGICAL_OR :
+                return "or";
+            default :
+                return token.getText();
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/sql/package.html b/groovy/src/main/groovy/sql/package.html
new file mode 100644
index 0000000..1fd3885
--- /dev/null
+++ b/groovy/src/main/groovy/sql/package.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>package groovy.sql.*</title>
+  </head>
+  <body>
+    <p>
+      Groovy helper classes for working with SQL data as Groovy objects
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/swing/LookAndFeelHelper.groovy b/groovy/src/main/groovy/swing/LookAndFeelHelper.groovy
new file mode 100644
index 0000000..7dc1b24
--- /dev/null
+++ b/groovy/src/main/groovy/swing/LookAndFeelHelper.groovy
@@ -0,0 +1,146 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing

+

+import javax.swing.LookAndFeel

+import javax.swing.UIManager

+import javax.swing.plaf.metal.MetalLookAndFeel

+import javax.swing.plaf.metal.MetalTheme

+import javax.swing.plaf.metal.DefaultMetalTheme

+

+

+class LookAndFeelHelper {

+

+    // protected so you can subclass and replace the singleton

+    protected static LookAndFeelHelper instance;

+

+    public static LookAndFeelHelper getInstance() {

+        return instance ?: (instance = new LookAndFeelHelper())

+    }

+

+    private Map lafCodeNames = [

+        // stuff built into various JDKs

+        metal   : 'javax.swing.plaf.metal.MetalLookAndFeel',

+        nimbus  : 'sun.swing.plaf.nimbus.NimbusLookAndFeel',

+        mac     : 'apple.laf.AquaLookAndFeel',

+        motif   : 'com.sun.java.swing.plaf.motif.MotifLookAndFeel',

+        windows : 'com.sun.java.swing.plaf.windows.WindowsLookAndFeel',

+        win2k   : 'com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel',

+        gtk     : 'com.sun.java.swing.plaf.gtk.GTKLookAndFeel',

+        synth   : 'javax.swing.plaf.synth.SynthLookAndFeel',

+

+        // generic aliases in UIManager

+        system        : UIManager.getSystemLookAndFeelClassName(),

+        crossPlatform : UIManager.getCrossPlatformLookAndFeelClassName(),

+

+        // jgoodies, requires external library

+        plastic   : 'com.jgoodies.looks.plastic.PlasticLookAndFeel',

+        plastic3D : 'com.jgoodies.looks.plastic.Plastic3DLookAndFeel',

+        plasticXP : 'com.jgoodies.looks.plastic.PlasticXPLookAndFeel',

+

+        // substance, requires external library

+        substance : 'org.jvnet.substance.SubstanceLookAndFeel',

+

+        // napkin, requires external library

+        napkin : 'net.sourceforge.napkinlaf.NapkinLookAndFeel'

+    ]

+

+    public String addLookAndFeelAlias(String alias, String className) {

+        lafCodeNames[alias] = className

+    }

+

+    private Map extendedAttributes = [

+        'javax.swing.plaf.metal.MetalLookAndFeel' : [

+            theme : { laf, theme ->

+                if (!(theme instanceof MetalTheme)) {

+                    if (theme == 'ocean') {

+                        theme = Class.forName('javax.swing.plaf.metal.OceanTheme').newInstance()

+                    } else if (theme == 'steel') {

+                        theme = new DefaultMetalTheme();

+                    } else {

+                        theme = Class.forName(theme as String).newInstance()

+                    }

+                };

+                MetalLookAndFeel.currentTheme = theme

+            },

+            boldFonts : { laf, bold -> UIManager.put('swing.boldMetal', bold as Boolean) },

+            noxp : { laf, xp -> UIManager.put('swing.noxp', bold as Boolean) },

+        ],

+        'org.jvnet.substance.SubstanceLookAndFeel' : [

+            // use setters instead of properties to get multi-dispatch

+            theme: { laf, theme -> laf.setCurrentTheme(theme) },

+            skin: { laf, skin -> laf.setSkin(skin) },

+            watermark : { laf, watermark -> laf.setCurrentWatermark(watermark) },

+        ],

+    ]

+

+    public String addLookAndFeelAttributeHandler(String className, String attr, Closure handler) {

+        Map attrs = extendedAttributes[className]

+        if (attrs == null) {

+            attrs = [:]

+            extendedAttributes[className] = attrs

+        }

+        attrs[attr] = handler

+    }

+

+

+    public boolean isLeaf() {

+        return true

+    }

+

+    public LookAndFeel lookAndFeel(Object value, Map attributes, Closure initClosure) {

+        LookAndFeel lafInstance

+        String lafClassName

+

+        if ((value instanceof Closure) && (initClosure == null)) {

+            initClosure = value

+            value = null

+        }

+        if (value == null) {

+            value = attributes.remove('lookAndFeel')

+        }

+        if (FactoryBuilderSupport.checkValueIsTypeNotString(value, 'lookAndFeel', LookAndFeel)) {

+            lafInstance = value

+            lafClassName = lafInstance.class.name

+        } else if (value != null) {

+            lafClassName = lafCodeNames[value] ?: value

+            lafInstance = (lafClassName as Class).newInstance()

+        }

+

+        // assume all configuration must be done prior to LAF being installed

+        Map possibleAttributes = extendedAttributes[lafClassName] ?: [:]

+

+        attributes.each {k, v ->

+            if (possibleAttributes[k]) {

+                possibleAttributes[k](lafInstance, v)

+            } else {

+                try {

+                    lafInstance."$k" = v

+                } catch (MissingPropertyException mpe) {

+                    throw new RuntimeException("SwingBuilder initialization for the Look and Feel Class $lafClassName does accept the attribute $k")

+                }

+            }

+        }

+

+        if (initClosure) {

+            initClosure.call(lafInstance)

+        }

+

+        UIManager.setLookAndFeel(lafInstance)

+

+        return lafInstance

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/SwingBuilder.groovy b/groovy/src/main/groovy/swing/SwingBuilder.groovy
new file mode 100644
index 0000000..7dd588d
--- /dev/null
+++ b/groovy/src/main/groovy/swing/SwingBuilder.groovy
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License")
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.swing
+
+import groovy.swing.factory.*
+import java.awt.*
+import java.lang.reflect.InvocationTargetException
+import java.util.logging.Logger
+import javax.swing.*
+import javax.swing.table.TableColumn
+import javax.swing.border.BevelBorder
+import javax.swing.border.EtchedBorder
+import org.codehaus.groovy.runtime.MethodClosure
+
+/**
+ * A helper class for creating Swing widgets using GroovyMarkup
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class SwingBuilder  extends FactoryBuilderSupport {
+
+    // Properties
+    LinkedList containingWindows = new LinkedList()
+
+    // local fields
+    private static final Logger LOG = Logger.getLogger(SwingBuilder.name)
+    // tracks all containing windows, for auto-owned dialogs
+    private boolean headless = false
+
+    public SwingBuilder() {
+        registerWidgets()
+        headless = GraphicsEnvironment.isHeadless()
+    }
+
+    protected void registerWidgets() {
+        //
+        // non-widget support classes
+        //
+        registerFactory("action", new ActionFactory())
+        registerFactory("actions", new CollectionFactory())
+        registerFactory("map", new MapFactory())
+        registerFactory("imageIcon", new ImageIconFactory())
+        registerBeanFactory("buttonGroup", ButtonGroup)
+        addAttributeDelegate(SwingBuilder.&buttonGroupAttributeDelegate)
+
+        //object id delegage, for propertyNotFound
+        addAttributeDelegate(SwingBuilder.&objectIDAttributeDelegate)
+
+        // binding related classes
+        registerFactory("bind", new BindFactory())
+        addAttributeDelegate(BindFactory.&bindingAttributeDelegate)
+        registerFactory("model", new ModelFactory())
+
+        // ulimate pass through types
+        registerFactory("widget", new WidgetFactory(Component, true))
+        registerFactory("container", new WidgetFactory(Component, false))
+        registerFactory("bean", new WidgetFactory(Object, true))
+
+
+        //
+        // standalone window classes
+        //
+        registerFactory("dialog", new DialogFactory())
+        registerBeanFactory("fileChooser", JFileChooser)
+        registerFactory("frame", new FrameFactory())
+        registerBeanFactory("optionPane", JOptionPane)
+        registerFactory("window", new WindowFactory())
+
+
+        //
+        // widgets
+        //
+        registerFactory("button", new RichActionWidgetFactory(JButton))
+        registerFactory("checkBox", new RichActionWidgetFactory(JCheckBox))
+        registerFactory("checkBoxMenuItem", new RichActionWidgetFactory(JCheckBoxMenuItem))
+        registerFactory("menuItem", new RichActionWidgetFactory(JMenuItem))
+        registerFactory("radioButton", new RichActionWidgetFactory(JRadioButton))
+        registerFactory("radioButtonMenuItem", new RichActionWidgetFactory(JRadioButtonMenuItem))
+        registerFactory("toggleButton", new RichActionWidgetFactory(JToggleButton))
+
+        registerFactory("editorPane", new TextArgWidgetFactory(JEditorPane))
+        registerFactory("label", new TextArgWidgetFactory(JLabel))
+        registerFactory("passwordField", new TextArgWidgetFactory(JPasswordField))
+        registerFactory("textArea", new TextArgWidgetFactory(JTextArea))
+        registerFactory("textField", new TextArgWidgetFactory(JTextField))
+        registerFactory("textPane", new TextArgWidgetFactory(JTextPane))
+
+        registerBeanFactory("colorChooser", JColorChooser)
+        registerFactory("comboBox", new ComboBoxFactory())
+        registerBeanFactory("desktopPane", JDesktopPane)
+        registerFactory("formattedTextField", new FormattedTextFactory())
+        registerFactory("internalFrame", new InternalFrameFactory())
+        registerBeanFactory("layeredPane", JLayeredPane)
+        registerBeanFactory("list", JList)
+        registerBeanFactory("menu", JMenu)
+        registerBeanFactory("menuBar", JMenuBar)
+        registerBeanFactory("panel", JPanel)
+        registerBeanFactory("popupMenu", JPopupMenu)
+        registerBeanFactory("progressBar", JProgressBar)
+        registerBeanFactory("scrollBar", JScrollBar)
+        registerFactory("scrollPane", new ScrollPaneFactory())
+        registerFactory("separator", new SeparatorFactory())
+        registerBeanFactory("slider", JSlider)
+        registerBeanFactory("spinner", JSpinner)
+        registerFactory("splitPane", new SplitPaneFactory())
+        registerFactory("tabbedPane", new TabbedPaneFactory(JTabbedPane))
+        registerFactory("table", new TableFactory())
+        registerBeanFactory("tableColumn", TableColumn)
+        registerBeanFactory("toolBar", JToolBar)
+        //registerBeanFactory("tooltip", JToolTip) // doesn't work, use toolTipText property
+        registerBeanFactory("tree", JTree)
+        registerBeanFactory("viewport", JViewport) // sub class?
+
+
+        //
+        // MVC models
+        //
+        registerBeanFactory("boundedRangeModel", DefaultBoundedRangeModel)
+
+        // spinner models
+        registerBeanFactory("spinnerDateModel", SpinnerDateModel)
+        registerBeanFactory("spinnerListModel", SpinnerListModel)
+        registerBeanFactory("spinnerNumberModel", SpinnerNumberModel)
+
+        // table models
+        registerFactory("tableModel", new TableModelFactory())
+        registerFactory("propertyColumn", new PropertyColumnFactory())
+        registerFactory("closureColumn", new ClosureColumnFactory())
+
+
+        //
+        // Layouts
+        //
+        registerFactory("borderLayout", new LayoutFactory(BorderLayout))
+        registerFactory("cardLayout", new LayoutFactory(CardLayout))
+        registerFactory("flowLayout", new LayoutFactory(FlowLayout))
+        registerFactory("gridBagLayout", new LayoutFactory(GridBagLayout))
+        registerFactory("gridLayout", new LayoutFactory(GridLayout))
+        registerFactory("overlayLayout", new LayoutFactory(OverlayLayout))
+        registerFactory("springLayout", new LayoutFactory(SpringLayout))
+        registerBeanFactory("gridBagConstraints", GridBagConstraints)
+        registerBeanFactory("gbc", GridBagConstraints) // shortcut name
+        // constraints delegate
+        addAttributeDelegate(SwingBuilder.&constraintsAttributeDelegate)
+
+
+        // Box layout and friends
+        registerFactory("boxLayout", new BoxLayoutFactory())
+        registerFactory("box", new BoxFactory())
+        registerFactory("hbox", new HBoxFactory())
+        registerFactory("hglue", new HGlueFactory())
+        registerFactory("hstrut", new HStrutFactory())
+        registerFactory("vbox", new VBoxFactory())
+        registerFactory("vglue", new VGlueFactory())
+        registerFactory("vstrut", new VStrutFactory())
+        registerFactory("glue", new GlueFactory())
+        registerFactory("rigidArea", new RigidAreaFactory())
+
+        // table layout
+        registerFactory("tableLayout", new TableLayoutFactory())
+        registerFactory("tr", new TRFactory())
+        registerFactory("td", new TDFactory())
+
+        //
+        // borders
+        //
+        registerFactory("lineBorder", new LineBorderFactory())
+        registerFactory("loweredBevelBorder", new BevelBorderFactory(BevelBorder.LOWERED))
+        registerFactory("raisedBevelBorder", new BevelBorderFactory(BevelBorder.RAISED))
+        registerFactory("etchedBorder", new EtchedBorderFactory(EtchedBorder.LOWERED))
+        registerFactory("loweredEtchedBorder", new EtchedBorderFactory(EtchedBorder.LOWERED))
+        registerFactory("raisedEtchedBorder", new EtchedBorderFactory(EtchedBorder.RAISED))
+        registerFactory("titledBorder", new TitledBorderFactory())
+        registerFactory("emptyBorder", new EmptyBorderFactory())
+        registerFactory("compoundBorder", new CompoundBorderFactory())
+        registerFactory("matteBorder", new MatteBorderFactory())
+
+    }
+
+    /**
+     * Do some overrides for standard component handlers, else use super
+     */
+    public void registerBeanFactory(String nodeName, Class klass) {
+        // poke at the type to see if we need special handling
+        if (LayoutManager.isAssignableFrom(klass)) {
+            registerFactory(nodeName, new LayoutFactory(klass))
+        } else if (JScrollPane.isAssignableFrom(klass)) {
+            registerFactory(nodeName, new ScrollPaneFactory(klass))
+        } else if (JTable.isAssignableFrom(klass)) {
+            registerFactory(nodeName, new TableFactory(klass))
+        } else if (JComponent.isAssignableFrom(klass)
+            || JApplet.isAssignableFrom(klass)
+            || JDialog.isAssignableFrom(klass)
+            || JFrame.isAssignableFrom(klass)
+            || JWindow.isAssignableFrom(klass)
+        ) {
+            registerFactory(nodeName, new ComponentFactory(klass))
+        } else {
+            super.registerBeanFactory(nodeName, klass)
+        }
+
+    }
+
+    public SwingBuilder edt(Closure c) {
+        c.setDelegate(this)
+        if (headless || SwingUtilities.isEventDispatchThread()) {
+            c.call(this)
+        } else {
+            try {
+                if (!(c instanceof MethodClosure)) {
+                    c = c.curry([this])
+                }
+                SwingUtilities.invokeAndWait(c)
+            } catch (InterruptedException e) {
+                throw new GroovyRuntimeException("interrupted swing interaction", e)
+            } catch (InvocationTargetException e) {
+                throw new GroovyRuntimeException("exception in event dispatch thread", e.getTargetException())
+            }
+        }
+        return this
+    }
+
+    public SwingBuilder doLater(Closure c) {
+        c.setDelegate(this)
+        if (headless) {
+            c.call()
+        } else {
+            if (!(c instanceof MethodClosure)) {
+                c = c.curry([this])
+            }
+            SwingUtilities.invokeLater(c)
+        }
+        return this
+    }
+
+    public SwingBuilder doOutside(Closure c) {
+        c.setDelegate(this)
+        if (!(c instanceof MethodClosure)) {
+            c = c.curry([this])
+        }
+        new Thread(c).start()
+        return this
+    }
+
+    public static SwingBuilder build(Closure c) {
+        SwingBuilder builder = new SwingBuilder()
+        return builder.edt(c)
+    }
+
+    public KeyStroke shortcut(key, modifier = 0) {
+        return KeyStroke.getKeyStroke(key, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask() | modifier)
+    }
+
+    public KeyStroke shortcut(String key, modifier = 0) {
+        KeyStroke ks = KeyStroke.getKeyStroke(key)
+        if (ks == null) {
+            return null
+        } else {
+            return KeyStroke.getKeyStroke(ks.getKeyCode(), ks.getModifiers() | modifier | Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())        }
+    }
+
+    public LookAndFeel lookAndFeel(Object lookAndFeel, Closure initCode = null) {
+        lookAndFeel([:], lookAndFeel, initCode)
+    }
+
+    public LookAndFeel lookAndFeel(Map attributes = [:], Object lookAndFeel = null, Closure initCode = null) {
+        // if we get rid of this warning, we can make it static.
+        if (context) {
+            LOG.warning "For best result do not call lookAndFeel when it is a child of a SwingBuidler node, initializaiton of the Look and Feel may be inconsistant."
+        }
+
+        LookAndFeelHelper.instance.lookAndFeel(lookAndFeel, attributes, initCode)
+    }
+
+    public static buttonGroupAttributeDelegate(def builder, def node, def attributes) {
+        if (attributes.containsKey("buttonGroup")) {
+            def o = attributes.get("buttonGroup")
+            if ((o instanceof ButtonGroup) && (node instanceof AbstractButton)) {
+                node.model.group = o
+                attributes.remove("buttonGroup")
+            }
+        }
+    }
+
+    public static objectIDAttributeDelegate(def builder, def node, def attributes) {
+        def theID = attributes.remove('id')
+        if (theID) {
+            builder.setVariable(theID, node)
+        }
+    }
+
+    public static constraintsAttributeDelegate(def builder, def node, def attributes) {
+        builder.context.constraints = attributes.remove('constraints')
+    }
+}
diff --git a/groovy/src/main/groovy/swing/binding/AbstractButtonProperties.java b/groovy/src/main/groovy/swing/binding/AbstractButtonProperties.java
new file mode 100644
index 0000000..997514f
--- /dev/null
+++ b/groovy/src/main/groovy/swing/binding/AbstractButtonProperties.java
@@ -0,0 +1,126 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.binding;

+

+import org.codehaus.groovy.binding.AbstractFullBinding;

+import org.codehaus.groovy.binding.FullBinding;

+import org.codehaus.groovy.binding.PropertyBinding;

+import org.codehaus.groovy.binding.SourceBinding;

+import org.codehaus.groovy.binding.TargetBinding;

+import org.codehaus.groovy.binding.TriggerBinding;

+

+import javax.swing.AbstractButton;

+import javax.swing.ButtonModel;

+import java.awt.event.ItemEvent;

+import java.awt.event.ItemListener;

+import java.beans.PropertyChangeEvent;

+import java.beans.PropertyChangeListener;

+import java.util.HashMap;

+import java.util.Map;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public class AbstractButtonProperties {

+    public static Map/*<String, TriggerBinding>*/ getSyntheticProperties() {

+        Map/*<String, TriggerBinding>*/ result = new HashMap/*<String, TriggerBinding>*/();

+        result.put(AbstractButton.class.getName() + "#selected",

+            new TriggerBinding() {

+                public FullBinding createBinding(SourceBinding source, TargetBinding target) {

+                    return new AbstractButtonSelectedBinding((PropertyBinding) source, target);

+                }

+            });

+        return result;

+    }

+}

+

+

+class AbstractButtonSelectedBinding extends AbstractFullBinding implements PropertyChangeListener, ItemListener {

+    boolean bound;

+    AbstractButton boundButton;

+

+    public AbstractButtonSelectedBinding(PropertyBinding source, TargetBinding target) {

+        bound = false;

+        setSourceBinding(source);

+        setTargetBinding(target);

+    }

+

+    public synchronized void bind() {

+        if (!bound) {

+            boundButton = (AbstractButton) ((PropertyBinding) sourceBinding).getBean();

+            try {

+                boundButton.addPropertyChangeListener("model", this);

+                boundButton.getModel().addItemListener(this);

+                bound = true;

+            } catch (RuntimeException re) {

+                try {

+                    boundButton.removePropertyChangeListener("model", this);

+                    boundButton.getModel().removeItemListener(this);

+                } catch (Exception e) {

+                    // ignore as we are re-throwing the original cause

+                }

+                throw re;

+            }

+        }

+    }

+

+    public synchronized void unbind() {

+        if (bound) {

+            bound = false;

+            // fail dirty, no checks

+            boundButton.removePropertyChangeListener("model", this);

+            boundButton.getModel().removeItemListener(this);

+            boundButton = null;

+        }

+    }

+

+    public void rebind() {

+        if (bound) {

+            unbind();

+            bind();

+        }

+    }

+

+    public void setSourceBinding(SourceBinding source) {

+        if (!(source instanceof PropertyBinding)) {

+            throw new IllegalArgumentException("Only PropertySourceBindings are accepted");

+        }

+

+        if (!"selected".equals(((PropertyBinding)source).getPropertyName())) {

+            throw new IllegalArgumentException("PropertyName must be 'selected'");

+        }

+        if (!(((PropertyBinding)source).getBean() instanceof AbstractButton)) {

+            throw new IllegalArgumentException("SourceBean must be an AbstractButton");

+        }

+        super.setSourceBinding(source);

+    }

+

+    public void setTargetBinding(TargetBinding target) {

+        super.setTargetBinding(target);

+    }

+

+    public void propertyChange(PropertyChangeEvent event) {

+        update();

+        ((ButtonModel)event.getOldValue()).removeItemListener(this);

+        ((ButtonModel)event.getNewValue()).addItemListener(this);

+    }

+

+    public void itemStateChanged(ItemEvent e) {

+        update();

+    }

+}

diff --git a/groovy/src/main/groovy/swing/binding/JSliderProperties.java b/groovy/src/main/groovy/swing/binding/JSliderProperties.java
new file mode 100644
index 0000000..4e4ff8d
--- /dev/null
+++ b/groovy/src/main/groovy/swing/binding/JSliderProperties.java
@@ -0,0 +1,127 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.binding;

+

+import org.codehaus.groovy.binding.AbstractFullBinding;

+import org.codehaus.groovy.binding.FullBinding;

+import org.codehaus.groovy.binding.PropertyBinding;

+import org.codehaus.groovy.binding.SourceBinding;

+import org.codehaus.groovy.binding.TargetBinding;

+import org.codehaus.groovy.binding.TriggerBinding;

+

+import javax.swing.BoundedRangeModel;

+import javax.swing.JSlider;

+import javax.swing.event.ChangeEvent;

+import javax.swing.event.ChangeListener;

+import java.beans.PropertyChangeEvent;

+import java.beans.PropertyChangeListener;

+import java.util.HashMap;

+import java.util.Map;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public class JSliderProperties {

+    public static Map/*<String, TriggerBinding>*/ getSyntheticProperties() {

+        Map/*<String, TriggerBinding>*/ result = new HashMap/*<String, TriggerBinding>*/();

+        result.put(JSlider.class.getName() + "#value",

+                new TriggerBinding() {

+                    public FullBinding createBinding(SourceBinding source, TargetBinding target) {

+                        return new JSliderValueBinding((PropertyBinding) source, target);

+                    }

+                });

+        return result;

+    }

+}

+

+

+class JSliderValueBinding extends AbstractFullBinding implements PropertyChangeListener, ChangeListener {

+    boolean bound;

+    JSlider boundSlider;

+

+

+    public JSliderValueBinding(PropertyBinding source, TargetBinding target) {

+        bound = false;

+        setSourceBinding(source);

+        setTargetBinding(target);

+    }

+

+    public synchronized void bind() {

+        if (!bound) {

+            boundSlider = (JSlider) ((PropertyBinding)sourceBinding).getBean();

+            try {

+                boundSlider.addPropertyChangeListener("model", this);

+                boundSlider.getModel().addChangeListener(this);

+                bound = true;

+            } catch (RuntimeException re) {

+                try {

+                    boundSlider.removePropertyChangeListener("model", this);

+                    boundSlider.getModel().removeChangeListener(this);

+                } catch (Exception e) {

+                    // ignore as we are re-throwing the original cause

+                }

+                throw re;

+            }

+        }

+    }

+

+    public synchronized void unbind() {

+        if (bound) {

+            bound = false;

+            // fail dirty, no checks

+            boundSlider.removePropertyChangeListener("model", this);

+            boundSlider.getModel().removeChangeListener(this);

+            boundSlider = null;

+        }

+    }

+

+    public void rebind() {

+        if (bound) {

+            unbind();

+            bind();

+        }

+    }

+

+    public void setSourceBinding(SourceBinding source) {

+        if (!(source instanceof PropertyBinding)) {

+            throw new IllegalArgumentException("Only PropertySourceBindings are accepted");

+        }

+

+        if (!"value".equals(((PropertyBinding) source).getPropertyName())) {

+            throw new IllegalArgumentException("PropertyName must be 'value'");

+        }

+        if (!(((PropertyBinding) source).getBean() instanceof JSlider)) {

+            throw new IllegalArgumentException("SourceBean must be an JSlider");

+        }

+        super.setSourceBinding(source);

+    }

+

+    public void setTargetBinding(TargetBinding target) {

+        super.setTargetBinding(target);

+    }

+

+    public void propertyChange(PropertyChangeEvent event) {

+        update();

+        ((BoundedRangeModel) event.getOldValue()).removeChangeListener(this);

+        ((BoundedRangeModel) event.getNewValue()).addChangeListener(this);

+    }

+

+    public void stateChanged(ChangeEvent e) {

+        update();

+    }

+}

diff --git a/groovy/src/main/groovy/swing/binding/JTextComponentProperties.java b/groovy/src/main/groovy/swing/binding/JTextComponentProperties.java
new file mode 100644
index 0000000..fed6d8d
--- /dev/null
+++ b/groovy/src/main/groovy/swing/binding/JTextComponentProperties.java
@@ -0,0 +1,137 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.binding;

+

+import org.codehaus.groovy.binding.FullBinding;

+import org.codehaus.groovy.binding.PropertyBinding;

+import org.codehaus.groovy.binding.SourceBinding;

+import org.codehaus.groovy.binding.TargetBinding;

+import org.codehaus.groovy.binding.TriggerBinding;

+import org.codehaus.groovy.binding.AbstractFullBinding;

+

+import javax.swing.event.DocumentEvent;

+import javax.swing.event.DocumentListener;

+import javax.swing.text.Document;

+import javax.swing.text.JTextComponent;

+import java.beans.PropertyChangeEvent;

+import java.beans.PropertyChangeListener;

+import java.util.HashMap;

+import java.util.Map;

+

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public class JTextComponentProperties {

+

+    public static Map/*<String, TriggerBinding>*/ getSyntheticProperties() {

+        Map/*<String, TriggerBinding>*/ result = new HashMap/*<String, TriggerBinding>*/();

+        result.put(JTextComponent.class.getName() + "#text",

+            new TriggerBinding() {

+                public FullBinding createBinding(SourceBinding source, TargetBinding target) {

+                    return new JTextComponentTextBinding((PropertyBinding) source, target);

+                }

+            });

+        return result;

+    }

+}

+

+

+class JTextComponentTextBinding extends AbstractFullBinding implements PropertyChangeListener, DocumentListener {

+    boolean bound;

+    JTextComponent boundTextComponent;

+

+    public JTextComponentTextBinding(PropertyBinding source, TargetBinding target) {

+        bound = false;

+        setSourceBinding(source);

+        setTargetBinding(target);

+    }

+

+    public synchronized void bind() {

+        if (!bound) {

+            boundTextComponent = (JTextComponent) ((PropertyBinding)sourceBinding).getBean();

+            try {

+                boundTextComponent.addPropertyChangeListener("document", this);

+                boundTextComponent.getDocument().addDocumentListener(this);

+                bound = true;

+            } catch (RuntimeException re) {

+                try {

+                    boundTextComponent.removePropertyChangeListener("document", this);

+                    boundTextComponent.getDocument().removeDocumentListener(this);

+                } catch (Exception e) {

+                    // ignore as we are re-throwing the original cause

+                }

+                throw re;

+            }

+        }

+    }

+

+    public synchronized void unbind() {

+        if (bound) {

+            bound = false;

+            // fail dirty, no checks

+            boundTextComponent.removePropertyChangeListener("document", this);

+            boundTextComponent.getDocument().removeDocumentListener(this);

+            boundTextComponent = null;

+        }

+    }

+

+    public void rebind() {

+        if (bound) {

+            unbind();

+            bind();

+        }

+    }

+

+    public void setSourceBinding(SourceBinding source) {

+        if (!(source instanceof PropertyBinding)) {

+            throw new IllegalArgumentException("Only PropertyBindings are accepted");

+        }

+

+        if (!"text".equals(((PropertyBinding)source).getPropertyName())) {

+            throw new IllegalArgumentException("PropertyName must be 'text'");

+        }

+        if (!(((PropertyBinding)source).getBean() instanceof JTextComponent)) {

+            throw new IllegalArgumentException("SourceBean must be a TextComponent");

+        }

+        super.setSourceBinding(source);

+    }

+

+    public void setTargetBinding(TargetBinding target) {

+        super.setTargetBinding(target);

+    }

+

+    public void propertyChange(PropertyChangeEvent event) {

+        update();

+        ((Document)event.getOldValue()).removeDocumentListener(this);

+        ((Document)event.getNewValue()).addDocumentListener(this);

+    }

+

+    public void changedUpdate(DocumentEvent event) {

+        update();

+    }

+

+    public void insertUpdate(DocumentEvent event) {

+        update();

+    }

+

+    public void removeUpdate(DocumentEvent event) {

+        update();

+    }

+

+}

diff --git a/groovy/src/main/groovy/swing/binding/package.html b/groovy/src/main/groovy/swing/binding/package.html
new file mode 100644
index 0000000..c172696
--- /dev/null
+++ b/groovy/src/main/groovy/swing/binding/package.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>package groovy.swing.binding.*</title>
+  </head>
+  <body>
+    <p>
+      Binding classes for SwingBuilder
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/swing/factory/ActionFactory.groovy b/groovy/src/main/groovy/swing/factory/ActionFactory.groovy
new file mode 100644
index 0000000..ccc1289
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/ActionFactory.groovy
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import groovy.swing.impl.DefaultAction
+import javax.swing.Action
+import javax.swing.KeyStroke
+import org.codehaus.groovy.runtime.InvokerHelper
+import javax.swing.JComponent
+
+/**
+ *
+ * @author Danno Ferrin
+ */
+public class ActionFactory extends AbstractFactory {
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        Action action = null;
+        if (FactoryBuilderSupport.checkValueIsTypeNotString(value, name, Action.class)) {
+            action = (Action) value;
+        } else if (attributes.get(name) instanceof Action) {
+            action = (Action) attributes.remove(name);
+        } else {
+            action = new DefaultAction();
+        }
+        return action;
+    }
+
+    public boolean onHandleNodeAttributes( FactoryBuilderSupport builder, Object action,
+            Map attributes)
+    {
+        if ((attributes.get("closure") instanceof Closure) && (action instanceof DefaultAction)){
+            Closure closure = (Closure) attributes.remove("closure");
+            ((DefaultAction)action).setClosure(closure);
+        }
+
+        Object accel = attributes.remove("accelerator");
+        if (accel != null) {
+            KeyStroke stroke = null;
+            if (accel instanceof KeyStroke) {
+                stroke = (KeyStroke) accel;
+            } else {
+                stroke = KeyStroke.getKeyStroke(accel.toString());
+            }
+            action.putValue(Action.ACCELERATOR_KEY, stroke);
+        }
+
+        Object mnemonic = attributes.remove("mnemonic");
+        if (mnemonic != null) {
+            if (!(mnemonic instanceof Number)) {
+                mnemonic = mnemonic.toString().charAt(0);
+            }
+            action.putValue(Action.MNEMONIC_KEY, mnemonic as Integer);
+        }
+        
+        for (entry in attributes.entrySet()) {
+            String propertyName = (String) entry.getKey();
+            // first attempt to set as a straight proeprty
+            try {
+                InvokerHelper.setProperty(action, propertyName, entry.getValue());
+            } catch (MissingPropertyException mpe) {
+                // failing that store them in the action values list
+                // typically standard Action names start with upper case, so lets upper case it
+                propertyName = capitalize(propertyName);
+                action.putValue(propertyName, entry.getValue());
+            }
+
+        }
+
+        return false
+    }
+
+    public void setParent(FactoryBuilderSupport builder, Object parent, Object action) {
+        try {
+            InvokerHelper.setProperty(parent, "action", action);
+        } catch (RuntimeException re) {
+            // must not have an action property...
+            // so we ignore it and go on
+        }
+        Object keyStroke = action.getValue("KeyStroke");
+        if (parent instanceof JComponent) {
+            JComponent component = (JComponent) parent;
+            KeyStroke stroke = null;
+            if (keyStroke instanceof String) {
+                stroke = KeyStroke.getKeyStroke((String) keyStroke);
+            } else if (keyStroke instanceof KeyStroke) {
+                stroke = (KeyStroke) keyStroke;
+            }
+            if (stroke != null) {
+                String key = action.toString();
+                component.getInputMap().put(stroke, key);
+                component.getActionMap().put(key, action);
+            }
+        }
+    }
+
+
+    String capitalize(String text) {
+        char ch = text.charAt(0);
+        if (Character.isUpperCase(ch)) {
+            return text;
+        }
+        StringBuffer buffer = new StringBuffer(text.length());
+        buffer.append(Character.toUpperCase(ch));
+        buffer.append(text.substring(1));
+        return buffer.toString();
+    }
+
+}
diff --git a/groovy/src/main/groovy/swing/factory/BeanFactory.groovy b/groovy/src/main/groovy/swing/factory/BeanFactory.groovy
new file mode 100644
index 0000000..cd672fe
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/BeanFactory.groovy
@@ -0,0 +1,54 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.factory

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision: 7953 $

+ * @since Groovy 1.1

+ */

+class BeanFactory extends AbstractFactory {

+    final Class beanClass

+    final protected boolean leaf

+

+    public BeanFactory(Class beanClass) {

+        this(beanClass, false)

+    }

+

+    public BeanFactory(Class beanClass, boolean leaf) {

+        this.beanClass = beanClass

+        this.leaf = leaf

+    }

+

+    public boolean isLeaf() {

+        return leaf

+    }

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {

+        if (FactoryBuilderSupport.checkValueIsTypeNotString(value, name, beanClass)) {

+            return value

+        }

+        Object bean = beanClass.newInstance()

+        if (value instanceof String) {

+            try {

+                bean.text = value

+            } catch (MissingPropertyException mpe) {

+                throw new RuntimeException("In $name value argument of type String cannot be applied to property text:");

+            }

+        }

+        return bean

+    }

+}

diff --git a/groovy/src/main/groovy/swing/factory/BevelBorderFactory.groovy b/groovy/src/main/groovy/swing/factory/BevelBorderFactory.groovy
new file mode 100644
index 0000000..ea25eaa
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/BevelBorderFactory.groovy
@@ -0,0 +1,63 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.swing.factory

+

+import javax.swing.BorderFactory

+import java.awt.Color

+

+/**

+ * accepts no value

+ * accepts attributes:<br />

+ * none <br />

+ * highlight: java.awt.Color, shadow: java.awt.Color<br />

+ * highlightOuter: java.awt.Color, highlightInner: java.awt.Color, shadowOuter: java.awt.Color, shadowInner: java.awt.Color<br />

+ *

+ */

+class BevelBorderFactory extends SwingBorderFactory {

+

+    final int type;

+

+    public BevelBorderFactory(int newType) {

+        type = newType;

+    }

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) {

+        builder.context.applyBorderToParent = attributes.remove('parent')

+

+        // no if-else-if chain so that we can have one attribute failure block

+        if (attributes.containsKey("highlight")) {

+            Color highlight = attributes.remove("highlight")

+            Color shadow = attributes.remove("shadow")

+            if (highlight && shadow && !attributes) {

+                return BorderFactory.createBevelBorder(type, highlight, shadow);

+            }

+        }

+        if (attributes.containsKey("highlightOuter")) {

+            Color highlightOuter = attributes.remove("highlightOuter")

+            Color highlightInner = attributes.remove("highlightInner")

+            Color shadowOuter = attributes.remove("shadowOuter")

+            Color shadowInner = attributes.remove("shadowInner")

+            if (highlightOuter && highlightInner && shadowOuter && shadowInner && !attributes) {

+                return BorderFactory.createBevelBorder(type, highlightOuter, highlightInner, shadowOuter, shadowInner);

+            }

+        }

+        if (attributes) {

+            throw new RuntimeException("$name only accepts no attributes, or highlight: and shadow: attributes, or highlightOuter: and highlightInner: and shadowOuter: and shadowInner: attributes")

+        }

+        return BorderFactory.createBevelBorder(type);

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/BindFactory.groovy b/groovy/src/main/groovy/swing/factory/BindFactory.groovy
new file mode 100644
index 0000000..ae2cb9e
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/BindFactory.groovy
@@ -0,0 +1,173 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.factory

+

+import groovy.swing.binding.AbstractButtonProperties

+import groovy.swing.binding.JSliderProperties

+import groovy.swing.binding.JTextComponentProperties

+

+import java.util.Map.Entry;

+

+import org.codehaus.groovy.binding.*

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public class BindFactory extends AbstractFactory {

+

+    final Map/*<String, TriggerBinding*/ syntheticBindings;

+

+    public BindFactory() {

+        syntheticBindings = new HashMap();

+

+        // covers JTextField.text

+        // covers JTextPane.text

+        // covers JTextArea.text

+        // covers JEditorPane.text

+        syntheticBindings.putAll(JTextComponentProperties.getSyntheticProperties());

+

+        // covers JCheckBox.selected

+        // covers JChecBoxMenuItem.selected

+        // covers JRadioButton.selected

+        // covers JRadioButtonMenuItem.selected

+        // covers JToggleButton.selected

+        syntheticBindings.putAll(AbstractButtonProperties.getSyntheticProperties());

+

+        // covers JSlider.value

+        syntheticBindings.putAll(JSliderProperties.getSyntheticProperties());

+

+        // JComboBox.elements

+        // JComboBox.selectedElement

+        //syntheticBindings.putAll(JComboBoxProperties.getSyntheticProperties());

+

+        // JList.elements

+        // JList.selectedElement

+        // JList.selectedElements

+        //syntheticBindings.putAll(JListProperties.getSyntheticProperties());

+

+        // JSpinner.value

+        //syntheticBindings.putAll(JSpinnerProperties.getSyntheticProperties());

+

+        // other properties handled in JSR-295

+        // JTable.elements

+        // JTable.selectedElement

+        // JTable.selectedElements

+        // JTree.root

+        // JTree.selectedElement

+        // JTree.selectedElements

+

+    }

+

+    /**

+     * Accepted Properties...

+     *

+     * group?

+     * source ((sourceProperty) | (sourceEvent sourceValue))

+     * (target targetProperty)? (? use default javabeans property if targetProperty is not present?)

+     *

+     *

+     * @param builder

+     * @param name

+     * @param value

+     * @param attributes

+     * @return the newly created instance

+     * @throws InstantiationException

+     * @throws IllegalAccessException

+     */

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {

+        if (value != null) {

+            throw new RuntimeException("$name elements do not accept a value argument.");

+        }

+        Object source = attributes.remove("source");

+        Object target = attributes.remove("target");

+

+        TargetBinding tb = null;

+        if (target != null) {

+            String targetProperty = (String) attributes.remove("targetProperty");

+            tb = new PropertyBinding(target, targetProperty);

+        }

+        FullBinding fb;

+

+        if (attributes.containsKey("sourceProperty")) {

+            // first check for synthetic properties

+            String property = (String) attributes.remove("sourceProperty");

+            PropertyBinding psb = new PropertyBinding(source, property);

+

+            TriggerBinding trigger = null;

+            Class currentClass = source.getClass();

+            while ((trigger == null) && (currentClass != null)) {

+                // should we check interfaces as well?  if so at what level?

+                trigger = (TriggerBinding) syntheticBindings.get("$currentClass.name#$property" as String);

+                currentClass = currentClass.getSuperclass();

+            }

+            if (trigger == null) {

+                //TODO inspect the bean info and throw an error if the property is not obserbable and not bind:false?

+                trigger = psb;

+            }

+            fb = trigger.createBinding(psb, tb);

+        } else if (attributes.containsKey("sourceEvent") && attributes.containsKey("sourceValue")) {

+            Closure queryValue = (Closure) attributes.remove("sourceValue");

+            ClosureSourceBinding psb = new ClosureSourceBinding(queryValue);

+            String trigger = (String) attributes.remove("sourceEvent");

+            EventTriggerBinding etb = new EventTriggerBinding(source, trigger);

+            fb = etb.createBinding(psb, tb);

+        } else {

+            throw new RuntimeException("$name does not have suffient attributes to initialize");

+        }

+

+        Object o = attributes.remove("bind");

+        if (    (o == null)

+            || ((o instanceof Boolean) && ((Boolean)o).booleanValue()))

+        {

+            fb.bind();

+        }

+        if (target != null) {

+            fb.update();

+        }

+

+        builder.addDisposalClosure(fb.&unbind)

+        return fb;

+    }

+

+    public static bindingAttributeDelegate(def builder, def node, def attributes) {

+        Iterator iter = attributes.entrySet().iterator()

+        while (iter.hasNext()) {

+            Entry entry = (Entry) iter.next()

+            String property = entry.key.toString();

+            Object value = entry.value;

+            if (value instanceof FullBinding) {

+                FullBinding fb = (FullBinding) value;

+                PropertyBinding ptb = new PropertyBinding(node, property);

+                fb.setTargetBinding(ptb);

+                try {

+                    fb.update();

+                } catch (Exception e) {

+                    // just eat it?

+                }

+                try {

+                    fb.rebind();

+                } catch (Exception e) {

+                    // just eat it?

+                }

+                // this is why we cannot use entrySet().each { }

+                iter.remove();

+            }

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/BoxFactory.groovy b/groovy/src/main/groovy/swing/factory/BoxFactory.groovy
new file mode 100644
index 0000000..b5f07d1
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/BoxFactory.groovy
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import java.awt.Dimension
+import javax.swing.Box
+import javax.swing.BoxLayout
+
+public class BoxFactory extends ComponentFactory {
+
+    public BoxFactory() {
+        super(null);
+    }
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        if (FactoryBuilderSupport.checkValueIsType(value, name, Box.class)) {
+            return value;
+        }
+        int axis = BoxLayout.X_AXIS; // default to X so it behaves like FlowLayout
+        if (attributes.containsKey("axis")) {
+            Object o = attributes.remove("axis");
+            if (o instanceof Number) {
+                axis = ((Number)o).intValue();
+            }
+        }
+        return new Box(axis);
+    }
+}
+
+public class HBoxFactory extends ComponentFactory {
+
+    public HBoxFactory() {
+        super(null);
+    }
+
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        return Box.createHorizontalBox();
+    }
+}
+
+public class HGlueFactory extends AbstractFactory {
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        return Box.createHorizontalGlue();
+    }
+}
+
+public class HStrutFactory extends AbstractFactory {
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsType(value, name, Number.class);
+        Object num;
+        if (value != null) {
+            num = value;
+        } else {
+            num = attributes.remove("width");
+        }
+        if (num instanceof Number) {
+            return Box.createHorizontalStrut(((Number) num).intValue());
+        } else {
+            return Box.createHorizontalStrut(6);
+        }
+    }
+}
+
+public class VBoxFactory extends ComponentFactory {
+
+    public VBoxFactory() {
+        super(null);
+    }
+
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        return Box.createVerticalBox();
+    }
+}
+
+public class VGlueFactory extends AbstractFactory {
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        return Box.createVerticalGlue();
+    }
+}
+
+public class VStrutFactory extends AbstractFactory {
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsType(value, name, Number.class);
+        Object num;
+        if (value != null) {
+            num = value;
+        } else {
+            num = attributes.remove("height");
+        }
+        if (num instanceof Number) {
+            return Box.createVerticalStrut(((Number) num).intValue());
+        } else {
+            return Box.createVerticalStrut(6);
+        }
+    }
+}
+
+public class GlueFactory extends AbstractFactory {
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        return Box.createGlue();
+    }
+}
+
+public class RigidAreaFactory extends AbstractFactory {
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        Dimension dim;
+        Object o = attributes.remove("size");
+        if (o instanceof Dimension) {
+            dim = (Dimension) o;
+        } else {
+            int w, h;
+            o = attributes.remove("width");
+            w = ((o instanceof Number)) ? ((Number) o).intValue() : 6;
+            o = attributes.remove("height");
+            h = ((o instanceof Number)) ? ((Number) o).intValue() : 6;
+            dim = new Dimension(w, h);
+        }
+        return Box.createRigidArea(dim);
+    }
+}
diff --git a/groovy/src/main/groovy/swing/factory/BoxLayoutFactory.groovy b/groovy/src/main/groovy/swing/factory/BoxLayoutFactory.groovy
new file mode 100644
index 0000000..ede8fcc
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/BoxLayoutFactory.groovy
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import java.awt.Container
+import javax.swing.BoxLayout
+import org.codehaus.groovy.runtime.InvokerHelper
+
+public class BoxLayoutFactory extends AbstractFactory {
+
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        Object parent = builder.getCurrent();
+        if (parent instanceof Container) {
+            Object axisObject = attributes.remove("axis");
+            int axis = BoxLayout.X_AXIS;
+            if (axisObject != null) {
+                Integer i = (Integer) axisObject;
+                axis = i.intValue();
+            }
+
+            Container target = LayoutFactory.getLayoutTarget(parent);
+            BoxLayout answer = new BoxLayout(target, axis);
+
+            // now let's try to set the layout property
+            InvokerHelper.setProperty(target, "layout", answer);
+            return answer;
+        } else {
+            throw new RuntimeException("Must be nested inside a Container");
+        }
+    }
+
+    public void setParent(FactoryBuilderSupport builder, Object parent, Object child) {
+        if (parent instanceof Container) {
+            Container target = LayoutFactory.getLayoutTarget(parent);
+            InvokerHelper.setProperty(target, "layout", child);
+        }
+    }
+
+}
diff --git a/groovy/src/main/groovy/swing/factory/CollectionFactory.groovy b/groovy/src/main/groovy/swing/factory/CollectionFactory.groovy
new file mode 100644
index 0000000..2d319e5
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/CollectionFactory.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+/**
+ * This returns a mutable java.util.Collection of some sort, to which items are added.  
+ */
+public class CollectionFactory extends AbstractFactory {
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        if (attributes.isEmpty()) {
+            return new ArrayList();
+        } else {
+            def item = attributes.entrySet().iterator().next();
+            throw new MissingPropertyException(
+                "The builder element '$name' is a collections element and accepts no attributes",
+                item.key as String, item.value as Class);
+        }
+    }
+
+    public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
+        parent.add(child)
+    }
+}
diff --git a/groovy/src/main/groovy/swing/factory/ComboBoxFactory.groovy b/groovy/src/main/groovy/swing/factory/ComboBoxFactory.groovy
new file mode 100644
index 0000000..0262615
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/ComboBoxFactory.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import javax.swing.JComboBox
+
+public class ComboBoxFactory extends AbstractFactory {
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        //TODO expand to allow the value arg to be items
+        Object items = attributes.remove("items");
+        if (items instanceof Vector) {
+            return new JComboBox((Vector) items);
+        } else if (items instanceof List) {
+            List list = (List) items;
+            return new JComboBox(list.toArray());
+        } else if (items instanceof Object[]) {
+            return new JComboBox((Object[]) items);
+        } else {
+            return new JComboBox();
+        }
+    }
+
+}
diff --git a/groovy/src/main/groovy/swing/factory/ComponentFactory.groovy b/groovy/src/main/groovy/swing/factory/ComponentFactory.groovy
new file mode 100644
index 0000000..4af50a6
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/ComponentFactory.groovy
@@ -0,0 +1,47 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.swing.factory

+

+import java.awt.Component

+import java.awt.Window

+

+class ComponentFactory extends BeanFactory {

+

+    public ComponentFactory(Class beanClass) {

+        super(beanClass)

+    }

+

+    public ComponentFactory(Class beanClass, boolean leaf) {

+        super(beanClass, leaf)

+    }

+

+    public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {

+        if (!(child instanceof Component) || (child instanceof Window)) {

+            return;

+        }

+        try {

+            def constraints = builder.context.constraints

+            if (constraints != null) {

+                parent.add(child, constraints)

+            } else {

+                parent.add(child)

+            }

+        } catch (MissingPropertyException mpe) {

+            parent.add(child)

+        }

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/CompoundBorderFactory.groovy b/groovy/src/main/groovy/swing/factory/CompoundBorderFactory.groovy
new file mode 100644
index 0000000..9781ff6
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/CompoundBorderFactory.groovy
@@ -0,0 +1,63 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.swing.factory

+

+import javax.swing.border.Border

+import javax.swing.border.CompoundBorder

+

+class CompoundBorderFactory extends SwingBorderFactory {

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) {

+        builder.context.applyBorderToParent = attributes.remove('parent')

+

+        Border border  = null

+        if (value instanceof List) {

+            switch (value.size()) {

+                case 0:

+                    throw new RuntimeException("$name does not accept an empty array as an value argument")

+                case 1:

+                    border = value[0]

+                    break

+                case 2:

+                    border = new CompoundBorder(value[0], value[1])

+                    break

+                case 3:

+                default:

+                    border = new CompoundBorder(value[0], value[1])

+                    border = value[2..-1].inject(border) {that, it -> new CompoundBorder(that, it) }

+                    break;

+            }

+        }

+

+        if (!border && attributes) {

+            if (value) {

+                throw new RuntimeException("$name only accepts an array of borders as a value argument")

+            }

+            def inner = attributes.remove("inner")

+            def outer = attributes.remove("outer")

+            if (inner instanceof Border && outer instanceof Border) {

+                border = new CompoundBorder(outer, inner)

+            }

+        }

+

+        if (!border) {

+            throw new RuntimeException("$name only accepts an array of javax.swing.border.Border or an inner: and outer: attribute")

+        }

+

+        return border

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/DialogFactory.groovy b/groovy/src/main/groovy/swing/factory/DialogFactory.groovy
new file mode 100644
index 0000000..249074e
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/DialogFactory.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import java.awt.Dialog
+import java.awt.Frame
+import javax.swing.JDialog
+
+public class DialogFactory extends RootPaneContainerFactory {
+
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        JDialog dialog
+        if (FactoryBuilderSupport.checkValueIsType(value, name, JDialog.class)) {
+            dialog = value
+        } else {
+            Object owner = attributes.remove("owner")
+            LinkedList containingWindows = builder.containingWindows
+            // if owner not explicit, use the last window type in the list
+            if ((owner == null) && !containingWindows.isEmpty()) {
+                owner = containingWindows.getLast()
+            }
+            if (owner instanceof Frame) {
+                dialog = new JDialog((Frame) owner)
+            } else if (owner instanceof Dialog) {
+                dialog = new JDialog((Dialog) owner)
+            } else {
+                dialog = new JDialog()
+            }
+        }
+
+        handleRootPaneTasks(builder, dialog, attributes)
+
+        return dialog
+    }
+
+
+}
diff --git a/groovy/src/main/groovy/swing/factory/EmptyBorderFactory.groovy b/groovy/src/main/groovy/swing/factory/EmptyBorderFactory.groovy
new file mode 100644
index 0000000..bd855fa
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/EmptyBorderFactory.groovy
@@ -0,0 +1,59 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.factory

+

+import javax.swing.BorderFactory

+

+/**

+ * accepts values in leiu of attributes:

+ *  int - all of top, left, bottom, right

+ *  [int, int, int, int] - top, left, bottom, right

+ * accepts attributes when no value present:

+ *  top: int, left: int, bottom: int, right: int

+ *

+ */

+class EmptyBorderFactory extends SwingBorderFactory {

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) {

+        builder.context.applyBorderToParent = attributes.remove('parent')

+        

+        if (!attributes) {

+            if (value instanceof Integer) {

+                return BorderFactory.createEmptyBorder(value, value, value, value)

+            } else if (value instanceof List && value.size() == 4) {

+                // we need GDK methods on Collection for boolean .any{} and boolean .all{} :(

+                boolean ints = true

+                value.each {ints = ints & it instanceof Integer}

+                if (ints) {

+                    return BorderFactory.createEmptyBorder(*value);

+                }

+            }

+            throw new RuntimeException("$name only accepts a single integer or an array of four integers as a value argument");

+        }

+        if (value == null) {

+            int top = attributes.remove("top")

+            int left = attributes.remove("left")

+            int bottom = attributes.remove("bottom")

+            int right = attributes.remove("right")

+            if ((top == null) || (top == null) || (top == null) || (top == null) || attributes) {

+                throw new RuntimeException("When $name is called it must be call with top:, left:, bottom:, right:, and no other attributes")

+            }

+            return BorderFactory.createEmptyBorder(top, left, bottom, right);

+        }

+        throw new RuntimeException("$name cannot be called with both an argulent value and attributes")

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/EtchedBorderFactory.groovy b/groovy/src/main/groovy/swing/factory/EtchedBorderFactory.groovy
new file mode 100644
index 0000000..ee6f20a
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/EtchedBorderFactory.groovy
@@ -0,0 +1,45 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.factory

+

+import java.awt.Color

+import javax.swing.BorderFactory

+

+class EtchedBorderFactory extends SwingBorderFactory {

+

+    final int type;

+

+    public EtchedBorderFactory(int newType) {

+        type = newType;

+    }

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) {

+        builder.context.applyBorderToParent = attributes.remove('parent')

+        // no if-else-if chain so that we can have one attribute failure block

+        if (attributes.containsKey("highlight")) {

+            Color highlight = attributes.remove("highlight")

+            Color shadow = attributes.remove("shadow")

+            if (highlight && shadow && !attributes) {

+                return BorderFactory.createEtchedBorder(type, highlight, shadow);

+            }

+        }

+        if (attributes) {

+            throw new RuntimeException("$name only accepts no attributes, or highlight: and shadow: attributes")

+        }

+        return BorderFactory.createEtchedBorder(type);

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/FormattedTextFactory.groovy b/groovy/src/main/groovy/swing/factory/FormattedTextFactory.groovy
new file mode 100644
index 0000000..69d7622
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/FormattedTextFactory.groovy
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import java.text.Format
+import javax.swing.JFormattedTextField
+
+public class FormattedTextFactory extends AbstractFactory {
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        //TODO expand value arg to take format
+        JFormattedTextField ftf;
+        if (attributes.containsKey("format")) {
+            ftf = new JFormattedTextField((Format) attributes.remove("format"));
+        } else if (attributes.containsKey("value")) {
+            ftf = new JFormattedTextField(attributes.remove("value"));
+        } else {
+            ftf = new JFormattedTextField();
+        }
+        return ftf;
+    }
+
+}
diff --git a/groovy/src/main/groovy/swing/factory/FrameFactory.groovy b/groovy/src/main/groovy/swing/factory/FrameFactory.groovy
new file mode 100644
index 0000000..485ce75
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/FrameFactory.groovy
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import javax.swing.JFrame
+import javax.swing.JMenuBar
+
+public class FrameFactory extends RootPaneContainerFactory {
+
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        JFrame frame
+        if (FactoryBuilderSupport.checkValueIsType(value, name, JFrame.class)) {
+            frame = value
+        } else {
+            frame = new JFrame()
+        }
+
+        handleRootPaneTasks(builder, frame, attributes)
+
+        return frame;
+    }
+
+    public void setChild(FactoryBuilderSupport build, Object parent, Object child) {
+        if (child instanceof JMenuBar) {
+            parent.JMenuBar = child
+        } else {
+            super.setChild(build, parent, child)
+        }
+    }
+
+}
diff --git a/groovy/src/main/groovy/swing/factory/ImageIconFactory.groovy b/groovy/src/main/groovy/swing/factory/ImageIconFactory.groovy
new file mode 100644
index 0000000..feeb693
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/ImageIconFactory.groovy
@@ -0,0 +1,83 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.factory

+

+import java.awt.Image

+import javax.swing.ImageIcon

+

+

+class ImageIconFactory extends AbstractFactory {

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) {

+        if (value == null) {

+            if (attributes.containsKey("image")) {

+                value = attributes.remove("image")

+                if (!(value instanceof Image)) {

+                    throw new RuntimeException("In $name image: attributes must be of type java.awt.Image")

+                }

+            } else if (attributes.containsKey("url")) {

+                value = attributes.remove("url")

+                if (!(value instanceof URL)) {

+                    throw new RuntimeException("In $name url: attributes must be of type java.net.URL")

+                }

+            } else if (attributes.containsKey("file")) {

+                value = attributes.remove("file")

+                if (value instanceof File) {

+                    value = value.toURL()

+                } else if (!(value instanceof String)) {

+                    throw new RuntimeException("In $name file: attributes must be of type java.io.File or a string")

+                }

+            }

+        }

+

+        // not else if so we can adjust for the case of file string where the file does not exist

+        def resource = null

+        if ((value == null) && (attributes.containsKey("resource"))) {

+            resource = attributes.remove('resource')

+        } else if ((value instanceof String) && !(new File(value).exists())) {

+            resource = value

+        }

+        if (resource != null) {

+            def klass = builder.context.owner

+            def origValue = value

+            if (attributes.containsKey("class")) {

+                klass = attributes.remove("class")

+            }

+            if (klass == null) {

+                klass = ImageIconFactory

+            } else if (!(klass instanceof Class)) {

+                klass = klass.class

+            }

+            // for now try URL approach.

+            // we may need to extract the byte[] for some packaging cases

+            value = klass.getResource(resource)

+            if (value == null) {

+                throw new RuntimeException("In $name the value argument '$origValue' does not refer to a file or a class resource")

+            }

+        }

+

+        if (value == null) {

+            throw new RuntimeException("$name has neither a value argument or one of image:, url:, file:, or resource:")

+        }

+        // exploit multi-dispatch in constructor calls

+        if (attributes.containsKey("description")) {

+            return new ImageIcon(value, attributes.remove("description"))

+        } else {

+            return new ImageIcon(value)

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/InternalFrameFactory.groovy b/groovy/src/main/groovy/swing/factory/InternalFrameFactory.groovy
new file mode 100644
index 0000000..9a38734
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/InternalFrameFactory.groovy
@@ -0,0 +1,31 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.swing.factory

+

+import javax.swing.JInternalFrame

+

+class InternalFrameFactory extends RootPaneContainerFactory {

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {

+        if (FactoryBuilderSupport.checkValueIsType(value, name, JInternalFrame.class)) {

+            return value;

+        }

+        JInternalFrame frame = new JInternalFrame();

+

+        return frame;

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/LayoutFactory.groovy b/groovy/src/main/groovy/swing/factory/LayoutFactory.groovy
new file mode 100644
index 0000000..93851fd
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/LayoutFactory.groovy
@@ -0,0 +1,45 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.swing.factory

+

+import java.awt.Container

+import javax.swing.RootPaneContainer

+

+class LayoutFactory extends BeanFactory {

+

+    public LayoutFactory(Class klass) {

+        super(klass)

+    }

+

+    public LayoutFactory(Class klass, boolean leaf) {

+        super(klass, leaf)

+    }

+

+    public void setParent(FactoryBuilderSupport builder, Object parent, Object child) {

+        if (parent instanceof Container) {

+            getLayoutTarget(parent).layout = child

+        }

+    }

+

+    public static Container getLayoutTarget(Container parent) {

+        if (parent instanceof RootPaneContainer) {

+            RootPaneContainer rpc = (RootPaneContainer) parent;

+            parent = rpc.getContentPane();

+        }

+        return parent;

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/LineBorderFactory.groovy b/groovy/src/main/groovy/swing/factory/LineBorderFactory.groovy
new file mode 100644
index 0000000..375a28f
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/LineBorderFactory.groovy
@@ -0,0 +1,51 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.factory

+

+import javax.swing.border.LineBorder

+

+/**

+ * accepts attributes:<br />

+ * color: java.awt.Color <br/>

+ * color: java.awt.Color, thickness: int <br/>

+ * color: java.awt.Color, thickness: int, roundedBorders: boolean <br/>

+ *

+ */

+class LineBorderFactory extends SwingBorderFactory {

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) {

+        builder.context.applyBorderToParent = attributes.remove('parent')

+        

+        def color = attributes.remove("color")

+        if (color == null) {

+            throw new RuntimeException("color: is a required attribute for $name")

+        }

+        def thickness = attributes.remove("thickness")

+        if (thickness == null) {

+            thickness = 1

+        }

+        def roundedCorners = attributes.remove("roundedCorners")

+        if (roundedCorners == null) {

+            roundedCorners = false

+        }

+        if (attributes) {

+            throw new RuntimeException("$name does not know how to handle the remaining attibutes: ${attributes.keySet()}")

+        }

+

+        return new LineBorder(color, thickness, roundedCorners);

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/MapFactory.groovy b/groovy/src/main/groovy/swing/factory/MapFactory.groovy
new file mode 100644
index 0000000..0fa72ea
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/MapFactory.groovy
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+public class MapFactory extends AbstractFactory {
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        return attributes;
+    }
+}
diff --git a/groovy/src/main/groovy/swing/factory/MatteBorderFactory.groovy b/groovy/src/main/groovy/swing/factory/MatteBorderFactory.groovy
new file mode 100644
index 0000000..175a665
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/MatteBorderFactory.groovy
@@ -0,0 +1,76 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.factory

+

+import javax.swing.BorderFactory

+

+/**

+ * matteBorder requires essentially two parameter, a mat definition and a

+ * border size definition.  The value argument can accept either of these:

+ * any of a java.awt.Color, a javax.swing.Icon, an integer, or a 4 arg

+ * integer array.  The remaining parameter must be define via attributes,

+ * either an icon: or color: attribute and a size: or top:, left:, bottom:,

+ * and right: attribute.

+ */

+class MatteBorderFactory extends SwingBorderFactory {

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) {

+        builder.context.applyBorderToParent = attributes.remove('parent')

+        

+        def matte

+        def border

+        if (attributes.containsKey('icon'))  {

+            matte = attributes.remove('icon')

+        } else if (attributes.containsKey('color')) {

+            matte = attributes.remove('color')

+        } else if (value != null) {

+            matte = value

+        } else {

+            throw new RuntimeException("$name must have a matte defined, either as a value argument or as a color: or icon: attribute");

+        }

+

+        if (attributes.containsKey('size')) {

+            border = attributes.remove('size');

+            border = [border, border, border, border]

+        } else if (attributes.containsKey('top')) {

+            def top = attributes.remove('top')

+            def left = attributes.remove('left')

+            def bottom = attributes.remove('bottom')

+            def right = attributes.remove('right')

+            if ((top == null) || (left == null) || (bottom == null) || (right == null)) {

+                throw new RuntimeException("In $name if one of top:, left:, bottom: or right: is specified all must be specified")

+            }

+            border = [top, left, bottom, right]

+        } else if (value != null) {

+            if (matte == value) {

+                throw new RuntimeException("In $name some attributes are required in addition to the value argument")

+            }

+            if (value instanceof Integer) {

+                border = [value, value, value, value]

+            } else {

+                border = value

+            }

+        }

+

+        if (attributes) {

+            throw new RuntimeException("$name only supports the attributes [ icon: | color:]  [ size: | ( top: left: bottom: right: ) }")

+        }

+

+        // spread list and milti-dispatch.  That's groovy!

+        return BorderFactory.createMatteBorder(*border, matte)

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/ModelFactory.groovy b/groovy/src/main/groovy/swing/factory/ModelFactory.groovy
new file mode 100644
index 0000000..606323b
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/ModelFactory.groovy
@@ -0,0 +1,48 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.factory

+

+import org.codehaus.groovy.binding.ModelBinding

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public class ModelFactory extends AbstractFactory {

+

+    public boolean isLeaf() {

+        return true

+    }

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {

+        if (value == null) {

+            throw new RuntimeException("$name requires a value argument.");

+        }

+        ModelBinding mb = new ModelBinding(value);

+

+        Object o = attributes.remove("bind");

+        builder.context.bind = (o instanceof Boolean) && ((Boolean) o).booleanValue()

+        return mb;

+    }

+

+    public void onNodeCompleted( FactoryBuilderSupport builder, Object parent, Object node ) {

+        if (builder.context.bind) {

+            node.bind()

+        }

+    }

+

+}

diff --git a/groovy/src/main/groovy/swing/factory/RichActionWidgetFactory.groovy b/groovy/src/main/groovy/swing/factory/RichActionWidgetFactory.groovy
new file mode 100644
index 0000000..09a592f
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/RichActionWidgetFactory.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import java.lang.reflect.Constructor
+import java.lang.reflect.InvocationTargetException
+import java.util.logging.Level
+import java.util.logging.Logger
+import javax.swing.Action
+import javax.swing.Icon
+
+/**
+ *
+ * @author shemnon
+ */
+public class RichActionWidgetFactory extends AbstractFactory {
+    static final Class[] ACTION_ARGS = [Action];
+    static final Class[] ICON_ARGS = [Icon];
+    static final Class[] STRING_ARGS = [String];
+    
+    final Constructor actionCtor;
+    final Constructor iconCtor;
+    final Constructor stringCtor;
+    final Class klass;
+    
+    public RichActionWidgetFactory(Class klass) {
+        try {
+            actionCtor = klass.getConstructor(ACTION_ARGS);
+            iconCtor = klass.getConstructor(ICON_ARGS);
+            stringCtor = klass.getConstructor(STRING_ARGS);
+            this.klass = klass;
+        } catch (NoSuchMethodException ex) {
+            Logger.getLogger("global").log(Level.INFO, null, ex);
+        } catch (SecurityException ex) {
+            Logger.getLogger("global").log(Level.SEVERE, null, ex);
+        }
+    }
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        try {
+            if (value == null) {
+                return klass.newInstance();
+            } else if (value instanceof Action) {
+                return actionCtor.newInstance(value);
+            } else if (value instanceof Icon) {
+                return iconCtor.newInstance(value);
+            } else if (value instanceof String) {
+                return stringCtor.newInstance(value);
+            } else if (klass.isAssignableFrom(value.getClass())) {
+                return value;
+            } else {
+                throw new RuntimeException("$name can only have a value argument of type javax.swing.Action, javax.swing.Icon, java.lang.String, or $klass.name");
+            }
+        } catch (IllegalArgumentException e) {
+            throw new RuntimeException("Failed to create component for '$name' reason: $e", e);
+        } catch (InvocationTargetException e) {
+            throw new RuntimeException("Failed to create component for '$name' reason: $e", e);
+        }
+    }
+
+}
diff --git a/groovy/src/main/groovy/swing/factory/RootPaneContainerFactory.groovy b/groovy/src/main/groovy/swing/factory/RootPaneContainerFactory.groovy
new file mode 100644
index 0000000..deeaaeb
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/RootPaneContainerFactory.groovy
@@ -0,0 +1,76 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.swing.factory

+

+import java.awt.Component

+import java.awt.Window

+import javax.swing.JButton

+

+abstract class RootPaneContainerFactory extends AbstractFactory {

+

+    public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {

+        if (!(child instanceof Component) || (child instanceof Window)) {

+            return;

+        }

+        try {

+            def constraints = builder.context.constraints

+            if (constraints != null) {

+                parent.contentPane.add(child, constraints)

+            } else {

+                parent.contentPane.add(child)

+            }

+        } catch (MissingPropertyException mpe) {

+            parent.contentPane.add(child)

+        }

+    }

+

+    public void handleRootPaneTasks(FactoryBuilderSupport builder, Window container, Map attributes) {

+        builder.context.defaultButtonDelegate =

+            builder.addAttributeDelegate {myBuilder, node, myAttributes ->

+                if (myAttributes.defaultButton && (node instanceof JButton)) {

+                    container.rootPane.defaultButton = node

+                    myAttributes.remove('defaultButton')

+                }

+            }

+

+        builder.containingWindows.add(container)

+

+        builder.context.pack = attributes.remove('pack')

+        builder.context.show = attributes.remove('show')

+

+        builder.addDisposalClosure(container.&dispose)

+    }

+

+    public void onNodeCompleted(FactoryBuilderSupport builder, Object parent, Object node) {

+        if (node instanceof Window) {

+            def containingWindows = builder.containingWindows

+            if (!containingWindows.empty && containingWindows.last == node) {

+                containingWindows.removeLast();

+            }

+        }

+

+        if (builder.context.pack) {

+            node.pack()

+        }

+        if (builder.context.show) {

+            node.visible = true

+        }

+

+        builder.removeAttributeDelegate(builder.context.defaultButtonDelegate)

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/ScrollPaneFactory.groovy b/groovy/src/main/groovy/swing/factory/ScrollPaneFactory.groovy
new file mode 100644
index 0000000..720e358
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/ScrollPaneFactory.groovy
@@ -0,0 +1,47 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.swing.factory

+

+import javax.swing.JScrollPane

+import javax.swing.JViewport

+import java.awt.Component

+import java.awt.Window

+

+

+class ScrollPaneFactory extends BeanFactory {

+

+    public ScrollPaneFactory() {

+        this(JScrollPane)

+    }

+

+    public ScrollPaneFactory(Class klass) {

+        super(klass, false);

+    }

+

+    public void setChild(FactoryBuilderSupport factory, Object parent, Object child) {

+        if (!(child instanceof Component) || (child instanceof Window)) {

+            return;

+        }

+        if (child instanceof JViewport) {

+            parent.setViewport(child);

+        } else {

+            parent.setViewportView(child);

+        }

+

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/SeparatorFactory.groovy b/groovy/src/main/groovy/swing/factory/SeparatorFactory.groovy
new file mode 100644
index 0000000..47de319
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/SeparatorFactory.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import javax.swing.JMenu
+import javax.swing.JPopupMenu.Separator as JPopupMenu_Separator // JetGroovy bug
+import javax.swing.JSeparator
+import javax.swing.JToolBar
+import javax.swing.JToolBar.Separator as JToolBar_Separator
+
+// JetGroovy bug
+
+public class SeparatorFactory extends AbstractFactory {
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        Object parent = builder.getCurrent();
+        if (parent instanceof JMenu) {
+            return new JPopupMenu_Separator();
+        } else if (parent instanceof JToolBar) {
+            return new JToolBar_Separator();
+        } else {
+            return new JSeparator();
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/swing/factory/SplitPaneFactory.groovy b/groovy/src/main/groovy/swing/factory/SplitPaneFactory.groovy
new file mode 100644
index 0000000..e97f64b
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/SplitPaneFactory.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import javax.swing.JSplitPane
+import java.awt.Window
+import java.awt.Component
+
+public class SplitPaneFactory extends AbstractFactory {
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        if (FactoryBuilderSupport.checkValueIsType(value, name, JSplitPane.class)) {
+            return value;
+        }
+        JSplitPane answer = new JSplitPane();
+        answer.setLeftComponent(null);
+        answer.setRightComponent(null);
+        answer.setTopComponent(null);
+        answer.setBottomComponent(null);
+        return answer;
+    }
+
+
+    public void setChild(FactoryBuilderSupport factory, Object parent, Object child) {
+        if (!(child instanceof Component) || (child instanceof Window)) {
+            return;
+        }
+        if (parent.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) {
+            if (parent.getTopComponent() == null) {
+                parent.setTopComponent(child);
+            } else {
+                parent.setBottomComponent(child);
+            }
+        } else {
+            if (parent.getLeftComponent() == null) {
+                parent.setLeftComponent(child);
+            } else {
+                parent.setRightComponent(child);
+            }
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/swing/factory/SwingBorderFactory.groovy b/groovy/src/main/groovy/swing/factory/SwingBorderFactory.groovy
new file mode 100644
index 0000000..68c0995
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/SwingBorderFactory.groovy
@@ -0,0 +1,46 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.factory

+

+import javax.swing.RootPaneContainer

+import javax.swing.JComponent

+

+abstract class SwingBorderFactory extends AbstractFactory {

+

+    public boolean isLeaf() {

+        // no children

+        return true;

+    }

+

+    public boolean onHandleNodeAttributes(FactoryBuilderSupport builder, Object node, Map attributes) {

+        // never do bean apply

+        return false;

+    }

+

+    public void setParent(FactoryBuilderSupport builder, Object parent, Object child) {

+        if (builder.context.applyBorderToParent) {

+            if (parent instanceof JComponent) {

+                parent.setBorder(child);

+            } else if (parent instanceof RootPaneContainer) {

+                setParent(builder, parent.contentPane, child)

+            } else {

+                throw new RuntimeException("Border cannot be applied to parent, it is neither a JComponent or a RootPaneContainer")

+            }

+        }

+    }

+

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/TabbedPaneFactory.groovy b/groovy/src/main/groovy/swing/factory/TabbedPaneFactory.groovy
new file mode 100644
index 0000000..0094c96
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/TabbedPaneFactory.groovy
@@ -0,0 +1,103 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.swing.factory

+

+import java.awt.Component

+import java.awt.Window

+import javax.swing.JTabbedPane

+

+class TabbedPaneFactory extends BeanFactory {

+

+    public TabbedPaneFactory(Class beanClass) {

+        super(beanClass, false)

+    }

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {

+        builder.context.tabbedPaneFactoryClosure = this.&inspectChild

+        builder.addAttributeDelegate(builder.context.tabbedPaneFactoryClosure)

+        builder.context.selectedIndex = attributes.remove('selectedIndex')

+        builder.context.selectedComponent = attributes.remove('selectedComponent')

+        return super.newInstance(builder, name, value, attributes)

+    }

+

+    public static void inspectChild(FactoryBuilderSupport builder, Object node, Map attributes) {

+        if (builder.current instanceof JTabbedPane) {

+            def name = attributes.remove('title')

+            def icon = attributes.remove('tabIcon')

+            def disabledIcon = attributes.remove('tabDisabledIcon')

+            def toolTip = attributes.remove('tabToolTip')

+            def background = attributes.remove('tabBackground')

+            def foreground = attributes.remove('tabForeground')

+            def enabled = attributes.remove('tabEnabled')

+            def mnemonic = attributes.remove('tabMnemonic')

+            def displayedMnemonicIndex = attributes.remove('tabDisplayedMnemonicIndex')

+            builder.context.put(node, [name, icon, disabledIcon, toolTip, background, foreground, enabled, mnemonic, displayedMnemonicIndex])

+        }

+    }

+

+    public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {

+        if (!(child instanceof Component) || (child instanceof Window)) {

+            return;

+        }

+        try {

+            def title = builder.context[child] ?: [null, null, null, null, null, null, null, null, null]

+            if (title[0] == null) {

+                title[0] = child.name

+            }

+            parent.addTab(title[0], title[1], child, title[3])

+            int index = parent.indexOfComponent(child)

+            if (title[2]) {

+                parent.setDisabledIconAt(index, title[2])

+            }

+            if (title[4]) {

+                parent.setBackgroundAt(index, title[4])

+            }

+            if (title[5]) {

+                parent.setForegroundAt(index, title[5])

+            }

+            if (title[6] != null) {

+                parent.setEnabledAt(index, title[6])

+            }

+            if (title[7]) {

+                def mnemonic = title[7]

+                if (mnemonic instanceof String) {

+                    parent.setMnemonicAt(index, mnemonic.charAt(0) as int)

+                } else {

+                    parent.setMnemonicAt(index, mnemonic as int)

+                } 

+            }

+            if (title[8]) {

+                parent.setDisplayedMnemonicIndexAt(index, title[8])

+            }

+        } catch (MissingPropertyException mpe) {

+            parent.add(child)

+        }

+    }

+

+    public void onNodeCompleted( FactoryBuilderSupport builder, Object parent, Object node ) {

+        super.onNodeCompleted (builder, parent, node)

+        builder.removeAttributeDelegate(builder.context.tabbedPaneFactoryClosure)

+        if (builder.context.selectedComponent != null) {

+            node.selectedComponent = builder.context.selectedComponent

+        }

+        if (builder.context.selectedIndex != null) {

+            node.selectedIndex = builder.context.selectedIndex

+        }

+    }

+

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/TableFactory.groovy b/groovy/src/main/groovy/swing/factory/TableFactory.groovy
new file mode 100644
index 0000000..e39e9d2
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/TableFactory.groovy
@@ -0,0 +1,41 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.swing.factory

+

+import javax.swing.JTable

+import javax.swing.table.TableColumn

+import javax.swing.table.TableModel

+

+class TableFactory extends BeanFactory {

+

+    public TableFactory() {

+        this(JTable)

+    }

+

+    public TableFactory(Class klass) {

+        super(klass, false)

+    }

+

+    public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {

+        if (child instanceof TableColumn) {

+            parent.addColumn(child);

+        } else if (child instanceof TableModel) {

+            parent.model = child;

+        }

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/swing/factory/TableLayoutFactory.groovy b/groovy/src/main/groovy/swing/factory/TableLayoutFactory.groovy
new file mode 100644
index 0000000..720fa53
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/TableLayoutFactory.groovy
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import groovy.swing.impl.TableLayout
+import groovy.swing.impl.TableLayoutCell
+import groovy.swing.impl.TableLayoutRow
+import java.awt.Component
+import java.awt.Window
+
+public class TableLayoutFactory extends AbstractFactory {
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        if (FactoryBuilderSupport.checkValueIsType(value, name, TableLayout.class)) {
+            return value;
+        }
+        return new TableLayout();
+    }
+
+    public void setParent(FactoryBuilderSupport builder, Object parent, Object child) {
+        if (builder.getParentFactory()) {
+            builder.getParentFactory().setChild (builder, parent, child.getComponent());
+        }
+    }
+}
+    
+public class TRFactory extends AbstractFactory {
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        //TODO we could make the value arg the parent
+        Object parent = builder.getCurrent();
+        if (parent instanceof TableLayout) {
+            return new TableLayoutRow((TableLayout) parent);
+        } else {
+            throw new RuntimeException("'tr' must be within a 'tableLayout'");
+        }
+    }
+
+    public void onNodeCompleted(FactoryBuilderSupport builder, Object parent, Object node) {
+        node.start()
+    }
+}
+
+public class TDFactory extends AbstractFactory {
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        //TODO we could make the value arg the TR
+        Object parent = builder.getCurrent();
+        if (parent instanceof TableLayoutRow) {
+            return new TableLayoutCell((TableLayoutRow) parent);
+        } else {
+            throw new RuntimeException("'td' must be within a 'tr'");
+        }
+    }
+
+    public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
+        if (!(child instanceof Component) || (child instanceof Window)) {
+            return;
+        }
+        parent.addComponent(child)
+    }
+}
diff --git a/groovy/src/main/groovy/swing/factory/TableModelFactory.groovy b/groovy/src/main/groovy/swing/factory/TableModelFactory.groovy
new file mode 100644
index 0000000..4d82b11
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/TableModelFactory.groovy
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import groovy.model.DefaultTableModel
+import groovy.model.ValueHolder
+import groovy.model.ValueModel
+import javax.swing.table.TableModel
+import javax.swing.JTable
+
+public class TableModelFactory extends AbstractFactory {
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        if (FactoryBuilderSupport.checkValueIsType(value, name, TableModel.class)) {
+            return value;
+        } else if (attributes.get(name) instanceof TableModel) {
+            return attributes.remove(name);
+        } else {
+            ValueModel model = (ValueModel) attributes.remove("model");
+            if (model == null) {
+                Object list = attributes.remove("list");
+                if (list == null) {
+                    list = new ArrayList();
+                }
+                model = new ValueHolder(list);
+            }
+            return new DefaultTableModel(model);
+        }
+    }
+
+    public void onNodeCompleted(FactoryBuilderSupport builder, Object parent, Object node) {
+        if ((node.columnCount > 0)
+            && (parent instanceof JTable))
+        {
+            parent.columnModel = node.columnModel
+            parent.autoCreateColumnsFromModel = false;
+        }
+    }
+}
+
+public class PropertyColumnFactory extends AbstractFactory {
+
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        Object current = builder.getCurrent();
+        if (current instanceof DefaultTableModel) {
+            DefaultTableModel model = (DefaultTableModel) current;
+            String property = (String) attributes.remove("propertyName");
+            if (property == null) {
+                throw new IllegalArgumentException("Must specify a property for a propertyColumn");
+            }
+            Object header = attributes.remove("header");
+            if (header == null) {
+                header = "";
+            }
+            Class type = (Class) attributes.remove("type");
+            if (type == null) {
+                type = Object.class;
+            }
+            Boolean editable = (Boolean) attributes.remove("editable");
+            if (editable == null) {
+                editable = Boolean.TRUE;
+            }
+            return model.addPropertyColumn(header, property, type, editable.booleanValue());
+        } else {
+            throw new RuntimeException("propertyColumn must be a child of a tableModel");
+        }
+    }
+}
+
+public class ClosureColumnFactory extends AbstractFactory {
+
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        FactoryBuilderSupport.checkValueIsNull(value, name);
+        Object current = builder.getCurrent();
+        if (current instanceof DefaultTableModel) {
+            DefaultTableModel model = (DefaultTableModel) current;
+            Object header = attributes.remove("header");
+            if (header == null) {
+                header = "";
+            }
+            Closure readClosure = (Closure) attributes.remove("read");
+            if (readClosure == null) {
+                throw new IllegalArgumentException("Must specify 'read' Closure property for a closureColumn");
+            }
+            Closure writeClosure = (Closure) attributes.remove("write");
+            Class type = (Class) attributes.remove("type");
+            if (type == null) {
+                type = Object.class;
+            }
+            return model.addClosureColumn(header, readClosure, writeClosure, type);
+        } else {
+            throw new RuntimeException("closureColumn must be a child of a tableModel");
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/swing/factory/TextArgWidgetFactory.groovy b/groovy/src/main/groovy/swing/factory/TextArgWidgetFactory.groovy
new file mode 100644
index 0000000..db02b4f
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/TextArgWidgetFactory.groovy
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import org.codehaus.groovy.runtime.InvokerHelper
+
+/**
+ *
+ * @author Danno Ferrin
+ */
+public class TextArgWidgetFactory extends AbstractFactory {
+    
+    final Class klass;
+    
+    public TextArgWidgetFactory(Class klass) {
+        this.klass = klass;
+    }
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        if (FactoryBuilderSupport.checkValueIsTypeNotString(value, name, klass)) {
+            return value;
+        }
+        
+        Object widget = klass.newInstance();
+        
+        if (value instanceof String) {
+            // this does not create property setting order issues, since the value arg preceeds all attributes in the builder element
+            InvokerHelper.setProperty(widget, "text", value);
+        }
+        
+        return widget;
+    }    
+
+}
diff --git a/groovy/src/main/groovy/swing/factory/TitledBorderFactory.groovy b/groovy/src/main/groovy/swing/factory/TitledBorderFactory.groovy
new file mode 100644
index 0000000..fc31bdb
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/TitledBorderFactory.groovy
@@ -0,0 +1,89 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.swing.factory

+

+import javax.swing.border.TitledBorder

+import java.awt.Color

+import javax.swing.border.Border

+import java.awt.Font

+

+

+/**

+ * The node must be called with either a value arugment or a title: attribute. <br />

+ * The following attributes are optional. <br />

+ *    position: one of "default", "aboveTop", "top", "belowTop", "aboveBottom", "bottom", "belowBottom", or a constant from javax.swing.border.TitledBorder

+ *    justification: one of "default", "left", "center", "right", "leading", "trailing", or a constant from javax.swing.border.TitledBorder

+ *    border: javax.swing.Border, some other border, if unset the look and feel default will be used (re

+ *    color: java.awt.Color the color of the text for the title

+ *    font: java.awt.Font the font of the text for the title

+ */

+class TitledBorderFactory extends SwingBorderFactory {

+

+    static final Map positions = [

+        'default':    TitledBorder.DEFAULT_POSITION,

+        aboveTop:    TitledBorder.ABOVE_TOP,

+        top:          TitledBorder.TOP,

+        belowTop:    TitledBorder.BELOW_TOP,

+        aboveBottom: TitledBorder.ABOVE_BOTTOM,

+        bottom:       TitledBorder.BOTTOM,

+        belowBottom: TitledBorder.BELOW_BOTTOM,

+    ]

+

+    static final Map justifications = [

+        'default': TitledBorder.DEFAULT_JUSTIFICATION,

+        left:      TitledBorder.LEFT,

+        center:    TitledBorder.CENTER,

+        right:     TitledBorder.RIGHT,

+        leading:   TitledBorder.LEADING,

+        trailing:  TitledBorder.TRAILING,

+    ]

+

+

+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) {

+        builder.context.applyBorderToParent = attributes.remove('parent')

+        

+        String title

+        if (value) {

+            title = value as String

+            if (attributes.containsKey("title")) {

+                throw new RuntimeException("$name cannot have both a value attribute and an attribute title:")

+            }

+        } else {

+            title = attributes.remove("title") as String

+        }

+        TitledBorder border = new TitledBorder(title)

+

+        def position = attributes.remove("position")

+        position = positions[position] ?: position

+        if (position instanceof Integer) { border.setTitlePosition(position) }

+

+        def justification = attributes.remove("justification")

+        justification = positions[justification] ?: justification

+        if (justification instanceof Integer) { border.setTitleJustification(justification) }

+

+        Border otherBorder = attributes.remove("border")

+        if (otherBorder != null) { border.setBorder(otherBorder) }

+

+        Color color = attributes.remove("color")

+        if (color) { border.setTitleColor(color) }

+

+        Font font = attributes.remove("font")

+        if (font) { border.setTitleFont(font) }

+

+        return border

+    }

+

+}

diff --git a/groovy/src/main/groovy/swing/factory/WidgetFactory.groovy b/groovy/src/main/groovy/swing/factory/WidgetFactory.groovy
new file mode 100644
index 0000000..b0bf5ef
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/WidgetFactory.groovy
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import java.awt.Component
+import java.awt.Window
+
+public class WidgetFactory extends AbstractFactory {
+
+    final Class restrictedType;
+    protected final boolean leaf
+
+    public WidgetFactory(Class restrictedType, boolean leaf) {
+        this.restrictedType = restrictedType
+        this.leaf = leaf
+    }
+
+    boolean isLeaf() {
+        return leaf
+    }
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        if (value == null) {
+            value = attributes.remove(name);
+        }
+        if ((value != null) && FactoryBuilderSupport.checkValueIsType(value, name, restrictedType)) {
+            return value;
+        } else {
+            throw new RuntimeException("$name must have either a value argument or an attribute named $name that must be of type $restrictedType.name");
+        }
+    }
+
+    public void setChild(FactoryBuilderSupport builder, Object parent, Object child) {
+        if (!(child instanceof Component) || (child instanceof Window)) {
+            return;
+        }
+        try {
+            def constraints = builder.context.constraints
+            if (constraints != null) {
+                parent.add(child, constraints)
+            } else {
+                parent.add(child)
+            }
+        } catch (MissingPropertyException mpe) {
+            parent.add(child)
+        }
+    }
+
+}
diff --git a/groovy/src/main/groovy/swing/factory/WindowFactory.groovy b/groovy/src/main/groovy/swing/factory/WindowFactory.groovy
new file mode 100644
index 0000000..c92e11d
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/WindowFactory.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.swing.factory
+
+import java.awt.Dialog
+import java.awt.Frame
+import javax.swing.JWindow
+
+public class WindowFactory extends RootPaneContainerFactory {
+    
+    public Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes) throws InstantiationException, IllegalAccessException {
+        JWindow window;
+        if (FactoryBuilderSupport.checkValueIsType(value, name, JWindow.class)) {
+            window = value
+        } else {
+            LinkedList containingWindows = builder.containingWindows;
+            Object owner = attributes.remove("owner");
+            // if owner not explicit, use the last window type in the list
+            if ((owner == null) && !containingWindows.empty) {
+                owner = containingWindows.last;
+            }
+            if (owner) {
+                // the joys of the MOP!
+                window = new JWindow(owner);
+            } else {
+                window = new JWindow();
+            }
+        }
+
+        handleRootPaneTasks(builder, window, attributes)
+
+        return window;
+    }
+
+}
diff --git a/groovy/src/main/groovy/swing/factory/package.html b/groovy/src/main/groovy/swing/factory/package.html
new file mode 100644
index 0000000..c7106e1
--- /dev/null
+++ b/groovy/src/main/groovy/swing/factory/package.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>package groovy.swing.factory.*</title>
+  </head>
+  <body>
+    <p>
+      SwingBuilder helper classes for creating components
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/swing/impl/ComponentFacade.java b/groovy/src/main/groovy/swing/impl/ComponentFacade.java
new file mode 100644
index 0000000..6275a69
--- /dev/null
+++ b/groovy/src/main/groovy/swing/impl/ComponentFacade.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.swing.impl;
+
+import java.awt.Component;
+
+/** 
+ * A facade to an object which contains a component.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public interface ComponentFacade {
+    Component getComponent();
+}
diff --git a/groovy/src/main/groovy/swing/impl/ContainerFacade.java b/groovy/src/main/groovy/swing/impl/ContainerFacade.java
new file mode 100644
index 0000000..53c46e1
--- /dev/null
+++ b/groovy/src/main/groovy/swing/impl/ContainerFacade.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.swing.impl;
+
+import java.awt.Component;
+
+/** 
+ * A facade to an object to which components can be added.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public interface ContainerFacade {
+    void addComponent(Component component);
+}
diff --git a/groovy/src/main/groovy/swing/impl/DefaultAction.java b/groovy/src/main/groovy/swing/impl/DefaultAction.java
new file mode 100644
index 0000000..49454e6
--- /dev/null
+++ b/groovy/src/main/groovy/swing/impl/DefaultAction.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.swing.impl;
+
+import groovy.lang.Closure;
+
+import java.awt.event.ActionEvent;
+
+import javax.swing.AbstractAction;
+
+/** 
+ * A default action implementation
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DefaultAction extends AbstractAction {
+
+    private Closure closure;
+
+    public void actionPerformed(ActionEvent event) {
+        if (closure == null) {
+            throw new NullPointerException("No closure has been configured for this Action");
+        }
+        closure.call(event);
+    }
+
+    public Closure getClosure() {
+        return closure;
+    }
+
+    public void setClosure(Closure closure) {
+        this.closure = closure;
+    }
+
+}
diff --git a/groovy/src/main/groovy/swing/impl/Startable.java b/groovy/src/main/groovy/swing/impl/Startable.java
new file mode 100644
index 0000000..2cc90a1
--- /dev/null
+++ b/groovy/src/main/groovy/swing/impl/Startable.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.swing.impl;
+
+/** 
+ * A simple lifecycle method called when an object is fully constructed.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public interface Startable {
+    void start();
+}
diff --git a/groovy/src/main/groovy/swing/impl/TableLayout.java b/groovy/src/main/groovy/swing/impl/TableLayout.java
new file mode 100644
index 0000000..98430b2
--- /dev/null
+++ b/groovy/src/main/groovy/swing/impl/TableLayout.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.swing.impl;
+
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.LayoutManager;
+
+import javax.swing.JPanel;
+
+/** 
+ * Represents a HTML style table layout
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TableLayout implements ComponentFacade {
+
+    private JPanel panel = new JPanel();
+    private int rowCount;
+    private int cellpadding;
+
+    public TableLayout() {
+        panel.setLayout(createLayoutManager());
+    }
+
+    public Component getComponent() {
+        return panel;
+    }
+    
+    public int getCellpadding() {
+        return cellpadding;
+    }
+
+    public void setCellpadding(int cellpadding) {
+        this.cellpadding = cellpadding;
+    }
+
+    /**
+     * Adds a new cell to the current grid
+     */
+    public void addCell(TableLayoutCell cell) {
+        GridBagConstraints constraints = cell.getConstraints();
+        constraints.insets = new Insets(cellpadding, cellpadding, cellpadding, cellpadding);
+        panel.add(cell.getComponent(), constraints);
+    }
+
+    /**
+     * Creates a new row index for child <tr> tags 
+     */
+    public int nextRowIndex() {
+        return rowCount++;
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------                    
+
+    /**
+     * Creates a GridBagLayout
+     */
+    protected LayoutManager createLayoutManager() {
+        return new GridBagLayout();
+    }
+}
diff --git a/groovy/src/main/groovy/swing/impl/TableLayoutCell.java b/groovy/src/main/groovy/swing/impl/TableLayoutCell.java
new file mode 100644
index 0000000..09c9ae7
--- /dev/null
+++ b/groovy/src/main/groovy/swing/impl/TableLayoutCell.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.swing.impl;
+
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/** 
+ * Represents a cell in a table layout.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TableLayoutCell implements ContainerFacade {
+
+    protected static final Logger LOG = Logger.getLogger(TableLayoutCell.class.getName());
+    
+    private TableLayoutRow parent;
+    private Component component;
+    private GridBagConstraints constraints;
+    private String align;
+    private String valign;
+
+    public int getColspan() {
+        return colspan;
+    }
+
+    public int getRowspan() {
+        return rowspan;
+    }
+
+    private int colspan = 1;
+    private int rowspan = 1;
+    private boolean colfill;
+    private boolean rowfill;
+
+        
+    public TableLayoutCell(TableLayoutRow parent) {
+        this.parent = parent;
+    }
+
+    public void addComponent(Component component)  {
+        if (this.component != null) {
+            LOG.log(Level.WARNING, "This td cell already has a component: " + component);
+        }
+        this.component = component;
+        parent.addCell(this);
+    }
+    
+    public Component getComponent() {
+        return component;
+    }
+
+    /**
+     * Sets the horizontal alignment to a case insensitive value of {LEFT, CENTER, RIGHT}
+     */
+    public void setAlign(String align) {
+        this.align = align;
+    }
+
+    /**
+     * Sets the vertical alignment to a case insensitive value of {TOP, MIDDLE, BOTTOM}
+     */
+    public void setValign(String valign) {
+        this.valign = valign;
+    }
+
+    
+    /**
+     * Sets the number of columns that this cell should span. The default value is 1
+     */
+    public void setColspan(int colspan) {
+        this.colspan = colspan;
+    }
+
+    /**
+     * Sets the number of rows that this cell should span. The default value is 1
+     */
+    public void setRowspan(int rowspan) {
+        this.rowspan = rowspan;
+    }
+
+    /**
+     * Returns the colfill.
+     * @return boolean
+     */
+    public boolean isColfill() {
+        return colfill;
+    }
+
+    /**
+     * Returns the rowfill.
+     * @return boolean
+     */
+    public boolean isRowfill() {
+        return rowfill;
+    }
+
+    /**
+     * Sets whether or not this column should allow its component to stretch to fill the space available
+     */
+    public void setColfill(boolean colfill) {
+        this.colfill = colfill;
+    }
+
+    /**
+     * Sets whether or not this row should allow its component to stretch to fill the space available
+     */
+    public void setRowfill(boolean rowfill) {
+        this.rowfill = rowfill;
+    }
+
+
+    /**
+     * @return the constraints of this cell
+     */
+    public GridBagConstraints getConstraints() {
+        if (constraints == null) {
+            constraints = createConstraints();
+        }
+        return constraints;
+    }
+    
+    // Implementation methods
+    //-------------------------------------------------------------------------                    
+    
+    protected GridBagConstraints createConstraints() {
+        GridBagConstraints answer = new GridBagConstraints();
+        answer.anchor = getAnchor();
+        if (colspan < 1) {
+            colspan = 1;
+        }
+        if (rowspan < 1) {
+            rowspan = 1;
+        }
+        if (isColfill())  {
+            answer.fill = isRowfill()
+                ? GridBagConstraints.BOTH 
+                : GridBagConstraints.HORIZONTAL;
+        }
+        else {
+            answer.fill = isRowfill()
+                ? GridBagConstraints.VERTICAL 
+                : GridBagConstraints.NONE;
+        }
+        answer.weightx = 0.2;
+        answer.weighty = 0;
+        answer.gridwidth = colspan;
+        answer.gridheight = rowspan;
+        return answer;
+    }
+    
+    /**
+     * @return the GridBagConstraints enumeration for anchor
+     */
+    protected int getAnchor() {
+        boolean isTop = "top".equalsIgnoreCase(valign);
+        boolean isBottom = "bottom".equalsIgnoreCase(valign);
+        
+        if ("center".equalsIgnoreCase(align)) {
+            if (isTop) {
+                return GridBagConstraints.NORTH;
+            }
+            else if (isBottom) {
+                return GridBagConstraints.SOUTH;
+            }
+            else {
+                return GridBagConstraints.CENTER;
+            }
+        }
+        else if ("right".equalsIgnoreCase(align)) {
+            if (isTop) {
+                return GridBagConstraints.NORTHEAST;
+            }
+            else if (isBottom) {
+                return GridBagConstraints.SOUTHEAST;
+            }
+            else {
+                return GridBagConstraints.EAST;
+            }
+        }
+        else {
+            // defaults to left
+            if (isTop) {
+                return GridBagConstraints.NORTHWEST;
+            }
+            else if (isBottom) {
+                return GridBagConstraints.SOUTHWEST;
+            }
+            else {
+                return GridBagConstraints.WEST;
+            }
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/swing/impl/TableLayoutRow.java b/groovy/src/main/groovy/swing/impl/TableLayoutRow.java
new file mode 100644
index 0000000..84cbf57
--- /dev/null
+++ b/groovy/src/main/groovy/swing/impl/TableLayoutRow.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.swing.impl;
+
+import java.awt.GridBagConstraints;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/** 
+ * Represents a row in a table layout
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TableLayoutRow implements Startable {
+
+    private final TableLayout parent;
+    private final List cells = new ArrayList();
+    private int rowIndex;
+    
+    public TableLayoutRow(TableLayout tableLayoutTag) {
+        this.parent = tableLayoutTag;
+    }
+
+    /**
+     * Adds a new cell to this row
+     */
+    public void addCell(TableLayoutCell tag) {
+        int gridx = 0;
+        for (Iterator iter = cells.iterator(); iter.hasNext(); ) {
+            TableLayoutCell cell = (TableLayoutCell) iter.next();
+            gridx += cell.getColspan();
+        }
+        tag.getConstraints().gridx = gridx;
+        cells.add(tag);
+    }
+    
+    public void start() {
+        rowIndex = parent.nextRowIndex();
+
+        // iterate through the rows and add each one to the layout...
+        for (Iterator iter = cells.iterator(); iter.hasNext(); ) {
+            TableLayoutCell cell = (TableLayoutCell) iter.next();
+            GridBagConstraints c = cell.getConstraints();
+            c.gridy = rowIndex;
+            // add the cell to the table
+            parent.addCell(cell);
+        }        
+    }
+
+    /**
+     * @return the row index of this row
+     */
+    public int getRowIndex() {
+        return rowIndex;
+    }
+
+}
diff --git a/groovy/src/main/groovy/swing/impl/package.html b/groovy/src/main/groovy/swing/impl/package.html
new file mode 100644
index 0000000..897df7f
--- /dev/null
+++ b/groovy/src/main/groovy/swing/impl/package.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>package groovy.swing.impl.*</title>
+  </head>
+  <body>
+    <p>
+      Implementation classes for SwingBuilder
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/swing/package.html b/groovy/src/main/groovy/swing/package.html
new file mode 100644
index 0000000..451766f
--- /dev/null
+++ b/groovy/src/main/groovy/swing/package.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>package groovy.swing.*</title>
+  </head>
+  <body>
+    <p>
+      A GroovyMarkup builder for creating Swing user interfaces
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/text/GStringTemplateEngine.java b/groovy/src/main/groovy/text/GStringTemplateEngine.java
new file mode 100644
index 0000000..ebf60e7
--- /dev/null
+++ b/groovy/src/main/groovy/text/GStringTemplateEngine.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.text;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyCodeSource;
+import groovy.lang.GroovyObject;
+import groovy.lang.Writable;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Map;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+
+/**
+ * @author tug@wilson.co.uk
+ * @author Paul King
+ */
+public class GStringTemplateEngine extends TemplateEngine {
+    private final ClassLoader parentLoader;
+
+    public GStringTemplateEngine() {
+        this(GStringTemplate.class.getClassLoader());
+    }
+
+    public GStringTemplateEngine(ClassLoader parentLoader) {
+        this.parentLoader = parentLoader;
+    }
+
+    /* (non-Javadoc)
+    * @see groovy.text.TemplateEngine#createTemplate(java.io.Reader)
+    */
+    public Template createTemplate(final Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
+        return new GStringTemplate(reader, parentLoader);
+    }
+
+    private static class GStringTemplate implements Template {
+        final Closure template;
+
+        /**
+         * Turn the template into a writable Closure
+         * When executed the closure evaluates all the code embedded in the
+         * template and then writes a GString containing the fixed and variable items
+         * to the writer passed as a parameter
+         * <p/>
+         * For example:
+         * <p/>
+         * '<%= "test" %> of expr and <% test = 1 %>${test} script.'
+         * <p/>
+         * would compile into:
+         * <p/>
+         * { out -> out << "${"test"} of expr and "; test = 1 ; out << "${test} script."}.asWritable()
+         *
+         * @param reader
+         * @param parentLoader
+         * @throws CompilationFailedException
+         * @throws ClassNotFoundException
+         * @throws IOException
+         */
+        GStringTemplate(final Reader reader, final ClassLoader parentLoader) throws CompilationFailedException, ClassNotFoundException, IOException {
+            final StringBuffer templateExpressions = new StringBuffer("package groovy.tmp.templates\n def getTemplate() { return { out -> delegate = new Binding(delegate); out << \"\"\"");
+            boolean writingString = true;
+
+            while (true) {
+                int c = reader.read();
+                if (c == -1) break;
+                if (c == '<') {
+                    c = reader.read();
+                    if (c == '%') {
+                        c = reader.read();
+                        if (c == '=') {
+                            parseExpression(reader, writingString, templateExpressions);
+                            writingString = true;
+                            continue;
+                        } else {
+                            parseSection(c, reader, writingString, templateExpressions);
+                            writingString = false;
+                            continue;
+                        }
+                    } else {
+                        appendCharacter('<', templateExpressions, writingString);
+                        writingString = true;
+                    }
+                } else if (c == '"') {
+                    appendCharacter('\\', templateExpressions, writingString);
+                    writingString = true;
+                }
+                appendCharacter((char) c, templateExpressions, writingString);
+                writingString = true;
+            }
+
+            if (writingString) {
+                templateExpressions.append("\"\"\"");
+            }
+
+            templateExpressions.append("}.asWritable()}");
+
+            final GroovyClassLoader loader =
+                    (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+                        public Object run() {
+                            return new GroovyClassLoader(parentLoader);
+                        }
+                    });
+            final Class groovyClass = loader.parseClass(new GroovyCodeSource(templateExpressions.toString(), "C", "x"));
+
+            try {
+                final GroovyObject object = (GroovyObject) groovyClass.newInstance();
+
+                this.template = (Closure) object.invokeMethod("getTemplate", null);
+            } catch (InstantiationException e) {
+                throw new ClassNotFoundException(e.getMessage());
+            } catch (IllegalAccessException e) {
+                throw new ClassNotFoundException(e.getMessage());
+            }
+        }
+
+        private static void appendCharacter(final char c,
+                                            final StringBuffer templateExpressions,
+                                            final boolean writingString) {
+            if (!writingString) {
+                templateExpressions.append("out << \"\"\"");
+            }
+            templateExpressions.append(c);
+        }
+
+        /**
+         * Parse a <% .... %> section
+         * if we are writing a GString close and append ';'
+         * then write the section as a statement
+         *
+         * @param pendingC
+         * @param reader
+         * @param writingString
+         * @param templateExpressions
+         * @throws IOException
+         */
+        private static void parseSection(final int pendingC,
+                                         final Reader reader,
+                                         final boolean writingString,
+                                         final StringBuffer templateExpressions)
+                throws IOException {
+            if (writingString) {
+                templateExpressions.append("\"\"\"; ");
+            }
+            templateExpressions.append((char) pendingC);
+
+            while (true) {
+                int c = reader.read();
+                if (c == -1) break;
+                if (c == '%') {
+                    c = reader.read();
+                    if (c == '>') break;
+                    templateExpressions.append('%');
+                }
+                templateExpressions.append((char) c);
+            }
+
+            templateExpressions.append(";\n ");
+        }
+
+        /**
+         * Parse a <%= .... %> expression
+         *
+         * @param reader
+         * @param writingString
+         * @param templateExpressions
+         * @throws IOException
+         */
+        private static void parseExpression(final Reader reader,
+                                            final boolean writingString,
+                                            final StringBuffer templateExpressions)
+                throws IOException {
+            if (!writingString) {
+                templateExpressions.append("out << \"\"\"");
+            }
+
+            templateExpressions.append("${");
+
+            while (true) {
+                int c = reader.read();
+                if (c == -1) break;
+                if (c == '%') {
+                    c = reader.read();
+                    if (c == '>') break;
+                    templateExpressions.append('%');
+                }
+                templateExpressions.append((char) c);
+            }
+
+            templateExpressions.append('}');
+        }
+
+        public Writable make() {
+            return make(null);
+        }
+
+        public Writable make(final Map map) {
+            final Closure template = (Closure) this.template.clone();
+            template.setDelegate(map);
+            return (Writable) template;
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/text/SimpleTemplateEngine.java b/groovy/src/main/groovy/text/SimpleTemplateEngine.java
new file mode 100644
index 0000000..274db33
--- /dev/null
+++ b/groovy/src/main/groovy/text/SimpleTemplateEngine.java
@@ -0,0 +1,250 @@
+/*
+ * $Id$
+ *
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.text;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyShell;
+import groovy.lang.Script;
+import groovy.lang.Writable;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Map;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * This simple template engine uses JSP <% %> script and <%= %> expression syntax.  It also lets you use normal groovy expressions in
+ * the template text much like the new JSP EL functionality.  The variable 'out' is bound to the writer that the template is being written to.
+ *
+ * @author sam
+ * @author Christian Stein
+ * @author Paul King
+ */
+public class SimpleTemplateEngine extends TemplateEngine {
+    private boolean verbose;
+
+    private GroovyShell groovyShell;
+
+    public SimpleTemplateEngine() {
+        this(GroovyShell.class.getClassLoader());
+    }
+
+    public SimpleTemplateEngine(boolean verbose) {
+        this(GroovyShell.class.getClassLoader());
+        setVerbose(verbose);
+    }
+
+    public SimpleTemplateEngine(ClassLoader parentLoader) {
+        this(new GroovyShell(parentLoader));
+    }
+
+    public SimpleTemplateEngine(GroovyShell groovyShell) {
+        this.groovyShell = groovyShell;
+    }
+
+    public Template createTemplate(Reader reader) throws CompilationFailedException, IOException {
+        SimpleTemplate template = new SimpleTemplate();
+        String script = template.parse(reader);
+        if (verbose) {
+            System.out.println("\n-- script source --");
+            System.out.print(script);
+            System.out.println("\n-- script end --\n");
+        }
+        template.script = groovyShell.parse(script);
+        return template;
+    }
+
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    public boolean isVerbose() {
+        return verbose;
+    }
+
+    private static class SimpleTemplate implements Template {
+
+        protected Script script;
+
+        public Writable make() {
+            return make(null);
+        }
+
+        public Writable make(final Map map) {
+            return new Writable() {
+                /**
+                 * Write the template document with the set binding applied to the writer.
+                 *
+                 * @see groovy.lang.Writable#writeTo(java.io.Writer)
+                 */
+                public Writer writeTo(Writer writer) {
+                    Binding binding;
+                    if (map == null)
+                        binding = new Binding();
+                    else
+                        binding = new Binding(map);
+                    Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
+                    PrintWriter pw = new PrintWriter(writer);
+                    scriptObject.setProperty("out", pw);
+                    scriptObject.run();
+                    pw.flush();
+                    return writer;
+                }
+
+                /**
+                 * Convert the template and binding into a result String.
+                 *
+                 * @see java.lang.Object#toString()
+                 */
+                public String toString() {
+                    StringWriter sw = new StringWriter();
+                    writeTo(sw);
+                    return sw.toString();
+                }
+            };
+        }
+
+        /**
+         * Parse the text document looking for <% or <%= and then call out to the appropriate handler, otherwise copy the text directly
+         * into the script while escaping quotes.
+         *
+         * @param reader a reader for the template text
+         * @return the parsed text
+         * @throws IOException if something goes wrong
+         */
+        protected String parse(Reader reader) throws IOException {
+            if (!reader.markSupported()) {
+                reader = new BufferedReader(reader);
+            }
+            StringWriter sw = new StringWriter();
+            startScript(sw);
+            int c;
+            while ((c = reader.read()) != -1) {
+                if (c == '<') {
+                    reader.mark(1);
+                    c = reader.read();
+                    if (c != '%') {
+                        sw.write('<');
+                        reader.reset();
+                    } else {
+                        reader.mark(1);
+                        c = reader.read();
+                        if (c == '=') {
+                            groovyExpression(reader, sw);
+                        } else {
+                            reader.reset();
+                            groovySection(reader, sw);
+                        }
+                    }
+                    continue; // at least '<' is consumed ... read next chars.
+                }
+                if (c == '\"') {
+                    sw.write('\\');
+                }
+                /*
+                 * Handle raw new line characters.
+                 */
+                if (c == '\n' || c == '\r') {
+                    if (c == '\r') { // on Windows, "\r\n" is a new line.
+                        reader.mark(1);
+                        c = reader.read();
+                        if (c != '\n') {
+                            reader.reset();
+                        }
+                    }
+                    sw.write("\\n\");\nout.print(\"");
+                    continue;
+                }
+                sw.write(c);
+            }
+            endScript(sw);
+            return sw.toString();
+        }
+
+        private void startScript(StringWriter sw) {
+            sw.write("/* Generated by SimpleTemplateEngine */\n");
+            sw.write("out.print(\"");
+        }
+
+        private void endScript(StringWriter sw) {
+            sw.write("\");\n");
+        }
+
+        /**
+         * Closes the currently open write and writes out the following text as a GString expression until it reaches an end %>.
+         *
+         * @param reader a reader for the template text
+         * @param sw     a StringWriter to write expression content
+         * @throws IOException if something goes wrong
+         */
+        private void groovyExpression(Reader reader, StringWriter sw) throws IOException {
+            sw.write("\");out.print(\"${");
+            int c;
+            while ((c = reader.read()) != -1) {
+                if (c == '%') {
+                    c = reader.read();
+                    if (c != '>') {
+                        sw.write('%');
+                    } else {
+                        break;
+                    }
+                }
+                if (c != '\n' && c != '\r') {
+                    sw.write(c);
+                }
+            }
+            sw.write("}\");\nout.print(\"");
+        }
+
+        /**
+         * Closes the currently open write and writes the following text as normal Groovy script code until it reaches an end %>.
+         *
+         * @param reader a reader for the template text
+         * @param sw     a StringWriter to write expression content
+         * @throws IOException if something goes wrong
+         */
+        private void groovySection(Reader reader, StringWriter sw) throws IOException {
+            sw.write("\");");
+            int c;
+            while ((c = reader.read()) != -1) {
+                if (c == '%') {
+                    c = reader.read();
+                    if (c != '>') {
+                        sw.write('%');
+                    } else {
+                        break;
+                    }
+                }
+                /* Don't eat EOL chars in sections - as they are valid instruction separators.
+                 * See http://jira.codehaus.org/browse/GROOVY-980
+                 */
+                // if (c != '\n' && c != '\r') {
+                sw.write(c);
+                //}
+            }
+            sw.write(";\nout.print(\"");
+        }
+
+    }
+}
diff --git a/groovy/src/main/groovy/text/Template.java b/groovy/src/main/groovy/text/Template.java
new file mode 100644
index 0000000..4d9888e
--- /dev/null
+++ b/groovy/src/main/groovy/text/Template.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.text;
+
+import groovy.lang.Writable;
+
+import java.util.Map;
+
+
+/**
+ * A template is a block of text with an associated binding that can be output to a writer or evaluated to a string.
+ * 
+ * @author sam
+ */
+public interface Template {
+    Writable make();
+    Writable make(Map binding);
+}
diff --git a/groovy/src/main/groovy/text/TemplateEngine.java b/groovy/src/main/groovy/text/TemplateEngine.java
new file mode 100644
index 0000000..10bc697
--- /dev/null
+++ b/groovy/src/main/groovy/text/TemplateEngine.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.text;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.URL;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+
+/**
+ * Represents an API to any template engine which is basically a factory of Template instances from a given text input.
+ * 
+ * @author sam
+ */
+public abstract class TemplateEngine {
+    public abstract Template createTemplate(Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException;
+    
+    public Template createTemplate(String templateText) throws CompilationFailedException, FileNotFoundException, ClassNotFoundException, IOException {
+        return createTemplate(new StringReader(templateText));
+    }
+    
+    public Template createTemplate(File file) throws CompilationFailedException, FileNotFoundException, ClassNotFoundException, IOException {
+        return createTemplate(new FileReader(file));
+    }
+
+    public Template createTemplate(URL url) throws CompilationFailedException, ClassNotFoundException, IOException {
+        return createTemplate(new InputStreamReader(url.openStream()));
+    }
+}
diff --git a/groovy/src/main/groovy/text/XmlTemplateEngine.java b/groovy/src/main/groovy/text/XmlTemplateEngine.java
new file mode 100644
index 0000000..7e2757d
--- /dev/null
+++ b/groovy/src/main/groovy/text/XmlTemplateEngine.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.text;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyShell;
+import groovy.lang.Script;
+import groovy.lang.Writable;
+import groovy.util.IndentPrinter;
+import groovy.util.Node;
+import groovy.util.XmlNodePrinter;
+import groovy.util.XmlParser;
+import groovy.xml.QName;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.xml.sax.SAXException;
+
+/**
+ * Template engine for xml data input.
+ *
+ * @author Christian Stein
+ * @author Paul King
+ */
+public class XmlTemplateEngine extends TemplateEngine {
+
+    private static class GspPrinter extends XmlNodePrinter {
+
+        public GspPrinter(PrintWriter out, String indent) {
+            this(new IndentPrinter(out, indent));
+        }
+
+        public GspPrinter(IndentPrinter out) {
+            super(out, "\\\"");
+        }
+
+        protected void printGroovyTag(String tag, String text) {
+            if (tag.equals("scriptlet")) {
+                out.print(text);
+                out.print("\n");
+                return;
+            }
+            if (tag.equals("expression")) {
+                printLineBegin();
+                out.print("${");
+                out.print(text);
+                out.print("}");
+                printLineEnd();
+                return;
+            }
+            throw new RuntimeException("Unsupported tag named \"" + tag + "\".");
+        }
+
+        protected void printLineBegin() {
+            out.print("out.print(\"");
+            out.printIndent();
+        }
+
+        protected void printLineEnd(String comment) {
+            out.print("\\n\");");
+            if (comment != null) {
+                out.print(" // ");
+                out.print(comment);
+            }
+            out.print("\n");
+        }
+
+        protected boolean printSpecialNode(Node node) {
+            Object name = node.name();
+            if (name != null && name instanceof QName) {
+                QName qn = (QName) name;
+                if (qn.getPrefix().equals("gsp")) {
+                    String s = qn.getLocalPart();
+                    if (s.length() == 0) {
+                        throw new RuntimeException("No local part after 'gsp:' given in node " + node);
+                    }
+                    printGroovyTag(s, node.text());
+                    return true;
+                }
+            }
+            return false;
+        }
+
+    }
+
+    private static class XmlTemplate implements Template {
+
+        private final Script script;
+
+        public XmlTemplate(Script script) {
+            this.script = script;
+        }
+
+        public Writable make() {
+            return make(new HashMap());
+        }
+
+        public Writable make(Map map) {
+            if (map == null) {
+                throw new IllegalArgumentException("map must not be null");
+            }
+            return new XmlWritable(script, new Binding(map));
+        }
+    }
+
+    private static class XmlWritable implements Writable {
+
+        private final Binding binding;
+        private final Script script;
+        private WeakReference result;
+
+        public XmlWritable(Script script, Binding binding) {
+            this.script = script;
+            this.binding = binding;
+            this.result = new WeakReference(null);
+        }
+
+        public Writer writeTo(Writer out) {
+            Script scriptObject = InvokerHelper.createScript(script.getClass(), binding);
+            PrintWriter pw = new PrintWriter(out);
+            scriptObject.setProperty("out", pw);
+            scriptObject.run();
+            pw.flush();
+            return out;
+        }
+
+        public String toString() {
+            if (result.get() != null) {
+                return result.get().toString();
+            }
+            String string = writeTo(new StringWriter(1024)).toString();
+            result = new WeakReference(string);
+            return string;
+        }
+    }
+
+    public static final String DEFAULT_INDENTATION = "  ";
+
+    private final GroovyShell groovyShell;
+    private final XmlParser xmlParser;
+    private String indentation;
+
+    public XmlTemplateEngine() throws SAXException, ParserConfigurationException {
+        this(DEFAULT_INDENTATION, false);
+    }
+
+    public XmlTemplateEngine(String indentation, boolean validating) throws SAXException, ParserConfigurationException {
+        this(new XmlParser(validating, true), new GroovyShell());
+        setIndentation(indentation);
+    }
+
+    public XmlTemplateEngine(XmlParser xmlParser, ClassLoader parentLoader) {
+        this(xmlParser, new GroovyShell(parentLoader));
+    }
+
+    public XmlTemplateEngine(XmlParser xmlParser, GroovyShell groovyShell) {
+        this.groovyShell = groovyShell;
+        this.xmlParser = xmlParser;
+        setIndentation(DEFAULT_INDENTATION);
+    }
+
+    public Template createTemplate(Reader reader) throws CompilationFailedException, ClassNotFoundException, IOException {
+        Node root = null;
+        try {
+            root = xmlParser.parse(reader);
+        } catch (SAXException e) {
+            throw new RuntimeException("Parsing XML source failed.", e);
+        }
+
+        if (root == null) {
+            throw new IOException("Parsing XML source failed: root node is null.");
+        }
+
+        StringWriter writer = new StringWriter(1024);
+        writer.write("/* Generated by XmlTemplateEngine */\n");
+        new GspPrinter(new PrintWriter(writer), indentation).print(root);
+
+        Script script = groovyShell.parse(writer.toString());
+        return new XmlTemplate(script);
+    }
+
+    public String getIndentation() {
+        return indentation;
+    }
+
+    public void setIndentation(String indentation) {
+        if (indentation == null) {
+            indentation = DEFAULT_INDENTATION;
+        }
+        this.indentation = indentation;
+    }
+
+    public String toString() {
+        return "XmlTemplateEngine";
+    }
+
+}
diff --git a/groovy/src/main/groovy/text/package.html b/groovy/src/main/groovy/text/package.html
new file mode 100644
index 0000000..bd68506
--- /dev/null
+++ b/groovy/src/main/groovy/text/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.text.*</title>
+  </head>
+  <body>
+    <p>Contains the text processing utilities in particular the template engine API and default implementation.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/time/BaseDuration.java b/groovy/src/main/groovy/time/BaseDuration.java
new file mode 100644
index 0000000..929c489
--- /dev/null
+++ b/groovy/src/main/groovy/time/BaseDuration.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.time;
+
+import java.util.Calendar;
+import java.util.Date;
+
+public abstract class BaseDuration {
+    protected final int years;
+    protected final int months;    
+    protected final int days;
+    protected final int hours;
+    protected final int minutes;
+    protected final int seconds;    
+    protected final int millis;
+
+    protected BaseDuration(final int years, final int months, final int days, final int hours, final int minutes, final int seconds, final int millis) {
+        this.years = years;
+        this.months = months;
+        this.days = days;
+        this.hours = hours;
+        this.minutes = minutes;
+        this.seconds = seconds;
+        this.millis = millis;
+    }
+
+    protected BaseDuration(final int days, final int hours, final int minutes, final int seconds, final int millis) {
+        this(0, 0, days, hours, minutes, seconds, millis);
+    }
+    
+    public int getYears() {
+        return this.years;
+    }
+    
+    public int getMonths() {
+        return this.months;
+    }
+    
+    public int getDays() {
+        return this.days;
+    }
+    
+    public int getHours() {
+        return this.hours;
+    }
+    
+    public int getMinutes() {
+        return this.minutes;
+    }
+    
+    public int getSeconds() {
+        return this.seconds;
+    }
+    
+    public int getMillis() {
+        return this.millis;
+    }
+    
+    public Date plus(final Date date) {
+    final Calendar cal = Calendar.getInstance();
+    
+        cal.setTime(date);
+        cal.add(Calendar.YEAR, this.years);
+        cal.add(Calendar.MONTH, this.months);
+        cal.add(Calendar.DAY_OF_YEAR, this.days);
+        cal.add(Calendar.HOUR_OF_DAY, this.hours);
+        cal.add(Calendar.MINUTE, this.minutes);
+        cal.add(Calendar.SECOND, this.seconds);
+        cal.add(Calendar.MILLISECOND, this.millis);
+        
+        return cal.getTime();
+    }
+    
+    public abstract long toMilliseconds();
+    
+    public abstract Date getAgo();
+    
+    public abstract From getFrom();
+
+    public abstract static class From {
+        public abstract Date getNow();
+        
+        public Date getToday() {
+            return getNow();
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/time/DatumDependentDuration.java b/groovy/src/main/groovy/time/DatumDependentDuration.java
new file mode 100644
index 0000000..abd6ba4
--- /dev/null
+++ b/groovy/src/main/groovy/time/DatumDependentDuration.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.time;
+
+import java.util.Calendar;
+import java.util.Date;
+
+import org.codehaus.groovy.runtime.TimeCategory;
+
+/**
+ * @author John Wilson tug@wilson.co.uk
+ *         <p/>
+ *         DatumDependentDuration represents durations whose length in milliseconds cannot be determined withou knowing the datum point.
+ *         <p/>
+ *         I don't know how many days in a year unless I know if it's a leap year or not.
+ *         <p/>
+ *         I don't know how many days in a month unless I know the name of the month (and if it's a leap yaer if the month is February)
+ */
+public class DatumDependentDuration extends BaseDuration {
+    public DatumDependentDuration(final int years, final int months, final int days, final int hours, final int minutes, final int seconds, final int millis) {
+        super(years, months, days, hours, minutes, seconds, millis);
+    }
+
+    public int getMonths() {
+        return this.months;
+    }
+
+    public int getYears() {
+        return this.years;
+    }
+
+    public DatumDependentDuration plus(final DatumDependentDuration rhs) {
+        return new DatumDependentDuration(this.getYears() + rhs.getYears(), this.getMonths() + rhs.getMonths(),
+                this.getDays() + rhs.getDays(), this.getHours() + rhs.getHours(),
+                this.getMinutes() + rhs.getMinutes(), this.getSeconds() + rhs.getSeconds(),
+                this.getMillis() + rhs.getMillis());
+    }
+
+    public DatumDependentDuration plus(final TimeDatumDependentDuration rhs) {
+        return rhs.plus(this);
+    }
+
+    public DatumDependentDuration plus(final Duration rhs) {
+        return new DatumDependentDuration(this.getYears(), this.getMonths(),
+                this.getDays() + rhs.getDays(), this.getHours() + rhs.getHours(),
+                this.getMinutes() + rhs.getMinutes(), this.getSeconds() + rhs.getSeconds(),
+                this.getMillis() + rhs.getMillis());
+
+    }
+
+    public DatumDependentDuration plus(final TimeDuration rhs) {
+        return rhs.plus(this);
+
+    }
+
+    public DatumDependentDuration minus(final DatumDependentDuration rhs) {
+        return new DatumDependentDuration(this.getYears() - rhs.getYears(), this.getMonths() - rhs.getMonths(),
+                this.getDays() - rhs.getDays(), this.getHours() - rhs.getHours(),
+                this.getMinutes() - rhs.getMinutes(), this.getSeconds() - rhs.getSeconds(),
+                this.getMillis() - rhs.getMillis());
+
+    }
+
+    public DatumDependentDuration minus(final Duration rhs) {
+        return new DatumDependentDuration(this.getYears(), this.getMonths(),
+                this.getDays() - rhs.getDays(), this.getHours() - rhs.getHours(),
+                this.getMinutes() - rhs.getMinutes(), this.getSeconds() - rhs.getSeconds(),
+                this.getMillis() - rhs.getMillis());
+
+    }
+
+    /* (non-Javadoc)
+    * @see groovy.time.BaseDuration#toMilliseconds()
+    *
+    * Do our best to change the duration into milliseconds
+    * We calculate the duration relative to now
+    */
+    public long toMilliseconds() {
+        final Date now = new Date();
+        return TimeCategory.minus(plus(now), now).toMilliseconds();
+    }
+
+    public Date getAgo() {
+        final Calendar cal = Calendar.getInstance();
+
+        cal.add(Calendar.YEAR, -this.getYears());
+        cal.add(Calendar.MONTH, -this.getMonths());
+        cal.add(Calendar.DAY_OF_YEAR, -this.getDays());
+        cal.add(Calendar.HOUR_OF_DAY, -this.getHours());
+        cal.add(Calendar.MINUTE, -this.getMinutes());
+        cal.add(Calendar.SECOND, -this.getSeconds());
+        cal.add(Calendar.MILLISECOND, -this.getMillis());
+
+        //
+        // SqlDate should not really care about these values but it seems to "remember" them
+        // so we clear them. We do the adds first in case we get carry into the day field.
+        //
+        cal.set(Calendar.HOUR_OF_DAY, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+
+        return new java.sql.Date(cal.getTimeInMillis());
+    }
+
+    public From getFrom() {
+        return new From() {
+            public Date getNow() {
+                final Calendar cal = Calendar.getInstance();
+
+                cal.add(Calendar.YEAR, DatumDependentDuration.this.getYears());
+                cal.add(Calendar.MONTH, DatumDependentDuration.this.getMonths());
+                cal.add(Calendar.DAY_OF_YEAR, DatumDependentDuration.this.getDays());
+                cal.add(Calendar.HOUR_OF_DAY, DatumDependentDuration.this.getHours());
+                cal.add(Calendar.MINUTE, DatumDependentDuration.this.getMinutes());
+                cal.add(Calendar.SECOND, DatumDependentDuration.this.getSeconds());
+                cal.add(Calendar.MILLISECOND, DatumDependentDuration.this.getMillis());
+
+                //
+                // SqlDate should not really care about these values but it seems to "remember" them
+                // so we clear them. We do the adds first in case we get carry into the day field.
+                //
+                cal.set(Calendar.HOUR_OF_DAY, 0);
+                cal.set(Calendar.MINUTE, 0);
+                cal.set(Calendar.SECOND, 0);
+                cal.set(Calendar.MILLISECOND, 0);
+
+                return new java.sql.Date(cal.getTimeInMillis());
+            }
+        };
+    }
+}
diff --git a/groovy/src/main/groovy/time/Duration.java b/groovy/src/main/groovy/time/Duration.java
new file mode 100644
index 0000000..2f958d3
--- /dev/null
+++ b/groovy/src/main/groovy/time/Duration.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.time;
+
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author John Wilson tug@wilson.co.uk
+ * 
+ * Duration represents time periods which have values independant of the context.
+ * So, whilst we can't say how long a month is without knowing the year and the name of the month,
+ * we know how long a day is independant of the date.
+ * 
+ * This is not 100% true for days.
+ * Days can actually be 23, 24 or 25 hours long (due to daylight saving adjustments)
+ * 
+ * If you ask Duration to convert itself to milliseconds then it will work on the basis of 24 hours
+ * in a day. If you add or subtract it from a date it will take daylight saving into account.
+ *
+ */
+public class Duration extends BaseDuration {
+    public Duration(final int days, final int hours, final int minutes, final int seconds, final int millis) {
+        super(days, hours, minutes, seconds, millis);
+    }
+    
+    public Duration plus(final Duration rhs) {
+        return new Duration(this.getDays() + rhs.getDays(), this.getHours() + rhs.getHours(),
+                            this.getMinutes() + rhs.getMinutes(), this.getSeconds() + rhs.getSeconds(),
+                            this.getMillis() + rhs.getMillis());
+    }
+
+    public Duration plus(final TimeDuration rhs) {
+        return rhs.plus(this);
+    }
+    
+    public DatumDependentDuration plus(final DatumDependentDuration rhs) {
+        return rhs.plus(this);
+    }
+    
+    public Duration minus(final Duration rhs) {
+        return new Duration(this.getDays() - rhs.getDays(), this.getHours() - rhs.getHours(),
+                            this.getMinutes() - rhs.getMinutes(), this.getSeconds() - rhs.getSeconds(),
+                            this.getMillis() - rhs.getMillis());
+    }
+    
+    public TimeDuration minus(final TimeDuration rhs) {
+        return new TimeDuration(this.getDays() - rhs.getDays(), this.getHours() - rhs.getHours(),
+                                this.getMinutes() - rhs.getMinutes(), this.getSeconds() - rhs.getSeconds(),
+                                this.getMillis() - rhs.getMillis());
+    }
+    
+    public DatumDependentDuration minus(final DatumDependentDuration rhs) {
+        return new DatumDependentDuration(-rhs.getYears(), -rhs.getMonths(),
+                                          this.getDays() - rhs.getDays(), this.getHours() - rhs.getHours(),
+                                          this.getMinutes() - rhs.getMinutes(), this.getSeconds() - rhs.getSeconds(),
+                                          this.getMillis() - rhs.getMillis());
+    }
+    
+    public TimeDatumDependentDuration minus(final TimeDatumDependentDuration rhs) {
+        return new TimeDatumDependentDuration(-rhs.getYears(), -rhs.getMonths(),
+                                              this.getDays() - rhs.getDays(), this.getHours() - rhs.getHours(),
+                                              this.getMinutes() - rhs.getMinutes(), this.getSeconds() - rhs.getSeconds(),
+                                              this.getMillis() - rhs.getMillis());
+    }
+    
+    public long toMilliseconds() {
+        return ((((((long)(this.getDays() * 24 ) + this.getHours()) * 60 + this.getMinutes()) * 60) + this.getSeconds()) * 1000) + this.getMillis();
+    }
+    
+    public Date getAgo() {
+    final Calendar cal = Calendar.getInstance();
+
+        cal.add(Calendar.DAY_OF_YEAR, -this.getDays());
+        cal.add(Calendar.HOUR_OF_DAY, -this.getHours());
+        cal.add(Calendar.MINUTE, -this.getMinutes());
+        cal.add(Calendar.SECOND, -this.getSeconds());
+        cal.add(Calendar.MILLISECOND, -this.getMillis());
+        
+        
+        //
+        // SqlDate should not really care about these values but it seems to "remember" them
+        // so we clear them.
+        // We do the adds first incase we get carry into the day field
+        //
+        cal.set(Calendar.HOUR_OF_DAY, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        
+        return new java.sql.Date(cal.getTimeInMillis());
+    }
+     
+    public From getFrom() {
+        return new From() {
+            public Date getNow() {
+            final Calendar cal = Calendar.getInstance();
+
+                cal.add(Calendar.DAY_OF_YEAR, Duration.this.getDays());
+                
+                //
+                // SqlDate should not really care about these values but it seems to "remember" them
+                // so we clear them.
+                // We do the adds first incase we get carry into the day field
+                //
+                cal.set(Calendar.HOUR_OF_DAY, 0);
+                cal.set(Calendar.MINUTE, 0);
+                cal.set(Calendar.SECOND, 0);
+                cal.set(Calendar.MILLISECOND, 0);
+                
+                return new java.sql.Date(cal.getTimeInMillis());
+            }
+        };
+    }
+}
diff --git a/groovy/src/main/groovy/time/TimeDatumDependentDuration.java b/groovy/src/main/groovy/time/TimeDatumDependentDuration.java
new file mode 100644
index 0000000..598e22a
--- /dev/null
+++ b/groovy/src/main/groovy/time/TimeDatumDependentDuration.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.time;
+
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author John Wilson tug@wilson.co.uk
+ * 
+ * TimeDatumDuration represents a time perid which results from an
+ * arithmetic operation between a TimeDuration object and a DatumDuration object
+ * 
+ */
+public class TimeDatumDependentDuration extends DatumDependentDuration {
+    public TimeDatumDependentDuration(int years, int months, int days, int hours, int minutes, int seconds, int millis) {
+        super(years, months, days, hours, minutes, seconds, millis);
+    }
+    
+    public DatumDependentDuration plus(final Duration rhs) {
+        return new TimeDatumDependentDuration(this.getYears(), this.getMonths(),
+                                              this.getDays() + rhs.getDays(), this.getHours() + rhs.getHours(),
+                                              this.getMinutes() + rhs.getMinutes(), this.getSeconds() + rhs.getSeconds(),
+                                              this.getMillis() + rhs.getMillis());
+    }
+    
+    public DatumDependentDuration plus(final DatumDependentDuration rhs) {
+        return new TimeDatumDependentDuration(this.getYears() + rhs.getYears(), this.getMonths() + rhs.getMonths(),
+                                              this.getDays() + rhs.getDays(), this.getHours() + rhs.getHours(),
+                                              this.getMinutes() + rhs.getMinutes(), this.getSeconds() + rhs.getSeconds(),
+                                              this.getMillis() + rhs.getMillis());
+    }
+    
+    public DatumDependentDuration minus(final Duration rhs) {
+        return new TimeDatumDependentDuration(this.getYears(), this.getMonths(),
+                                              this.getDays() - rhs.getDays(), this.getHours() - rhs.getHours(),
+                                              this.getMinutes() - rhs.getMinutes(), this.getSeconds() - rhs.getSeconds(),
+                                              this.getMillis() - rhs.getMillis());
+    }
+    
+    public DatumDependentDuration minus(final DatumDependentDuration rhs) {
+        return new TimeDatumDependentDuration(this.getYears() - rhs.getYears(), this.getMonths() - rhs.getMonths(),
+                                              this.getDays() - rhs.getDays(), this.getHours() - rhs.getHours(),
+                                              this.getMinutes() - rhs.getMinutes(), this.getSeconds() - rhs.getSeconds(),
+                                              this.getMillis() - rhs.getMillis());
+    }
+    
+    public From getFrom() {
+        return new From() {
+            public Date getNow() {
+            final Calendar cal = Calendar.getInstance();
+
+                cal.add(Calendar.YEAR, TimeDatumDependentDuration.this.getYears());
+                cal.add(Calendar.MONTH, TimeDatumDependentDuration.this.getMonths());
+                cal.add(Calendar.DAY_OF_YEAR, TimeDatumDependentDuration.this.getDays());
+                cal.add(Calendar.HOUR_OF_DAY, TimeDatumDependentDuration.this.getHours());
+                cal.add(Calendar.MINUTE, TimeDatumDependentDuration.this.getMinutes());
+                cal.add(Calendar.SECOND, TimeDatumDependentDuration.this.getSeconds());
+                cal.add(Calendar.MILLISECOND, TimeDatumDependentDuration.this.getMillis());
+                
+                return cal.getTime();
+            }
+        };
+    }
+
+}
diff --git a/groovy/src/main/groovy/time/TimeDuration.java b/groovy/src/main/groovy/time/TimeDuration.java
new file mode 100644
index 0000000..667f1e7
--- /dev/null
+++ b/groovy/src/main/groovy/time/TimeDuration.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.time;
+
+import java.util.Calendar;
+import java.util.Date;
+
+/**
+ * @author John Wilson tug@wilson.co.uk
+ * 
+ * TimeDuration represents time periods expressed in units of hours, minutes, seconds and milliseconds.
+ * 
+ * Whilst we can't say how long a month is without knowing the year and the name of the month,
+ * we know how long a second is independant of the date.
+ * 
+ * This is not 100% true for minutes.
+ * Minutes can be 59, 60 or 61 seconds long (due to leap seconds)
+ * 
+ * If you ask Duration to convert itself to milliseconds then it will work on the basis of 60 seconds in a minute.
+ * If you add or subtract it from a date it will take leap seconds into account
+ *
+ */
+
+public class TimeDuration extends Duration {
+    public TimeDuration(final int hours, final int minutes, final int seconds, final int millis) {
+        super(0, hours, minutes, seconds, millis);
+     }
+    
+    public TimeDuration(final int days, final int hours, final int minutes, final int seconds, final int millis) {
+        super(days, hours, minutes, seconds, millis);
+     }
+    
+    public Duration plus(final Duration rhs) {
+        return new TimeDuration(this.getDays() + rhs.getDays(), this.getHours() + rhs.getHours(),
+                                this.getMinutes() + rhs.getMinutes(), this.getSeconds() + rhs.getSeconds(),
+                                this.getMillis() + rhs.getMillis());
+    }
+    
+    public DatumDependentDuration plus(final DatumDependentDuration rhs) {
+        return new TimeDatumDependentDuration(rhs.getYears(), rhs.getMonths(),
+                                              this.getDays() + rhs.getDays(), this.getHours() + rhs.getHours(),
+                                              this.getMinutes() + rhs.getMinutes(), this.getSeconds() + rhs.getSeconds(),
+                                              this.getMillis() + rhs.getMillis());
+    }
+    
+    public Duration minus(final Duration rhs) {
+        return new TimeDuration(this.getDays() - rhs.getDays(), this.getHours() - rhs.getHours(),
+                                this.getMinutes() - rhs.getMinutes(), this.getSeconds() - rhs.getSeconds(),
+                                this.getMillis() - rhs.getMillis());
+    }
+    
+    public DatumDependentDuration minus(final DatumDependentDuration rhs) {
+        return new TimeDatumDependentDuration(-rhs.getYears(), -rhs.getMonths(),
+                                              this.getDays() - rhs.getDays(), this.getHours() - rhs.getHours(),
+                                              this.getMinutes() - rhs.getMinutes(), this.getSeconds() - rhs.getSeconds(),
+                                              this.getMillis() - rhs.getMillis());
+    }
+    
+    public From getFrom() {
+        return new From() {
+            public Date getNow() {
+            final Calendar cal = Calendar.getInstance();
+
+                cal.add(Calendar.DAY_OF_YEAR, TimeDuration.this.getDays());
+                cal.add(Calendar.HOUR_OF_DAY, TimeDuration.this.getHours());
+                cal.add(Calendar.MINUTE, TimeDuration.this.getMinutes());
+                cal.add(Calendar.SECOND, TimeDuration.this.getSeconds());
+                cal.add(Calendar.MILLISECOND, TimeDuration.this.getMillis());
+                
+                return cal.getTime();
+            }
+        };
+    }
+}
diff --git a/groovy/src/main/groovy/time/package.html b/groovy/src/main/groovy/time/package.html
new file mode 100644
index 0000000..50f6958
--- /dev/null
+++ b/groovy/src/main/groovy/time/package.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>package groovy.time.*</title>
+  </head>
+  <body>
+    <p>
+      TimeCategory related classes
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/ui/Console.groovy b/groovy/src/main/groovy/ui/Console.groovy
new file mode 100644
index 0000000..06dd233
--- /dev/null
+++ b/groovy/src/main/groovy/ui/Console.groovy
@@ -0,0 +1,660 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.ui
+
+import groovy.inspect.swingui.ObjectBrowser
+import groovy.swing.SwingBuilder
+import groovy.ui.ConsoleTextEditor
+import groovy.ui.SystemOutputInterceptor
+import groovy.ui.text.FindReplaceUtility
+import java.awt.Component
+import java.awt.EventQueue
+import java.awt.Font
+import java.awt.Toolkit
+import java.awt.event.ActionEvent
+import java.util.prefs.Preferences
+import javax.swing.*
+import javax.swing.event.CaretEvent
+import javax.swing.event.CaretListener
+import javax.swing.text.Element
+import javax.swing.text.Style
+import org.codehaus.groovy.runtime.InvokerHelper
+import org.codehaus.groovy.runtime.StackTraceUtils
+
+/**
+ * Groovy Swing console.
+ *
+ * Allows user to interactively enter and execute Groovy.
+ *
+ * @version $Id$
+ * @author Danno Ferrin
+ * @author Dierk Koenig, changed Layout, included Selection sensitivity, included ObjectBrowser
+ * @author Alan Green more features: history, System.out capture, bind result to _
+ */
+class Console implements CaretListener {
+
+    private prefs = Preferences.userNodeForPackage(Console)
+
+    // Whether or not std output should be captured to the console
+    boolean captureStdOut = prefs.getBoolean('captureStdOut', true)
+
+    boolean fullStackTraces = prefs.getBoolean('fullStackTraces',
+        Boolean.valueOf(System.getProperty("groovy.full.stacktrace", "false")))
+    Action fullStackTracesAction
+
+    boolean showToolbar = prefs.getBoolean('showToolbar', true)
+    Component toolbar
+    Action showToolbarAction
+
+    // Maximum size of history
+    int maxHistory = 10
+
+    // Maximum number of characters to show on console at any time
+    int maxOutputChars = 20000
+
+    // UI
+    SwingBuilder swing
+    JFrame frame
+    ConsoleTextEditor inputEditor
+    JTextPane inputArea
+    JTextPane outputArea
+    JLabel statusLabel
+    JDialog runWaitDialog
+    JLabel rowNumAndColNum
+
+    // row info
+    Element rootElement
+    int cursorPos
+    int rowNum
+    int colNum
+
+    // Styles for output area
+    Style promptStyle
+    Style commandStyle
+    Style outputStyle
+    Style resultStyle
+
+    // Internal history
+    List history = []
+    int historyIndex = 1 // valid values are 0..history.length()
+    HistoryRecord pendingRecord = new HistoryRecord( allText: "", selectionStart: 0, selectionEnd: 0)
+    Action prevHistoryAction
+    Action nextHistoryAction
+
+    // Current editor state
+    boolean dirty
+    Action saveAction
+    int textSelectionStart  // keep track of selections in inputArea
+    int textSelectionEnd
+    def scriptFile
+    File currentFileChooserDir = new File(Preferences.userNodeForPackage(Console).get('currentFileChooserDir', '.'))
+
+    // Running scripts
+    GroovyShell shell
+    int scriptNameCounter = 0
+    SystemOutputInterceptor systemOutInterceptor
+    def runThread = null
+    Closure beforeExecution
+    Closure afterExecution
+
+    public static String ICON_PATH = '/groovy/ui/ConsoleIcon.png' // used by ObjectBrowser too
+
+    static void main(args) {
+        // allow the full stack traces to bubble up to the root logger
+        java.util.logging.Logger.getLogger(StackTraceUtils.STACK_LOG_NAME).useParentHandlers = true
+
+        //when starting via main set the look and feel to system
+        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
+
+        def console = new Console()
+        console.run()
+    }
+
+    Console() {
+        this(new Binding())
+    }
+
+    Console(Binding binding) {
+        this(null, binding)
+    }
+
+    Console(ClassLoader parent, Binding binding) {
+        shell = new GroovyShell(parent,binding)
+        try {
+            System.setProperty("groovy.full.stacktrace",
+                Boolean.toString(Boolean.valueOf(System.getProperty("groovy.full.stacktrace", "false"))))
+        } catch (SecurityException se) {
+            fullStackTracesAction.enabled = false;
+        }
+    }
+
+    void run() {
+        swing = new SwingBuilder()
+
+        // tweak what the stack traces filter out to be fairly broad
+        System.setProperty("groovy.sanitized.stacktraces", """org.codehaus.groovy.runtime.
+                org.codehaus.groovy.
+                groovy.lang.
+                gjdk.groovy.lang.
+                sun.
+                java.lang.reflect.
+                java.lang.Thread
+                groovy.ui.Console""")
+
+
+        // add controller to the swingBuilder bindings
+        swing.controller = this
+
+        // create the actions
+        swing.build(ConsoleActions)
+
+        // create the view
+        swing.build(ConsoleView)
+
+        bindResults()
+
+        // stitch some actions togeather
+        swing.bind(source:swing.inputEditor.undoAction, sourceProperty:'enabled', target:swing.undoAction, targetProperty:'enabled')
+        swing.bind(source:swing.inputEditor.redoAction, sourceProperty:'enabled', target:swing.redoAction, targetProperty:'enabled')
+
+        swing.consoleFrame.pack()
+        swing.consoleFrame.show()
+        installInterceptor()
+        swing.doLater inputArea.&requestFocus
+    }
+
+
+    public void installInterceptor() {
+        systemOutInterceptor = new SystemOutputInterceptor(this.&notifySystemOut)
+        systemOutInterceptor.start()
+    }
+
+    void addToHistory(record) {
+        history.add(record)
+        // history.size here just retrieves method closure
+        if (history.size() > maxHistory) {
+            history.remove(0)
+        }
+        // history.size doesn't work here either
+        historyIndex = history.size()
+        updateHistoryActions()
+    }
+
+    // Append a string to the output area
+    void appendOutput(text, style){
+        def doc = outputArea.styledDocument
+        doc.insertString(doc.length, text, style)
+
+        // Ensure we don't have too much in console (takes too much memory)
+        if (doc.length > maxOutputChars) {
+            doc.remove(0, doc.length - maxOutputChars)
+        }
+    }
+
+    // Append a string to the output area on a new line
+    void appendOutputNl(text, style){
+        def doc = outputArea.styledDocument
+        def len = doc.length
+        if (len > 0 && doc.getText(len - 1, 1) != "\n") {
+            appendOutput("\n", style)
+        }
+        appendOutput(text, style)
+    }
+
+    // Return false if use elected to cancel
+    boolean askToSaveFile() {
+        if (scriptFile == null || !dirty) {
+            return true
+        }
+        switch (JOptionPane.showConfirmDialog(frame,
+            "Save changes to " + scriptFile.name + "?",
+            "GroovyConsole", JOptionPane.YES_NO_CANCEL_OPTION))
+        {
+            case JOptionPane.YES_OPTION:
+                return fileSave()
+            case JOptionPane.NO_OPTION:
+                return true
+            default:
+                return false
+        }
+    }
+
+    void beep() {
+        Toolkit.defaultToolkit.beep()
+    }
+
+    // Binds the "_" and "__" variables in the shell
+    void bindResults() {
+        shell.setVariable("_", getLastResult()) // lastResult doesn't seem to work
+        shell.setVariable("__", history.collect {it.result})
+    }
+
+    // Handles menu event
+    void captureStdOut(EventObject evt) {
+        captureStdOut = evt.source.selected
+        prefs.putBoolean('captureStdOut', captureStdOut)
+    }
+
+    void fullStackTraces(EventObject evt) {
+        fullStackTraces = evt.source.selected
+        System.setProperty("groovy.full.stacktrace",
+            Boolean.toString(fullStackTraces))
+        prefs.putBoolean('fullStackTraces', fullStackTraces)
+    }
+
+    void showToolbar(EventObject evt) {
+        showToolbar = evt.source.selected
+        prefs.putBoolean('showToolbar', showToolbar)
+        toolbar.visible = showToolbar
+    }
+
+    void caretUpdate(CaretEvent e){
+        textSelectionStart = Math.min(e.dot,e.mark)
+        textSelectionEnd = Math.max(e.dot,e.mark)
+
+        setRowNumAndColNum()
+    }
+
+    void clearOutput(EventObject evt = null) {
+        outputArea.setText('')
+    }
+
+    // Confirm whether to interrupt the running thread
+    void confirmRunInterrupt(EventObject evt) {
+        def rc = JOptionPane.showConfirmDialog(frame, "Attempt to interrupt script?",
+            "GroovyConsole", JOptionPane.YES_NO_OPTION)
+        if (rc == JOptionPane.YES_OPTION && runThread != null) {
+            runThread.interrupt()
+        }
+    }
+
+    void exit(EventObject evt = null) {
+        if (askToSaveFile()) {
+            frame.hide()
+            frame.dispose()
+            FindReplaceUtility.dispose()
+        }
+
+        systemOutInterceptor.stop()
+    }
+
+    void fileNewFile(EventObject evt = null) {
+        if (askToSaveFile()) {
+            scriptFile = null
+            setDirty(false)
+            inputArea.text = ''
+        }
+    }
+
+    // Start a new window with a copy of current variables
+    void fileNewWindow(EventObject evt = null) {
+        Console consoleController = new Console(
+            new Binding(
+                new HashMap(shell.context.variables)))
+        consoleController.systemOutInterceptor = systemOutInterceptor
+        SwingBuilder swing = new SwingBuilder()
+        swing.controller = consoleController
+        swing.build(ConsoleActions)
+        swing.build(ConsoleView)
+        installInterceptor()
+        swing.consoleFrame.pack()
+        swing.consoleFrame.show()
+    }
+
+    void fileOpen(EventObject evt = null) {
+        scriptFile = selectFilename()
+        if (scriptFile != null) {
+            inputArea.text = scriptFile.readLines().join('\n')
+            setDirty(false)
+            inputArea.caretPosition = 0
+        }
+    }
+
+    // Save file - return false if user cancelled save
+    boolean fileSave(EventObject evt = null) {
+        if (scriptFile == null) {
+            return fileSaveAs(evt)
+        } else {
+            scriptFile.write(inputArea.text)
+            setDirty(false)
+            return true
+        }
+    }
+
+    // Save file - return false if user cancelled save
+    boolean fileSaveAs(EventObject evt = null) {
+        scriptFile = selectFilename("Save")
+        if (scriptFile != null) {
+            scriptFile.write(inputArea.text)
+            setDirty(false)
+            return true
+        } else {
+            return false
+        }
+    }
+
+    def finishException(Throwable t) {
+        statusLabel.text = 'Execution terminated with exception.'
+        history[-1].exception = t
+
+        appendOutputNl("Exception thrown: ", promptStyle)
+        appendOutput(t.toString(), resultStyle)
+
+        StringWriter sw = new StringWriter()
+        new PrintWriter(sw).withWriter { pw -> StackTraceUtils.deepSanitize(t).printStackTrace(pw) }
+
+        appendOutputNl("\n${sw.buffer}\n", outputStyle)
+        bindResults()
+    }
+
+    def finishNormal(Object result) {
+        // Take down the wait/cancel dialog
+        history[-1].result = result
+        if (result != null) {
+            statusLabel.text = 'Execution complete.'
+            appendOutputNl("Result: ", promptStyle)
+            appendOutput("${InvokerHelper.inspect(result)}", resultStyle)
+        } else {
+            statusLabel.text = 'Execution complete. Result was null.'
+        }
+        bindResults()
+    }
+
+    // Gets the last, non-null result
+    def getLastResult() {
+        // runtime bugs in here history.reverse produces odd lookup
+        // return history.reverse.find {it != null}
+        if (!history) {
+            return
+        }
+        for (i in (history.size() - 1)..0) {
+            if (history[i].result != null) {
+                return history[i].result
+            }
+        }
+        return null
+    }
+
+    // Allow access to shell from outside console
+    // (useful for configuring shell before startup)
+    GroovyShell getShell() {
+        return shell
+    }
+
+    void historyNext(EventObject evt = null) {
+        if (historyIndex < history.size()) {
+            setInputTextFromHistory(historyIndex + 1)
+        } else {
+            statusLabel.text = "Can't go past end of history (time travel not allowed)"
+            beep()
+        }
+    }
+
+    void historyPrev(EventObject evt = null) {
+        if (historyIndex > 0) {
+            setInputTextFromHistory(historyIndex - 1)
+        } else {
+            statusLabel.text = "Can't go past start of history"
+            beep()
+        }
+    }
+
+    void inspectLast(EventObject evt = null){
+        if (null == lastResult) {
+            JOptionPane.showMessageDialog(frame, "The last result is null.",
+                "Cannot Inspect", JOptionPane.INFORMATION_MESSAGE)
+            return
+        }
+        ObjectBrowser.inspect(lastResult)
+    }
+
+    void inspectVariables(EventObject evt = null) {
+        ObjectBrowser.inspect(shell.context.variables)
+    }
+
+    void largerFont(EventObject evt = null) {
+        if (inputArea.font.size > 40) return
+        // don't worry, the fonts won't be changed to monospaced face, the styles will only derive from this
+        def newFont = new Font('Monospaced', Font.PLAIN, inputArea.font.size + 2)
+        inputArea.font = newFont
+        outputArea.font = newFont
+    }
+
+    Boolean notifySystemOut(String str) {
+        if (!captureStdOut) {
+            // Output as normal
+            return true
+        }
+
+        // Put onto GUI
+        if (EventQueue.isDispatchThread()) {
+            appendOutput(str, outputStyle)
+        }
+        else {
+            SwingUtilities.invokeLater {
+                appendOutput(str, outputStyle)
+            }
+        }
+        return false
+    }
+
+    // actually run the script
+
+    void runScript(EventObject evt = null) {
+        runScriptImpl(false)
+    }
+
+    void runSelectedScript(EventObject evt = null) {
+        runScriptImpl(true)
+    }
+
+    private void runScriptImpl(boolean selected) {
+        def endLine = System.getProperty('line.separator')
+        def record = new HistoryRecord( allText: inputArea.getText().replaceAll(endLine, '\n'),
+            selectionStart: textSelectionStart, selectionEnd: textSelectionEnd)
+        addToHistory(record)
+        pendingRecord = new HistoryRecord(allText:'', selectionStart:0, selectionEnd:0)
+
+        // Print the input text
+        for (line in record.getTextToRun(selected).tokenize("\n")) {
+            appendOutputNl('groovy> ', promptStyle)
+            appendOutput(line, commandStyle)
+        }
+
+        //appendOutputNl("") - with wrong number of args, causes StackOverFlowError
+        appendOutputNl("\n", promptStyle)
+
+        // Kick off a new thread to do the evaluation
+        statusLabel.text = 'Running Script...'
+
+        // Run in separate thread, so that System.out can be captured
+        runThread = Thread.start {
+            try {
+                SwingUtilities.invokeLater { showRunWaitDialog() }
+                String name = "Script${scriptNameCounter++}"
+                if(beforeExecution) {
+                    beforeExecution()
+                }
+                def result = shell.evaluate(record.getTextToRun(selected), name)
+                if(afterExecution) {
+                    afterExecution()
+                }
+                SwingUtilities.invokeLater { finishNormal(result) }
+            } catch (Throwable t) {
+                SwingUtilities.invokeLater { finishException(t) }
+            } finally {
+                SwingUtilities.invokeLater {
+                    runWaitDialog.hide()
+                    runThread = null
+                }
+            }
+        }
+    }
+
+    def selectFilename(name = "Open") {
+        def fc = new JFileChooser(currentFileChooserDir)
+        fc.fileSelectionMode = JFileChooser.FILES_ONLY
+        fc.acceptAllFileFilterUsed = true
+        if (fc.showDialog(frame, name) == JFileChooser.APPROVE_OPTION) {
+            currentFileChooserDir = fc.currentDirectory
+            Preferences.userNodeForPackage(Console).put('currentFileChooserDir', currentFileChooserDir.path)
+            return fc.selectedFile
+        } else {
+            return null
+        }
+    }
+
+    void setDirty(boolean newDirty) {
+        //TODO when @BoundProperty is live, this should be handled via listeners
+        dirty = newDirty
+        saveAction.enabled = newDirty
+        updateTitle()
+    }
+
+    private void setInputTextFromHistory(newIndex) {
+        def endLine = System.getProperty('line.separator')
+        if (historyIndex >= history.size()) {
+            pendingRecord = new HistoryRecord( allText: inputArea.getText().replaceAll(endLine, '\n'),
+                selectionStart: textSelectionStart, selectionEnd: textSelectionEnd)
+        }
+        historyIndex = newIndex
+        def record
+        if (historyIndex < history.size()) {
+            record = history[historyIndex]
+            statusLabel.text = "command history ${history.size() - historyIndex}"
+        } else {
+            record = pendingRecord
+            statusLabel.text = 'at end of history'
+        }
+        inputArea.text = record.allText
+        inputArea.selectionStart = record.selectionStart
+        inputArea.selectionEnd = record.selectionEnd
+        setDirty(true) // Should calculate dirty flag properly (hash last saved/read text in each file)
+        updateHistoryActions()
+    }
+
+    private void updateHistoryActions() {
+        nextHistoryAction.enabled = historyIndex < history.size()
+        prevHistoryAction.enabled = historyIndex > 0
+    }
+
+    // Adds a variable to the binding
+    // Useful for adding variables before openning the console
+    void setVariable(String name, Object value) {
+        shell.context.setVariable(name, value)
+    }
+
+    void showAbout(EventObject evt = null) {
+        def version = InvokerHelper.getVersion()
+        def pane = swing.optionPane()
+         // work around GROOVY-1048
+        pane.setMessage('Welcome to the Groovy Console for evaluating Groovy scripts\nVersion ' + version)
+        def dialog = pane.createDialog(frame, 'About GroovyConsole')
+        dialog.show()
+    }
+
+    void find(EventObject evt = null) {
+        FindReplaceUtility.showDialog()
+    }
+
+    void findNext(EventObject evt = null) {
+        FindReplaceUtility.FIND_ACTION.actionPerformed(evt)
+    }
+
+    void findPrevious(EventObject evt = null) {
+        def reverseEvt = new ActionEvent(
+            evt.getSource(), evt.getID(),
+            evt.getActionCommand(), evt.getWhen(),
+            ActionEvent.SHIFT_MASK) //reverse
+        FindReplaceUtility.FIND_ACTION.actionPerformed(reverseEvt)
+    }
+
+    void replace(EventObject evt = null) {
+        FindReplaceUtility.showDialog(true)
+    }
+
+
+    // Shows the 'wait' dialog
+    void showRunWaitDialog() {
+        runWaitDialog.pack()
+        int x = frame.x + (frame.width - runWaitDialog.width) / 2
+        int y = frame.y + (frame.height - runWaitDialog.height) / 2
+        runWaitDialog.setLocation(x, y)
+        runWaitDialog.show()
+    }
+
+    void smallerFont(EventObject evt = null){
+        if (inputArea.font.size < 5) return
+        // don't worry, the fonts won't be changed to monospaced face, the styles will only derive from this
+        def newFont = new Font('Monospaced', Font.PLAIN, inputArea.font.size - 2)
+        inputArea.font = newFont
+        outputArea.font = newFont
+    }
+
+    void updateTitle() {
+        if (scriptFile != null) {
+            frame.title = scriptFile.name + (dirty?" * ":"") + " - GroovyConsole"
+        } else {
+            frame.title = "GroovyConsole"
+        }
+    }
+
+    void invokeTextAction(evt, closure) {
+        def source = evt.getSource()
+        if (source != null) {
+            closure(inputArea)
+        }
+    }
+
+    void cut(EventObject evt = null) {
+        invokeTextAction(evt, { source -> source.cut() })
+    }
+
+    void copy(EventObject evt = null) {
+        invokeTextAction(evt, { source -> source.copy() })
+    }
+
+    void paste(EventObject evt = null) {
+        invokeTextAction(evt, { source -> source.paste() })
+    }
+
+    void selectAll(EventObject evt = null) {
+        invokeTextAction(evt, { source -> source.selectAll() })
+    }
+
+    void setRowNumAndColNum() {
+        cursorPos = inputArea.getCaretPosition()
+        rowNum = rootElement.getElementIndex(cursorPos) + 1
+
+        def rowElement = rootElement.getElement(rowNum - 1)
+        colNum = cursorPos - rowElement.getStartOffset() + 1
+
+        rowNumAndColNum.setText("$rowNum:$colNum")
+    }
+
+    void print(EventObject evt = null) {
+        inputEditor.printAction.actionPerformed(evt)
+    }
+
+    void undo(EventObject evt = null) {
+        inputEditor.undoAction.actionPerformed(evt)
+    }
+
+    void redo(EventObject evt = null) {
+        inputEditor.redoAction.actionPerformed(evt)
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/ui/ConsoleActions.groovy b/groovy/src/main/groovy/ui/ConsoleActions.groovy
new file mode 100644
index 0000000..3d542b2
--- /dev/null
+++ b/groovy/src/main/groovy/ui/ConsoleActions.groovy
@@ -0,0 +1,265 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.ui

+

+import java.awt.event.InputEvent

+import java.awt.event.KeyEvent

+import javax.swing.KeyStroke

+

+action(id: 'newFileAction',

+    name: 'New File',

+    closure: controller.&fileNewFile,

+    mnemonic: 'N',

+    accelerator: shortcut('N'),

+    smallIcon: imageIcon(resource:"icons/page.png", class:this),

+    shortDescription: 'New Groovy Script'

+)

+

+action(id: 'newWindowAction',

+    name: 'New Window',

+    closure: controller.&fileNewWindow,

+    mnemonic: 'W',

+    accelerator: shortcut('shift N')

+)

+

+action(id: 'openAction',

+    name: 'Open',

+    closure: controller.&fileOpen,

+    mnemonic: 'O',

+    accelerator: shortcut('O'),

+    smallIcon: imageIcon(resource:"icons/folder_page.png", class:this),

+    shortDescription: 'Open Groovy Script'

+)

+

+action(id: 'saveAction',

+    name: 'Save',

+    closure: controller.&fileSave,

+    mnemonic: 'S',

+    accelerator: shortcut('S'),

+    smallIcon: imageIcon(resource:"icons/disk.png", class:this),

+    shortDescription: 'Save Groovy Script',

+    enabled: false // controller will enable as needed

+)

+

+action(id: 'saveAsAction',

+    name: 'Save As...',

+    closure: controller.&fileSaveAs,

+    mnemonic: 'A',

+)

+

+action(id: 'printAction',

+    name: 'Print...',

+    closure: controller.&print,

+    mnemonic: 'P',

+    accelerator: shortcut('P')

+)

+

+action(id: 'exitAction',

+    name: 'Exit',

+    closure: controller.&exit,

+    mnemonic: 'X'

+// whether or not application exit should have an

+// accellerator is debatable in usability circles

+// at the very least a confirm dialog should dhow up

+//accelerator: shortcut('Q')

+)

+

+action(id: 'undoAction',

+    name: 'Undo',

+    closure: controller.&undo,

+    mnemonic: 'U',

+    accelerator: shortcut('Z'),

+    smallIcon: imageIcon(resource:"icons/arrow_undo.png", class:this),

+    shortDescription: 'Undo'

+)

+

+action(id: 'redoAction',

+    name: 'Redo',

+    closure: controller.&redo,

+    mnemonic: 'R',

+    accelerator: shortcut('shift Z'), // is control-shift-Z or control-Y more common?

+    smallIcon: imageIcon(resource:"icons/arrow_redo.png", class:this),

+    shortDescription: 'Redo'

+)

+

+action(id: 'findAction',

+    name: 'Find...',

+    closure: controller.&find,

+    mnemonic: 'F',

+    accelerator: shortcut('F'),

+    smallIcon: imageIcon(resource:"icons/find.png", class:this),

+    shortDescription: 'Find'

+)

+

+action(id: 'findNextAction',

+    name: 'Find Next',

+    closure: controller.&findNext,

+    mnemonic: 'N',

+    accelerator: KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0)

+)

+

+action(id: 'findPreviousAction',

+    name: 'Find Previous',

+    closure: controller.&findPrevious,

+    mnemonic: 'V',

+    accelerator: KeyStroke.getKeyStroke(KeyEvent.VK_F3, InputEvent.SHIFT_DOWN_MASK)

+)

+

+action(id: 'replaceAction',

+    name: 'Replace...',

+    closure: controller.&replace,

+    mnemonic: 'E',

+    accelerator: shortcut('H'),

+    smallIcon: imageIcon(resource:"icons/text_replace.png", class:this),

+    shortDescription: 'Replace'

+)

+

+action(id: 'cutAction',

+    name: 'Cut',

+    closure: controller.&cut,

+    mnemonic: 'T',

+    accelerator: shortcut('X'),

+    smallIcon: imageIcon(resource:"icons/cut.png", class:this),

+    shortDescription: 'Cut'

+)

+

+action(id: 'copyAction',

+    name: 'Copy',

+    closure: controller.&copy,

+    mnemonic: 'C',

+    accelerator: shortcut('C'),

+    smallIcon: imageIcon(resource:"icons/page_copy.png", class:this),

+    shortDescription: 'Copy'

+)

+

+action(id: 'pasteAction',

+    name: 'Paste',

+    closure: controller.&paste,

+    mnemonic: 'P',

+    accelerator: shortcut('V'),

+    smallIcon: imageIcon(resource:"icons/page_paste.png", class:this),

+    shortDescription: 'Paste'

+)

+

+action(id: 'selectAllAction',

+    name: 'Select All',

+    closure: controller.&selectAll,

+    mnemonic: 'A',

+    accelerator: shortcut('A')

+)

+

+action(id: 'historyPrevAction',

+    name: 'Previous',

+    closure: controller.&historyPrev,

+    mnemonic: 'P',

+    accelerator: shortcut(KeyEvent.VK_COMMA),

+    smallIcon: imageIcon(resource:"icons/book_previous.png", class:this),

+    shortDescription: 'Previous Groovy Script',

+    enabled: false // controller will enable as needed

+)

+

+action(id: 'historyNextAction',

+    name: 'Next',

+    closure: controller.&historyNext,

+    mnemonic: 'N',

+    accelerator: shortcut(KeyEvent.VK_PERIOD),

+    smallIcon: imageIcon(resource:"icons/book_next.png", class:this),

+    shortDescription: 'Next Groovy Script',

+    enabled: false // controller will enable as needed

+)

+

+action(id: 'clearOutputAction',

+    name: 'Clear Output',

+    closure: controller.&clearOutput,

+    mnemonic: 'O',

+    accelerator: shortcut('W')

+)

+

+action(id: 'runAction',

+    name: 'Run',

+    closure: controller.&runScript,

+    mnemonic: 'R',

+    keyStroke: shortcut('ENTER'),

+    accelerator: shortcut('R'),

+    smallIcon: imageIcon(resource:"icons/script_go.png", class:this),

+    shortDescription: 'Execute Groovy Script'

+)

+

+action(id: 'runSelectionAction',

+    name: 'Run Selection',

+    closure: controller.&runSelectedScript,

+    mnemonic: 'E',

+    keyStroke: shortcut('shift ENTER'),

+    accelerator: shortcut('shift R')

+)

+

+action(id: 'inspectLastAction',

+    name: 'Inspect Last',

+    closure: controller.&inspectLast,

+    mnemonic: 'I',

+    accelerator: shortcut('I')

+)

+

+action(id: 'inspectVariablesAction',

+    name: 'Inspect Variables',

+    closure: controller.&inspectVariables,

+    mnemonic: 'V',

+    accelerator: shortcut('J')

+)

+

+action(id: 'captureStdOutAction',

+    name: 'Capture Standard Output',

+    closure: controller.&captureStdOut,

+    mnemonic: 'C'

+)

+

+action(id: 'fullStackTracesAction',

+    name: 'Show Full Stack Traces',

+    closure: controller.&fullStackTraces,

+    mnemonic: 'F'

+)

+

+action(id:'showToolbarAction',

+    name: 'Show Toolbar',

+    closure: controller.&showToolbar,

+    mnemonic: 'T'

+)

+

+action(id: 'largerFontAction',

+    name: 'Larger Font',

+    closure: controller.&largerFont,

+    mnemonic: 'L',

+    accelerator: shortcut('shift L')

+)

+

+action(id: 'smallerFontAction',

+    name: 'Smaller Font',

+    closure: controller.&smallerFont,

+    mnemonic: 'S',

+    accelerator: shortcut('shift S')

+)

+

+action(id: 'aboutAction',

+    name: 'About',

+    closure: controller.&showAbout,

+    mnemonic: 'A'

+)

+

+action(id: 'interruptAction',

+    name: 'Interrupt',

+    closure: controller.&confirmRunInterrupt

+)

diff --git a/groovy/src/main/groovy/ui/ConsoleIcon.png b/groovy/src/main/groovy/ui/ConsoleIcon.png
new file mode 100644
index 0000000..aec07f0
--- /dev/null
+++ b/groovy/src/main/groovy/ui/ConsoleIcon.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/ConsoleSupport.java b/groovy/src/main/groovy/ui/ConsoleSupport.java
new file mode 100644
index 0000000..37439b5
--- /dev/null
+++ b/groovy/src/main/groovy/ui/ConsoleSupport.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.ui;
+
+import groovy.lang.GroovyShell;
+
+import java.awt.Color;
+
+import javax.swing.JTextPane;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+import javax.swing.text.StyledDocument;
+
+/**
+ * Base class for console
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class ConsoleSupport {
+
+    Style promptStyle;
+    Style commandStyle;
+    Style outputStyle;
+    private GroovyShell shell;
+    int counter;
+
+    protected void addStylesToDocument(JTextPane outputArea) {
+        StyledDocument doc = outputArea.getStyledDocument();
+
+        Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
+
+        Style regular = doc.addStyle("regular", def);
+        StyleConstants.setFontFamily(def, "Monospaced");
+
+        promptStyle = doc.addStyle("prompt", regular);
+        StyleConstants.setForeground(promptStyle, Color.BLUE);
+
+        commandStyle = doc.addStyle("command", regular);
+        StyleConstants.setForeground(commandStyle, Color.MAGENTA);
+
+        outputStyle = doc.addStyle("output", regular);
+        StyleConstants.setBold(outputStyle, true);
+    }
+
+    public Style getCommandStyle() {
+        return commandStyle;
+    }
+
+    public Style getOutputStyle() {
+        return outputStyle;
+    }
+
+    public Style getPromptStyle() {
+        return promptStyle;
+    }
+
+    public GroovyShell getShell() {
+        if (shell == null) {
+            shell = new GroovyShell();
+        }
+        return shell;
+    }
+
+    protected Object evaluate(String text) {
+        String name = "Script" + counter++;
+        try {
+            return getShell().evaluate(text, name);
+        }
+        catch (Exception e) {
+            handleException(text, e);
+            return null;
+        }
+    }
+    
+    protected abstract void handleException(String text, Exception e);
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/ui/ConsoleTextEditor.java b/groovy/src/main/groovy/ui/ConsoleTextEditor.java
new file mode 100644
index 0000000..d48c11a
--- /dev/null
+++ b/groovy/src/main/groovy/ui/ConsoleTextEditor.java
@@ -0,0 +1,216 @@
+/*

+ * UIResourceMgr.java

+ *

+ * Copyright 2004, 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.ui;

+

+import groovy.ui.text.GroovyFilter;

+import groovy.ui.text.StructuredSyntaxResources;

+import groovy.ui.text.TextEditor;

+import groovy.ui.text.TextUndoManager;

+

+import javax.swing.AbstractAction;

+import javax.swing.Action;

+import javax.swing.ActionMap;

+import javax.swing.InputMap;

+import javax.swing.JComponent;

+import javax.swing.JScrollPane;

+import javax.swing.KeyStroke;

+import javax.swing.event.DocumentEvent;

+import javax.swing.event.DocumentListener;

+import javax.swing.text.DefaultStyledDocument;

+import java.awt.datatransfer.DataFlavor;

+import java.awt.datatransfer.Transferable;

+import java.awt.event.ActionEvent;

+import java.awt.event.InputEvent;

+import java.awt.event.KeyEvent;

+import java.awt.print.PrinterJob;

+import java.beans.PropertyChangeEvent;

+import java.beans.PropertyChangeListener;

+import java.io.File;

+

+/**

+ * Component which provides a styled editor for the console.

+ *

+ * @version $Id$

+ * @author hippy

+ */

+public class ConsoleTextEditor extends JScrollPane {

+

+    private static final PrinterJob PRINTER_JOB = PrinterJob.getPrinterJob();

+        

+    private TextEditor textEditor = new TextEditor(true, true, true);

+    

+    private UndoAction undoAction = new UndoAction();

+    private RedoAction redoAction = new RedoAction();

+    private PrintAction printAction = new PrintAction();

+    

+    private boolean editable = true;

+    

+    private File textFile;

+    

+    private TextUndoManager undoManager;

+

+    /**

+     * Creates a new instance of ConsoleTextEditor

+     */

+    public ConsoleTextEditor() {        

+        textEditor.setFont(StructuredSyntaxResources.EDITOR_FONT);

+        

+        setWheelScrollingEnabled(true);

+

+        setViewportView(textEditor);

+        

+        textEditor.setDragEnabled(editable);

+        

+        initActions();

+        

+        DefaultStyledDocument doc = new DefaultStyledDocument();

+        doc.setDocumentFilter(new GroovyFilter(doc));

+        textEditor.setDocument(doc);

+

+        // create and add the undo/redo manager

+        this.undoManager = new TextUndoManager();

+        doc.addUndoableEditListener(undoManager);

+        

+        // add the undo actions

+        undoManager.addPropertyChangeListener(undoAction);

+        undoManager.addPropertyChangeListener(redoAction);

+

+        doc.addDocumentListener(undoAction);

+        doc.addDocumentListener(redoAction);

+        

+        InputMap im = textEditor.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);

+        KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK, false);

+        im.put(ks, StructuredSyntaxResources.UNDO);

+        ActionMap am = textEditor.getActionMap();

+        am.put(StructuredSyntaxResources.UNDO, undoAction);

+

+        ks = KeyStroke.getKeyStroke(KeyEvent.VK_Y, InputEvent.CTRL_MASK, false);

+        im.put(ks, StructuredSyntaxResources.REDO);

+        am.put(StructuredSyntaxResources.REDO, redoAction);

+

+        ks = KeyStroke.getKeyStroke(KeyEvent.VK_P, InputEvent.CTRL_MASK, false);

+        im.put(ks, StructuredSyntaxResources.PRINT);

+        am.put(StructuredSyntaxResources.PRINT, printAction);

+    }

+    

+    public boolean clipBoardAvailable() {

+        Transferable t = StructuredSyntaxResources.SYSTEM_CLIPBOARD.getContents(this);

+        return t.isDataFlavorSupported(DataFlavor.stringFlavor);

+    }

+    

+    public TextEditor getTextEditor() {

+        return textEditor;

+    }

+

+    protected void initActions() {

+        ActionMap map = getActionMap();

+        

+        PrintAction printAction = new PrintAction();

+        map.put(StructuredSyntaxResources.PRINT, printAction);

+    }

+        

+    private class PrintAction extends AbstractAction {

+        

+        public PrintAction() {

+            setEnabled(true);

+        }

+        

+        public void actionPerformed(ActionEvent ae) {

+            PRINTER_JOB.setPageable(textEditor);

+            

+            try {

+                if (PRINTER_JOB.printDialog()) {

+                    PRINTER_JOB.print();

+                }

+            }

+            catch (Exception e) {

+                e.printStackTrace();

+            }

+        }

+    } // end ConsoleTextEditor.PrintAction

+    

+    private class RedoAction extends UpdateCaretListener implements PropertyChangeListener {

+

+        public RedoAction() {

+            setEnabled(false);

+        }

+        

+        public void actionPerformed(ActionEvent ae) {

+            undoManager.redo();

+            setEnabled(undoManager.canRedo());

+            undoAction.setEnabled(undoManager.canUndo());

+            super.actionPerformed(ae);

+        }

+        

+        public void propertyChange(PropertyChangeEvent pce) {

+            setEnabled(undoManager.canRedo());

+        }

+    } // end ConsoleTextEditor.RedoAction

+    

+    private abstract class UpdateCaretListener extends AbstractAction implements DocumentListener {

+        

+        protected int lastUpdate;

+        

+        public void changedUpdate(DocumentEvent de) {

+        }

+        

+        public void insertUpdate(DocumentEvent de) {

+            lastUpdate = de.getOffset() + de.getLength();

+        }

+        

+        public void removeUpdate(DocumentEvent de) {

+            lastUpdate = de.getOffset();

+        }

+        

+        public void actionPerformed(ActionEvent ae) {

+            textEditor.setCaretPosition(lastUpdate);

+        }

+    }

+    

+    private class UndoAction extends UpdateCaretListener  implements PropertyChangeListener {

+

+        public UndoAction() {

+            setEnabled(false);

+        }

+        

+        public void actionPerformed(ActionEvent ae) {

+            undoManager.undo();

+            setEnabled(undoManager.canUndo());

+            redoAction.setEnabled(undoManager.canRedo());

+            super.actionPerformed(ae);

+        }        

+        

+        public void propertyChange(PropertyChangeEvent pce) {

+            setEnabled(undoManager.canUndo());

+        }

+    }

+

+    public Action getUndoAction() {

+        return undoAction;

+    }

+

+    public Action getRedoAction() {

+        return redoAction;

+    }

+

+    public Action getPrintAction() {

+        return printAction;

+    }

+

+}

diff --git a/groovy/src/main/groovy/ui/ConsoleView.groovy b/groovy/src/main/groovy/ui/ConsoleView.groovy
new file mode 100644
index 0000000..34e610a
--- /dev/null
+++ b/groovy/src/main/groovy/ui/ConsoleView.groovy
@@ -0,0 +1,108 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.ui

+

+import groovy.ui.view.*

+import javax.swing.UIManager

+import static javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE

+import javax.swing.event.DocumentListener

+

+switch (UIManager.getSystemLookAndFeelClassName()) {

+    case 'com.sun.java.swing.plaf.windows.WindowsLookAndFeel':

+    case 'com.sun.java.swing.plaf.windows.WindowsClassicLookAndFeel':

+        build(WindowsDefaults)

+        break

+

+    case 'apple.laf.AquaLookAndFeel':

+        build(MacOSXDefaults)

+        break

+

+    case 'com.sun.java.swing.plaf.gtk.GTKLookAndFeel':

+        build(GTKDefaults)

+        break

+

+    default:

+        build(Defaults)

+        break

+}

+

+frame(

+    title: 'GroovyConsole',

+    location: [100,100], // in groovy 2.0 use platform default location

+    iconImage: imageIcon("/groovy/ui/ConsoleIcon.png").image,

+    defaultCloseOperation: DO_NOTHING_ON_CLOSE,

+    id:'consoleFrame'

+) {

+    build(menuBarClass)

+

+    build(contentPaneClass)

+

+    build(toolBarClass)

+

+    build(statusBarClass)

+

+    dialog(title: 'Groovy executing',

+        modal: true,

+        id:'runWaitDialog',

+        pack:true

+    ) {

+        vbox(border: emptyBorder(6)) {

+            label(text: "Groovy is now executing. Please wait.", alignmentX: 0.5f)

+            vstrut()

+            button(interruptAction,

+                margin: [10, 20, 10, 20],

+                alignmentX: 0.5f

+            )

+        }

+    }

+}

+

+

+controller.promptStyle = promptStyle

+controller.commandStyle = commandStyle

+controller.outputStyle = outputStyle

+controller.resultStyle = resultStyle

+

+// add the window close handler

+consoleFrame.windowClosing = controller.&exit

+

+// link in references to the controller

+controller.inputEditor = inputEditor

+controller.inputArea = inputEditor.textEditor

+controller.outputArea = outputArea

+controller.statusLabel = status

+controller.frame = consoleFrame

+controller.runWaitDialog = runWaitDialog

+controller.rowNumAndColNum = rowNumAndColNum

+controller.toolbar = toolbar

+

+// link actions

+controller.saveAction = saveAction

+controller.prevHistoryAction = historyPrevAction

+controller.nextHistoryAction = historyNextAction

+controller.fullStackTracesAction = fullStackTracesAction

+controller.showToolbarAction = showToolbarAction

+

+// some more UI linkage

+controller.inputArea.addCaretListener(controller)

+controller.inputArea.document.addDocumentListener({ controller.setDirty(true) } as DocumentListener)

+controller.rootElement = inputArea.document.defaultRootElement

+

+

+

+// don't send any return value from the view, all items should be referenced via the bindings

+return null

diff --git a/groovy/src/main/groovy/ui/GroovyMain.java b/groovy/src/main/groovy/ui/GroovyMain.java
new file mode 100644
index 0000000..38c01e7
--- /dev/null
+++ b/groovy/src/main/groovy/ui/GroovyMain.java
@@ -0,0 +1,498 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.ui;
+
+import groovy.lang.GroovyShell;
+import groovy.lang.Script;
+
+import java.io.*;
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.PosixParser;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+
+/**
+ * A Command line to execute groovy.
+ *
+ * @author Jeremy Rayner
+ * @author Yuri Schimke
+ * @version $Revision$
+ */
+public class GroovyMain {
+
+    // arguments to the script
+    private List args;
+
+    // is this a file on disk
+    private boolean isScriptFile;
+
+    // filename or content of script
+    private String script;
+
+    // process args as input files
+    private boolean processFiles;
+
+    // edit input files in place
+    private boolean editFiles;
+
+    // automatically output the result of each script
+    private boolean autoOutput;
+
+    // automatically split each line using the splitpattern
+    private boolean autoSplit;
+
+    // The pattern used to split the current line
+    private String splitPattern = " ";
+
+    // process sockets
+    private boolean processSockets;
+
+    // port to listen on when processing sockets
+    private int port;
+
+    // backup input files with extension
+    private String backupExtension;
+
+    // do you want full stack traces in script exceptions?
+    private boolean debug = false;
+
+    // Compiler configuration, used to set the encodings of the scripts/classes
+    private CompilerConfiguration conf = new CompilerConfiguration(System.getProperties());
+
+    /**
+     * Main CLI interface.
+     *
+     * @param args all command line args.
+     */
+    public static void main(String args[]) {
+        processArgs(args, System.out);
+    }
+
+    // package-level visibility for testing purposes (just usage/errors at this stage)
+    // TODO: should we have an 'err' printstream too for ParseException?
+    static void processArgs(String[] args, final PrintStream out) {
+        Options options = buildOptions();
+
+        try {
+            CommandLine cmd = parseCommandLine(options, args);
+
+            if (cmd.hasOption('h')) {
+                printHelp(out, options);
+            } else if (cmd.hasOption('v')) {
+                String version = InvokerHelper.getVersion();
+                out.println("Groovy Version: " + version + " JVM: " + System.getProperty("java.vm.version"));
+            } else {
+                // If we fail, then exit with an error so scripting frameworks can catch it
+                // TODO: pass printstream(s) down through process
+                if (!process(cmd)) {
+                    System.exit(1);
+                }
+            }
+        } catch (ParseException pe) {
+            out.println("error: " + pe.getMessage());
+            printHelp(out, options);
+        }
+    }
+
+    private static void printHelp(PrintStream out, Options options) {
+        HelpFormatter formatter = new HelpFormatter();
+        PrintWriter pw = new PrintWriter(out);
+
+        formatter.printHelp(
+            pw,
+            80,
+            "groovy [options] [args]",
+            "options:",
+            options,
+            2,
+            4,
+            null, // footer
+            false);
+       
+        pw.flush();
+    }
+
+    /**
+     * Parse the command line.
+     *
+     * @param options the options parser.
+     * @param args    the command line args.
+     * @return parsed command line.
+     * @throws ParseException if there was a problem.
+     */
+    private static CommandLine parseCommandLine(Options options, String[] args) throws ParseException {
+        CommandLineParser parser = new PosixParser();
+        return parser.parse(options, args, true);
+    }
+
+    /**
+     * Build the options parser.  Has to be synchronized because of the way Options are constructed.
+     *
+     * @return an options parser.
+     */
+    private static synchronized Options buildOptions() {
+        Options options = new Options();
+
+        options.addOption(
+            OptionBuilder.withLongOpt("define").
+                withDescription("define a system property").
+                hasArg(true).
+                withArgName("name=value").
+                create('D')
+        );
+        options.addOption(
+            OptionBuilder.hasArg(false)
+            .withDescription("usage information")
+            .withLongOpt("help")
+            .create('h'));
+        options.addOption(
+            OptionBuilder.hasArg(false)
+            .withDescription("debug mode will print out full stack traces")
+            .withLongOpt("debug")
+            .create('d'));
+        options.addOption(
+            OptionBuilder.hasArg(false)
+            .withDescription("display the Groovy and JVM versions")
+            .withLongOpt("version")
+            .create('v'));
+        options.addOption(
+            OptionBuilder.withArgName("charset")
+            .hasArg()
+            .withDescription("specify the encoding of the files")
+            .withLongOpt("encoding")
+            .create('c'));
+        options.addOption(
+            OptionBuilder.withArgName("script")
+            .hasArg()
+            .withDescription("specify a command line script")
+            .create('e'));
+        options.addOption(
+            OptionBuilder.withArgName("extension")
+            .hasOptionalArg()
+            .withDescription("modify files in place, create backup if extension is given (e.g. \'.bak\')")
+            .create('i'));
+        options.addOption(
+            OptionBuilder.hasArg(false)
+            .withDescription("process files line by line")
+            .create('n'));
+        options.addOption(
+            OptionBuilder.hasArg(false)
+            .withDescription("process files line by line and print result")
+            .create('p'));
+        options.addOption(
+            OptionBuilder.withArgName("port")
+            .hasOptionalArg()
+            .withDescription("listen on a port and process inbound lines")
+            .create('l'));
+        options.addOption(
+            OptionBuilder.withArgName("splitPattern")
+            .hasOptionalArg()
+            .withDescription("automatically split current line (defaults to '\\s')")
+            .withLongOpt("autosplit")
+            .create('a'));
+
+        return options;
+    }
+
+    private static void setSystemPropertyFrom(final String nameValue) {
+        if(nameValue==null) throw new IllegalArgumentException("argument should not be null");
+
+        String name, value;
+        int i = nameValue.indexOf("=");
+
+        if (i == -1) {
+            name = nameValue;
+            value = Boolean.TRUE.toString();
+        }
+        else {
+            name = nameValue.substring(0, i);
+            value = nameValue.substring(i + 1, nameValue.length());
+        }
+        name = name.trim();
+
+        System.setProperty(name, value);
+    }
+
+    /**
+     * Process the users request.
+     *
+     * @param line the parsed command line.
+     * @throws ParseException if invalid options are chosen
+     */
+    private static boolean process(CommandLine line) throws ParseException {
+        GroovyMain main = new GroovyMain();
+
+        List args = line.getArgList();
+        
+        if (line.hasOption('D')) {
+            String[] values = line.getOptionValues('D');
+
+            for (int i=0; i<values.length; i++) {
+                setSystemPropertyFrom(values[i]);
+            }
+        }
+
+        // add the ability to parse scripts with a specified encoding
+        main.conf.setSourceEncoding(line.getOptionValue('c',main.conf.getSourceEncoding()));
+
+        main.isScriptFile = !line.hasOption('e');
+        main.debug = line.hasOption('d');
+        main.conf.setDebug(main.debug);
+        main.processFiles = line.hasOption('p') || line.hasOption('n');
+        main.autoOutput = line.hasOption('p');
+        main.editFiles = line.hasOption('i');
+        if (main.editFiles) {
+            main.backupExtension = line.getOptionValue('i');
+        }
+        main.autoSplit = line.hasOption('a');
+        String sp = line.getOptionValue('a');
+        if (sp != null)
+            main.splitPattern = sp;
+
+        if (main.isScriptFile) {
+            if (args.isEmpty())
+                throw new ParseException("neither -e or filename provided");
+
+            main.script = (String) args.remove(0);
+            if (main.script.endsWith(".java"))
+                throw new ParseException("error: cannot compile file with .java extension: " + main.script);
+        } else {
+            main.script = line.getOptionValue('e');
+        }
+
+        main.processSockets = line.hasOption('l');
+        if (main.processSockets) {
+            String p = line.getOptionValue('l', "1960"); // default port to listen to
+            main.port = Integer.parseInt(p);
+        }
+        main.args = args;
+
+        return main.run();
+    }
+
+
+    /**
+     * Run the script.
+     */
+    private boolean run() {
+        try {
+            if (processSockets) {
+                processSockets();
+            } else if (processFiles) {
+                processFiles();
+            } else {
+                processOnce();
+            }
+            return true;
+        } catch (CompilationFailedException e) {
+            System.err.println(e);
+            return false;
+        } catch (Throwable e) {
+            if (e instanceof InvokerInvocationException) {
+                InvokerInvocationException iie = (InvokerInvocationException) e;
+                e = iie.getCause();
+            }
+            System.err.println("Caught: " + e);
+            if (debug) {
+                e.printStackTrace();
+            } else {
+                StackTraceElement[] stackTrace = e.getStackTrace();
+                for (int i = 0; i < stackTrace.length; i++) {
+                    StackTraceElement element = stackTrace[i];
+                    String fileName = element.getFileName();
+                    if (fileName!=null && !fileName.endsWith(".java")) {
+                        System.err.println("\tat " + element);
+                    }
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Process Sockets.
+     */
+    private void processSockets() throws CompilationFailedException, IOException {
+        GroovyShell groovy = new GroovyShell(conf);
+        //check the script is currently valid before starting a server against the script
+        if (isScriptFile) {
+            groovy.parse(new FileInputStream(huntForTheScriptFile(script)));
+        } else {
+            groovy.parse(script);
+        }
+        new GroovySocketServer(groovy, isScriptFile, script, autoOutput, port);
+    }
+
+    /**
+     * Hunt for the script file, doesn't bother if it is named precisely.
+     *
+     * Tries in this order:
+     * - actual supplied name
+     * - name.groovy
+     * - name.gvy
+     * - name.gy
+     * - name.gsh
+     */
+    public File huntForTheScriptFile(String scriptFileName) {
+        File scriptFile = new File(scriptFileName);
+        String[] standardExtensions = {".groovy",".gvy",".gy",".gsh"};
+        int i = 0;
+        while (i < standardExtensions.length && !scriptFile.exists()) {
+            scriptFile = new File(scriptFileName + standardExtensions[i]);
+            i++;
+        }
+        // if we still haven't found the file, point back to the originally specified filename
+        if (!scriptFile.exists()) {
+            scriptFile = new File(scriptFileName);
+        }
+        return scriptFile;
+    }
+
+    /**
+     * Process the input files.
+     */
+    private void processFiles() throws CompilationFailedException, IOException {
+        GroovyShell groovy = new GroovyShell(conf);
+
+        Script s;
+
+        if (isScriptFile) {
+            s = groovy.parse(huntForTheScriptFile(script));
+        } else {
+            s = groovy.parse(script, "main");
+        }
+
+        if (args.isEmpty()) {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
+            PrintWriter writer = new PrintWriter(System.out);
+
+            try {
+                processReader(s, reader, writer);
+            } finally {
+                reader.close();
+                writer.close();
+            }
+
+        } else {
+            Iterator i = args.iterator();
+            while (i.hasNext()) {
+                String filename = (String) i.next();
+                File file = huntForTheScriptFile(filename);
+                processFile(s, file);
+            }
+        }
+    }
+
+    /**
+     * Process a single input file.
+     *
+     * @param s    the script to execute.
+     * @param file the input file.
+     */
+    private void processFile(Script s, File file) throws IOException {
+        if (!file.exists())
+            throw new FileNotFoundException(file.getName());
+
+        if (!editFiles) {
+            BufferedReader reader = new BufferedReader(new FileReader(file));
+            try {
+                PrintWriter writer = new PrintWriter(System.out);
+                processReader(s, reader, writer);
+                writer.flush();
+            } finally {
+                reader.close();
+            }
+        } else {
+            File backup;
+            if (backupExtension == null) {
+                backup = File.createTempFile("groovy_", ".tmp");
+                backup.deleteOnExit();
+            } else {
+                backup = new File(file.getPath() + backupExtension);
+            }
+            backup.delete();
+            if (!file.renameTo(backup))
+                throw new IOException("unable to rename " + file + " to " + backup);
+
+            BufferedReader reader = new BufferedReader(new FileReader(backup));
+            try {
+                PrintWriter writer = new PrintWriter(new FileWriter(file));
+                try {
+                    processReader(s, reader, writer);
+                } finally {
+                    writer.close();
+                }
+            } finally {
+                reader.close();
+            }
+        }
+    }
+
+    /**
+     * Process a script against a single input file.
+     *
+     * @param s      script to execute.
+     * @param reader input file.
+     * @param pw     output sink.
+     */
+    private void processReader(Script s, BufferedReader reader, PrintWriter pw) throws IOException {
+        String line;
+        String lineCountName = "count";
+        s.setProperty(lineCountName, BigInteger.ZERO);
+        String autoSplitName = "split";
+        s.setProperty("out", pw);
+
+        while ((line = reader.readLine()) != null) {
+            s.setProperty("line", line);
+            s.setProperty(lineCountName, ((BigInteger)s.getProperty(lineCountName)).add(BigInteger.ONE));
+
+            if(autoSplit) {
+                s.setProperty(autoSplitName, line.split(splitPattern));
+            }
+
+            Object o = s.run();
+
+            if (autoOutput && o != null) {
+                pw.println(o);
+            }
+        }
+    }
+    
+    /**
+     * Process the standard, single script with args.
+     */
+    private void processOnce() throws CompilationFailedException, IOException {
+        GroovyShell groovy = new GroovyShell(conf);
+
+        if (isScriptFile) {
+            groovy.run(huntForTheScriptFile(script), args);
+        }
+        else {
+            groovy.run(script, "script_from_command_line", args);
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/ui/GroovySocketServer.java b/groovy/src/main/groovy/ui/GroovySocketServer.java
new file mode 100644
index 0000000..8bfeb9b
--- /dev/null
+++ b/groovy/src/main/groovy/ui/GroovySocketServer.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.ui;
+
+import groovy.lang.GroovyShell;
+import groovy.lang.Script;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.URL;
+
+/**
+ * Simple server that executes supplied script against a socket
+ *
+ * @version $Id$
+ * @author Jeremy Rayner
+ */
+public class GroovySocketServer implements Runnable {
+    private URL url;
+    private GroovyShell groovy;
+    private boolean isScriptFile;
+    private String scriptFilenameOrText;
+    private boolean autoOutput;
+    
+    public GroovySocketServer(GroovyShell groovy, boolean isScriptFile, String scriptFilenameOrText, boolean autoOutput, int port) {
+        this.groovy = groovy;
+        this.isScriptFile = isScriptFile;
+        this.scriptFilenameOrText = scriptFilenameOrText;
+        this.autoOutput = autoOutput;
+        try {
+            url = new URL("http", InetAddress.getLocalHost().getHostAddress(), port, "/");
+            System.out.println("groovy is listening on port " + port);
+        } catch (IOException e) { 
+            e.printStackTrace();
+        }
+        new Thread(this).start();
+    }
+
+    public void run() {
+        try {
+            ServerSocket serverSocket = new ServerSocket(url.getPort());
+            while (true) {
+                // Create one script per socket connection.
+                // This is purposefully not caching the Script
+                // so that the script source file can be changed on the fly,
+                // as each connection is made to the server.
+                Script script;
+                if (isScriptFile) {
+                    GroovyMain gm = new GroovyMain();
+                    script = groovy.parse(new FileInputStream(gm.huntForTheScriptFile(scriptFilenameOrText)));
+                } else {
+                    script = groovy.parse(scriptFilenameOrText);
+                }
+                new GroovyClientConnection(script, autoOutput, serverSocket.accept());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+    
+    class GroovyClientConnection implements Runnable {
+        private Script script;
+        private Socket socket;
+        private BufferedReader reader;
+        private PrintWriter writer;
+        private boolean autoOutputFlag;
+    
+        GroovyClientConnection(Script script, boolean autoOutput,Socket socket) throws IOException {
+            this.script = script;
+            this.autoOutputFlag = autoOutput;
+            this.socket = socket;
+            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+            writer = new PrintWriter(socket.getOutputStream());
+            new Thread(this, "Groovy client connection - " + socket.getInetAddress().getHostAddress()).start();
+        }
+        public void run() {
+            try {
+                String line = null;
+                script.setProperty("out", writer);
+                script.setProperty("socket", socket);
+                script.setProperty("init", Boolean.TRUE);
+                while ((line = reader.readLine()) != null) {
+                    // System.out.println(line);
+                    script.setProperty("line", line);
+                    Object o = script.run();
+                    script.setProperty("init", Boolean.FALSE);
+                    if (o != null) {
+                        if ("success".equals(o)) {
+                            break; // to close sockets gracefully etc...
+                        } else {
+                            if (autoOutputFlag) {
+                                writer.println(o);
+                            }
+                        }
+                    }
+                    writer.flush();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                try {
+                    writer.flush();
+                    writer.close();
+                } finally {
+                    try {
+                        socket.close();
+                    } catch (IOException e3) {
+                        e3.printStackTrace();
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/ui/HistoryRecord.groovy b/groovy/src/main/groovy/ui/HistoryRecord.groovy
new file mode 100644
index 0000000..0360ed5
--- /dev/null
+++ b/groovy/src/main/groovy/ui/HistoryRecord.groovy
@@ -0,0 +1,37 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.ui

+

+

+class HistoryRecord {

+    String allText

+    int selectionStart

+    int selectionEnd

+    String scriptName

+    Object result

+    Throwable exception

+

+    public String getTextToRun(boolean useSelection) {

+        if (useSelection && selectionStart != selectionEnd) {

+            return allText[selectionStart ..< selectionEnd]

+        }

+        return allText

+    }

+

+    public Object getValue() {

+        return exception ? exception : result

+    }

+}

diff --git a/groovy/src/main/groovy/ui/InteractiveShell.java b/groovy/src/main/groovy/ui/InteractiveShell.java
new file mode 100644
index 0000000..8de2df4
--- /dev/null
+++ b/groovy/src/main/groovy/ui/InteractiveShell.java
@@ -0,0 +1,844 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.ui;
+
+import groovy.lang.Binding;
+import groovy.lang.Closure;
+import groovy.lang.GroovyShell;
+import org.codehaus.groovy.tools.shell.util.MessageSource;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.tools.ErrorReporter;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.PosixParser;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.HelpFormatter;
+
+import jline.ConsoleReader;
+import jline.SimpleCompletor;
+
+//
+// TODO: See if there is any reason why this class is implemented in Java instead of Groovy, and if there
+//       is none, then port it over ;-)
+//
+
+//
+// NOTE: After GShell becomes a little more mature, this shell could be easily implemented as a set of GShell
+//       commands, and would inherit a lot of functionality and could be extended easily to allow groovysh
+//       to become very, very powerful
+//
+
+/**
+ * A simple interactive shell for evaluating groovy expressions on the command line (aka. groovysh).
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:cpoirier@dreaming.org"   >Chris Poirier</a>
+ * @author Yuri Schimke
+ * @author Brian McCallistair
+ * @author Guillaume Laforge
+ * @author Dierk Koenig, include the inspect command, June 2005
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ *
+ * @version $Revision$
+ */
+public class InteractiveShell
+    implements Runnable
+{
+    private static final String NEW_LINE = System.getProperty("line.separator");
+    private static final MessageSource MESSAGES = new MessageSource(InteractiveShell.class);
+
+    private final GroovyShell shell;
+    private final InputStream in; // FIXME: This doesn't really need to be a field, but hold on to it for now
+    private final PrintStream out;
+    private final PrintStream err;
+    private final ConsoleReader reader;
+
+    private Object lastResult;
+    private Closure beforeExecution;
+    private Closure afterExecution;
+
+    /**
+     * Entry point when called directly.
+     */
+    public static void main(final String args[]) {
+        try {
+            processCommandLineArguments(args);
+
+            final InteractiveShell groovy = new InteractiveShell();
+            groovy.run();
+        }
+        catch (Exception e) {
+            System.err.println("FATAL: " + e);
+            e.printStackTrace();
+            System.exit(1);
+        }
+
+        System.exit(0);
+    }
+
+    /**
+     * Process cli args when the shell is invoked via main().
+     *
+     * @noinspection AccessStaticViaInstance
+     */
+    private static void processCommandLineArguments(final String[] args) throws Exception {
+        assert args != null;
+
+        //
+        // TODO: Let this take a single, optional argument which is a file or URL to run?
+        //
+        
+        Options options = new Options();
+
+        options.addOption(OptionBuilder.withLongOpt("help")
+            .withDescription(MESSAGES.getMessage("cli.option.help.description"))
+            .create('h'));
+
+        options.addOption(OptionBuilder.withLongOpt("version")
+            .withDescription(MESSAGES.getMessage("cli.option.version.description"))
+            .create('V'));
+
+        //
+        // TODO: Add more options, maybe even add an option to prime the buffer from a URL or File?
+        //
+        
+        //
+        // FIXME: This does not currently barf on unsupported options short options, though it does for long ones.
+        //        Same problem with commons-cli 1.0 and 1.1
+        //
+        
+        CommandLineParser parser = new PosixParser();
+        CommandLine line = parser.parse(options, args, true);
+        String[] lineargs = line.getArgs();
+
+        // Puke if there were arguments, we don't support any right now
+        if (lineargs.length != 0) {
+            System.err.println(MESSAGES.format("cli.info.unexpected_args", new Object[] { DefaultGroovyMethods.join(lineargs, " ") }));
+            System.exit(1);
+        }
+
+        PrintWriter writer = new PrintWriter(System.out);
+
+        if (line.hasOption('h')) {
+            HelpFormatter formatter = new HelpFormatter();
+            formatter.printHelp(
+                writer,
+                80, // width
+                "groovysh [options]",
+                "",
+                options,
+                4, // left pad
+                4, // desc pad
+                "",
+                false); // auto usage
+
+            writer.flush();
+            System.exit(0);
+        }
+
+        if (line.hasOption('V')) {
+            writer.println(MESSAGES.format("cli.info.version", new Object[] { InvokerHelper.getVersion() }));
+            writer.flush();
+            System.exit(0);
+        }
+    }
+
+    /**
+     * Default constructor, initializes uses new binding and system streams.
+     */
+    public InteractiveShell() throws IOException {
+        this(System.in, System.out, System.err);
+    }
+
+    /**
+     * Constructs a new InteractiveShell instance
+     *
+     * @param in The input stream to use
+     * @param out The output stream to use
+     * @param err The error stream to use
+     */
+    public InteractiveShell(final InputStream in, final PrintStream out, final PrintStream err) throws IOException {
+        this(null, new Binding(), in, out, err);
+    }
+
+    /**
+     * Constructs a new InteractiveShell instance
+     * 
+     * @param binding The binding instance
+     * @param in The input stream to use
+     * @param out The output stream to use
+     * @param err The error stream to use
+     */    
+    public InteractiveShell(final Binding binding, final InputStream in, final PrintStream out, final PrintStream err) throws IOException {
+    	this(null, binding, in, out, err);
+    }
+    
+    /**
+     * Constructs a new InteractiveShell instance
+     * 
+     * @param parent The parent ClassLoader
+     * @param binding The binding instance
+     * @param in The input stream to use
+     * @param out The output stream to use
+     * @param err The error stream to use
+     */
+    public InteractiveShell(final ClassLoader parent, final Binding binding, final InputStream in, final PrintStream out, final PrintStream err) throws IOException {
+        assert binding != null;
+        assert in != null;
+        assert out != null;
+        assert err != null;
+
+        this.in = in;
+        this.out = out;
+        this.err = err;
+
+        // Initialize the JLine console input reader
+        Writer writer = new OutputStreamWriter(out);
+        reader = new ConsoleReader(in, writer);
+        reader.setDefaultPrompt("groovy> ");
+
+        // Add some completors to fancy things up
+        reader.addCompletor(new CommandNameCompletor());
+
+        if (parent != null) {
+            shell = new GroovyShell(parent, binding);
+        }
+        else {
+            shell = new GroovyShell(binding);
+        }        
+
+        // Add some default variables to the shell
+        Map map = shell.getContext().getVariables();
+
+        //
+        // FIXME: Um, is this right?  Only set the "shell" var in the context if its set already?
+        //
+        
+        if (map.get("shell") != null) {
+            map.put("shell", shell);
+        }
+    }    
+
+    //---------------------------------------------------------------------------
+    // COMMAND LINE PROCESSING LOOP
+
+    //
+    // TODO: Add a general error display handler, and probably add a "ERROR: " prefix to the result for clarity ?
+    //       Maybe add one for WARNING's too?
+    //
+    
+    /**
+     * Reads commands and statements from input stream and processes them.
+     */
+    public void run() {
+        // Display the startup banner
+        out.println(MESSAGES.format("startup_banner.0", new Object[] { InvokerHelper.getVersion(), System.getProperty("java.vm.version") }));
+        out.println(MESSAGES.getMessage("startup_banner.1"));
+
+        while (true) {
+            // Read a code block to evaluate; this will deal with basic error handling
+            final String code = read();
+
+            // If we got a null, then quit
+            if (code == null) {
+                break;
+            }
+
+            reset();
+
+            // Evaluate the code block if it was parsed
+            if (code.length() > 0) {
+                try {
+                    if (beforeExecution != null) {
+                        beforeExecution.call();
+                    }
+
+                    lastResult = shell.evaluate(code);
+                    
+                    if (afterExecution != null) {
+                        afterExecution.call();
+                    }
+
+                    // Shows the result of the evaluated code
+                    out.print("===> ");
+                    out.println(lastResult);
+                }
+                catch (CompilationFailedException e) {
+                    err.println(e);
+                }
+                catch (Throwable e) {
+                    // Unroll invoker exceptions
+                    if (e instanceof InvokerInvocationException) {
+                        e = e.getCause();
+                    }
+                    
+                    filterAndPrintStackTrace(e);
+                }
+            }
+        }
+    }
+
+    /**
+     * A closure that is executed before the exection of a given script
+     *
+     * @param beforeExecution The closure to execute
+     */
+    public void setBeforeExecution(final Closure beforeExecution) {
+        this.beforeExecution = beforeExecution;
+    }
+
+    /**
+     * A closure that is executed after the execution of the last script. The result of the
+     * execution is passed as the first argument to the closure (the value of 'it')
+     *
+     * @param afterExecution The closure to execute
+     */
+    public void setAfterExecution(final Closure afterExecution) {
+        this.afterExecution = afterExecution;
+    }
+
+    /**
+     * Filter stacktraces to show only relevant lines of the exception thrown.
+     *
+     * @param cause the throwable whose stacktrace needs to be filtered
+     */
+    private void filterAndPrintStackTrace(final Throwable cause) {
+        assert cause != null;
+
+        //
+        // TODO: Use message...
+        //
+        
+        err.print("ERROR: ");
+        err.println(cause);
+
+        cause.printStackTrace(err);
+
+        //
+        // FIXME: What is the point of this?  AFAICT, this just produces crappy/corrupt traces and is completely useless
+        //
+        
+//        StackTraceElement[] stackTrace = e.getStackTrace();
+//
+//        for (int i = 0; i < stackTrace.length; i++) {
+//            StackTraceElement element = stackTrace[i];
+//            String fileName = element.getFileName();
+//
+//            if ((fileName==null || (!fileName.endsWith(".java")) && (!element.getClassName().startsWith("gjdk")))) {
+//                err.print("\tat ");
+//                err.println(element);
+//            }
+//        }
+    }
+
+    //---------------------------------------------------------------------------
+    // COMMAND LINE PROCESSING MACHINERY
+
+    /** The statement text accepted to date */
+    private StringBuffer accepted = new StringBuffer();
+
+    /** A line of statement text not yet accepted */
+    private String pending;
+
+    //
+    // FIXME: Doesn't look like 'line' is really used/needed anywhere... could drop it, or perhaps
+    //        could use it to update the prompt er something to show the buffer size?
+    //
+
+    /** The current line number */
+    private int line;
+    
+    /** Set to force clear of accepted */
+    private boolean stale = false;
+
+    /** A SourceUnit used to check the statement */
+    private SourceUnit parser;
+
+    /** Any actual syntax error caught during parsing */
+    private Exception error;
+
+    /**
+     * Resets the command-line processing machinery after use.
+     */
+    protected void reset() {
+        stale = true;
+        pending = null;
+        line = 1;
+        parser = null;
+        error = null;
+    }
+
+    //
+    // FIXME: This Javadoc is not correct... read() will return the full code block read until "go"
+    //
+    
+    /**
+     * Reads a single statement from the command line.  Also identifies
+     * and processes command shell commands.  Returns the command text
+     * on success, or null when command processing is complete.
+     * 
+     * NOTE: Changed, for now, to read until 'execute' is issued.  At
+     * 'execute', the statement must be complete.
+     */
+    protected String read() {
+        reset();
+        
+        boolean complete = false;
+        boolean done = false;
+        
+        while (/* !complete && */ !done) {
+            // Read a line.  If IOException or null, or command "exit", terminate processing.
+            try {
+                pending = reader.readLine();
+            }
+            catch (IOException e) {
+                //
+                // FIXME: Shouldn't really eat this exception, may be something we need to see... ?
+                //
+            }
+
+            // If result is null then we are shutting down
+            if (pending == null) {
+                return null;
+            }
+
+            // First up, try to process the line as a command and proceed accordingly
+            // Trim what we have for use in command bits, so things like "help " actually show the help screen
+            String command = pending.trim();
+
+            if (COMMAND_MAPPINGS.containsKey(command)) {
+                int code = ((Integer)COMMAND_MAPPINGS.get(command)).intValue();
+
+                switch (code) {
+                    case COMMAND_ID_EXIT:
+                        return null;
+                    
+                    case COMMAND_ID_HELP:
+                        displayHelp();
+                        break;
+
+                    case COMMAND_ID_DISCARD:
+                        reset();
+                        done = true;
+                        break;
+
+                    case COMMAND_ID_DISPLAY:
+                        displayStatement();
+                        break;
+
+                    case COMMAND_ID_EXPLAIN:
+                        explainStatement();
+                        break;
+
+                    case COMMAND_ID_BINDING:
+                        displayBinding();
+                        break;
+
+                    case COMMAND_ID_EXECUTE:
+                        if (complete) {
+                            done = true;
+                        }
+                        else {
+                            err.println(MESSAGES.getMessage("command.execute.not_complete"));
+                        }
+                        break;
+
+                    case COMMAND_ID_DISCARD_LOADED_CLASSES:
+                        resetLoadedClasses();
+                        break;
+
+                    case COMMAND_ID_INSPECT:
+                        inspect();
+                        break;
+
+                    default:
+                        throw new Error("BUG: Unknown command for code: " + code);
+                }
+
+                // Finished processing command bits, continue reading, don't need to process code
+                continue;
+            }
+
+            // Otherwise, it's part of a statement.  If it's just whitespace,
+            // we'll just accept it and move on.  Otherwise, parsing is attempted
+            // on the cumulated statement text, and errors are reported.  The
+            // pending input is accepted or rejected based on that parsing.
+
+            freshen();
+
+            if (pending.trim().length() == 0) {
+                accept();
+                continue;
+            }
+
+            // Try to parse the current code buffer
+            final String code = current();
+            
+            if (parse(code)) {
+                // Code parsed fine
+                accept();
+                complete = true;
+            }
+            else if (error == null) {
+                // Um... ???
+                accept();
+            }
+            else {
+                // Parse failed, spit out something to the user
+                report();
+            }
+        }
+
+        // Get and return the statement.
+        return accepted(complete);
+    }
+
+    private void inspect() {
+        if (lastResult == null){
+            err.println(MESSAGES.getMessage("command.inspect.no_result"));
+            return;
+        }
+
+        //
+        // FIXME: Update this once we have joint compile happy in the core build?
+        //
+        // this should read: groovy.inspect.swingui.ObjectBrowser.inspect(lastResult)
+        // but this doesnt compile since ObjectBrowser.groovy is compiled after this class.
+        //
+
+        //
+        // FIXME: When launching this, if the user tries to "exit" and the window is still opened, the shell will
+        //        hang... not really nice user experence IMO.  Should try to fix this if we can.
+        //
+        
+        try {
+            Class type = Class.forName("groovy.inspect.swingui.ObjectBrowser");
+            Method method = type.getMethod("inspect", new Class[]{ Object.class });
+            method.invoke(type, new Object[]{ lastResult });
+        }
+        catch (Exception e) {
+            err.println("Cannot invoke ObjectBrowser");
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Returns the accepted statement as a string.  If not complete, returns empty string.
+     */
+    private String accepted(final boolean complete) {
+        if (complete) {
+            return accepted.toString();
+        }
+        return "";
+    }
+
+    /**
+     * Returns the current statement, including pending text.
+     */
+    private String current() {
+        return accepted.toString() + pending + NEW_LINE;
+    }
+
+    /**
+     * Accepts the pending text into the statement.
+     */
+    private void accept() {
+        accepted.append(pending).append(NEW_LINE);
+        line += 1;
+    }
+
+    /**
+     * Clears accepted if stale.
+     */
+    private void freshen() {
+        if (stale) {
+            accepted.setLength(0);
+            stale = false;
+        }
+    }
+
+    //---------------------------------------------------------------------------
+    // SUPPORT ROUTINES
+
+    /**
+     * Attempts to parse the specified code with the specified tolerance.
+     * Updates the <code>parser</code> and <code>error</code> members
+     * appropriately.  Returns true if the text parsed, false otherwise.
+     * The attempts to identify and suppress errors resulting from the
+     * unfinished source text.
+     */
+    private boolean parse(final String code, final int tolerance) {
+        assert code != null;
+
+        boolean parsed = false;
+        parser = null;
+        error = null;
+
+        // Create the parser and attempt to parse the text as a top-level statement.
+        try {
+            parser = SourceUnit.create("groovysh-script", code, tolerance);
+            parser.parse();
+            parsed = true;
+        }
+
+        // We report errors other than unexpected EOF to the user.
+        catch (CompilationFailedException e) {
+            if (parser.getErrorCollector().getErrorCount() > 1 || !parser.failedWithUnexpectedEOF()) {
+                error = e;
+            }
+        }
+        catch (Exception e) {
+            error = e;
+        }
+
+        return parsed;
+    }
+
+    private boolean parse(final String code) {
+        return parse(code, 1);
+    }
+    
+    /**
+     * Reports the last parsing error to the user.
+     */
+    private void report() {
+        err.println("Discarding invalid text:"); // TODO: i18n
+        new ErrorReporter(error, false).write(err);
+    }
+
+    //-----------------------------------------------------------------------
+    // COMMANDS
+
+    //
+    // TODO: Add a simple command to read in a File/URL into the buffer for execution, but need better command
+    //       support first (aka GShell) so we can allow commands to take args, etc.
+    //
+
+    private static final int COMMAND_ID_EXIT = 0;
+    private static final int COMMAND_ID_HELP = 1;
+    private static final int COMMAND_ID_DISCARD = 2;
+    private static final int COMMAND_ID_DISPLAY = 3;
+    private static final int COMMAND_ID_EXPLAIN = 4;
+    private static final int COMMAND_ID_EXECUTE = 5;
+    private static final int COMMAND_ID_BINDING = 6;
+    private static final int COMMAND_ID_DISCARD_LOADED_CLASSES = 7;
+    private static final int COMMAND_ID_INSPECT = 8;
+    private static final int LAST_COMMAND_ID = 8;
+
+    private static final String[] COMMANDS = {
+        "exit",
+        "help",
+        "discard",
+        "display",
+        "explain",
+        "execute",
+        "binding",
+        "discardclasses",
+        "inspect"
+    };
+
+    private static final Map COMMAND_MAPPINGS = new HashMap();
+
+    static {
+        for (int i = 0; i <= LAST_COMMAND_ID; i++) {
+            COMMAND_MAPPINGS.put(COMMANDS[i], new Integer(i));
+        }
+
+        // A few synonyms
+        COMMAND_MAPPINGS.put("quit", new Integer(COMMAND_ID_EXIT));
+        COMMAND_MAPPINGS.put("go", new Integer(COMMAND_ID_EXECUTE));
+    }
+
+    private static final Map COMMAND_HELP = new HashMap();
+
+    static {
+        COMMAND_HELP.put(COMMANDS[COMMAND_ID_EXIT],    "exit/quit         - " + MESSAGES.getMessage("command.exit.descripion"));
+        COMMAND_HELP.put(COMMANDS[COMMAND_ID_HELP],    "help              - " + MESSAGES.getMessage("command.help.descripion"));
+        COMMAND_HELP.put(COMMANDS[COMMAND_ID_DISCARD], "discard           - " + MESSAGES.getMessage("command.discard.descripion"));
+        COMMAND_HELP.put(COMMANDS[COMMAND_ID_DISPLAY], "display           - " + MESSAGES.getMessage("command.display.descripion"));
+
+        //
+        // FIXME: If this is disabled, then er comment it out, so it doesn't confuse the user
+        //
+        
+        COMMAND_HELP.put(COMMANDS[COMMAND_ID_EXPLAIN], "explain           - " + MESSAGES.getMessage("command.explain.descripion"));
+        COMMAND_HELP.put(COMMANDS[COMMAND_ID_EXECUTE], "execute/go        - " + MESSAGES.getMessage("command.execute.descripion"));
+        COMMAND_HELP.put(COMMANDS[COMMAND_ID_BINDING], "binding           - " + MESSAGES.getMessage("command.binding.descripion"));
+        COMMAND_HELP.put(COMMANDS[COMMAND_ID_DISCARD_LOADED_CLASSES],
+                                                       "discardclasses    - " + MESSAGES.getMessage("command.discardclasses.descripion"));
+        COMMAND_HELP.put(COMMANDS[COMMAND_ID_INSPECT], "inspect           - " + MESSAGES.getMessage("command.inspect.descripion"));
+    }
+
+    /**
+     * Displays help text about available commands.
+     */
+    private void displayHelp() {
+        out.println(MESSAGES.getMessage("command.help.available_commands"));
+
+        for (int i = 0; i <= LAST_COMMAND_ID; i++) {
+            out.print("    ");
+            out.println(COMMAND_HELP.get(COMMANDS[i]));
+        }
+    }
+
+    /**
+     * Displays the accepted statement.
+     */
+    private void displayStatement() {
+        final String[] lines = accepted.toString().split(NEW_LINE);
+
+        if (lines.length == 1 && lines[0].trim().equals("")) {
+            out.println(MESSAGES.getMessage("command.display.buffer_empty"));
+        }
+        else {
+            // Eh, try to pick a decent pad size... but don't try to hard
+            int padsize = 2;
+            if (lines.length >= 10) padsize++;
+            if (lines.length >= 100) padsize++;
+            if (lines.length >= 1000) padsize++;
+
+            // Dump the current buffer with a line number prefix
+            for (int i = 0; i < lines.length; i++) {
+                // Normalize the field size of the line number
+                String lineno = DefaultGroovyMethods.padLeft(String.valueOf(i + 1), new Integer(padsize), " ");
+                
+                out.print(lineno);
+                out.print("> ");
+                out.println(lines[i]);
+            }
+        }
+    }
+
+    /**
+     * Displays the current binding used when instanciating the shell.
+     */
+    private void displayBinding() {
+        Binding context = shell.getContext();
+        Map variables = context.getVariables();
+        Set set = variables.keySet();
+
+        if (set.isEmpty()) {
+            out.println(MESSAGES.getMessage("command.binding.binding_empty"));
+        }
+        else {
+            out.println(MESSAGES.getMessage("command.binding.available_variables"));
+
+            Iterator iter = set.iterator();
+            while (iter.hasNext()) {
+                Object key = iter.next();
+
+                out.print("    ");
+                out.print(key);
+                out.print(" = ");
+                out.println(variables.get(key));
+            }
+        }
+    }
+
+    /**
+     * Attempts to parse the accepted statement and display the parse tree for it.
+     */
+    private void explainStatement() {
+        if (parse(accepted(true), 10) || error == null) {
+            out.println(MESSAGES.getMessage("command.explain.tree_header"));
+            //out.println(tree);
+        }
+        else {
+            out.println(MESSAGES.getMessage("command.explain.unparsable"));
+        }
+    }
+
+    private void resetLoadedClasses() {
+        shell.resetLoadedClasses();
+        
+        out.println(MESSAGES.getMessage("command.discardclasses.classdefs_discarded"));
+    }
+
+    //
+    // Custom JLine Completors to fancy up the user experence more.
+    //
+
+    private class CommandNameCompletor
+        extends SimpleCompletor
+    {
+        public CommandNameCompletor() {
+            super(new String[0]);
+
+            // Add each command name/alias as a candidate
+            Iterator iter = COMMAND_MAPPINGS.keySet().iterator();
+
+            while (iter.hasNext()) {
+                addCandidateString((String)iter.next());
+            }
+        }
+    }
+
+    //
+    // TODO: Add local variable completion?
+    //
+
+    //
+    // TODO: Add shell method complention?
+    //
+
+    /*
+    private void findShellMethods(String complete) {
+        List methods = shell.getMetaClass().getMetaMethods();
+        for (Iterator i = methods.iterator(); i.hasNext();) {
+            MetaMethod method = (MetaMethod) i.next();
+            if (method.getName().startsWith(complete)) {
+                if (method.getParameterTypes().length > 0) {
+                    completions.add(method.getName() + "(");
+                }
+                else {
+                    completions.add(method.getName() + "()");
+                }
+            }
+        }
+    }
+
+    private void findLocalVariables(String complete) {
+        Set names = shell.getContext().getVariables().keySet();
+
+        for (Iterator i = names.iterator(); i.hasNext();) {
+            String name = (String) i.next();
+            if (name.startsWith(complete)) {
+                completions.add(name);
+            }
+        }
+    }
+    */
+}
diff --git a/groovy/src/main/groovy/ui/InteractiveShell.properties b/groovy/src/main/groovy/ui/InteractiveShell.properties
new file mode 100644
index 0000000..db94415
--- /dev/null
+++ b/groovy/src/main/groovy/ui/InteractiveShell.properties
@@ -0,0 +1,67 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+#
+# CLI messages
+#
+
+cli.option.help.description=Display this help message
+cli.option.version.description=Display the version
+
+cli.info.unexpected_args=Unexpected arguments: {0}
+cli.info.version=Groovy Shell {0}
+
+#
+# Command messages
+#
+
+command.help.descripion=Displays this help text
+command.help.available_commands=Available commands:
+
+command.exit.descripion=Exit the shell
+
+command.discard.descripion=Discards the current statement buffer
+
+command.display.descripion=Displays the current statement buffer
+command.display.buffer_empty=The current buffer is empty.
+
+command.explain.descripion=Explains the parsing of the current statement buffer (currently disabled)
+command.explain.tree_header=Parse tree:
+command.explain.unparsable=The statement does not parse.
+
+command.execute.descripion=Executes the current statement buffer
+command.execute.not_complete=Statement is not complete.
+
+command.binding.descripion=Shows the binding used by the shell
+command.binding.binding_empty=The current binding is empty.
+command.binding.available_variables=Available variables:
+
+command.discardclasses.descripion=Discards all former unbound class definitions
+command.discardclasses.classdefs_discarded=All former unbound class definitions are discarded.
+
+command.inspect.descripion=Opens an object browser for the expression returned from previous 'go'
+command.inspect.no_result=Nothing to inspect (preceding 'go' missing?).
+
+#
+# Misc messages
+#
+
+startup_banner.0=Groovy Shell ({0}, JVM: {1})
+startup_banner.1=Type 'go' to execute statements; Type 'help' for more information.
diff --git a/groovy/src/main/groovy/ui/SystemOutputInterceptor.java b/groovy/src/main/groovy/ui/SystemOutputInterceptor.java
new file mode 100644
index 0000000..ac582c5
--- /dev/null
+++ b/groovy/src/main/groovy/ui/SystemOutputInterceptor.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.ui;
+
+import groovy.lang.Closure;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+
+/**
+ * Intercepts System.out. Implementation helper for Console.groovy.
+ *
+ * @version $Id$
+ */
+public class SystemOutputInterceptor extends FilterOutputStream {
+
+    private Closure callback;
+
+    /**
+     * Constructor
+     * 
+     * @param callback
+     *            accepts a string to be sent to std out and returns a Boolean.
+     *            If the return value is true, output will be sent to
+     *            System.out, otherwise it will not.
+     */
+    public SystemOutputInterceptor(final Closure callback) {
+        super(System.out);
+        
+        assert callback != null;
+        
+        this.callback = callback;
+    }
+
+    /**
+     * Starts intercepting System.out
+     */
+    public void start() {
+        System.setOut(new PrintStream(this));
+    }
+
+    /**
+     * Stops intercepting System.out, sending output to whereever it was
+     * going when this interceptor was created.
+     */
+    public void stop() {
+        System.setOut((PrintStream) out);
+    }
+
+    /**
+     * Intercepts output - moret common case of byte[]
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        Boolean result = (Boolean) callback.call(new String(b, off, len));
+        if (result.booleanValue()) {
+            out.write(b, off, len);
+        }
+    }
+
+    /**
+     * Intercepts output - single characters
+     */
+    public void write(int b) throws IOException {
+        Boolean result = (Boolean) callback.call(String.valueOf((char) b));
+        if (result.booleanValue()) {
+            out.write(b);
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/ui/icons/arrow_redo.png b/groovy/src/main/groovy/ui/icons/arrow_redo.png
new file mode 100644
index 0000000..fdc394c
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/arrow_redo.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/arrow_undo.png b/groovy/src/main/groovy/ui/icons/arrow_undo.png
new file mode 100644
index 0000000..6972c5e
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/arrow_undo.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/book_next.png b/groovy/src/main/groovy/ui/icons/book_next.png
new file mode 100644
index 0000000..ff2ea1a
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/book_next.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/book_previous.png b/groovy/src/main/groovy/ui/icons/book_previous.png
new file mode 100644
index 0000000..2e53c69
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/book_previous.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/credits.txt b/groovy/src/main/groovy/ui/icons/credits.txt
new file mode 100644
index 0000000..711e0e4
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/credits.txt
@@ -0,0 +1,5 @@
+These icons are from the famfamfam.com silk icons set

+http://www.famfamfam.com/lab/icons/silk/

+

+Licensed under the Creative Commons Attribution Licence v2.5

+http://creativecommons.org/licenses/by/2.5/
\ No newline at end of file
diff --git a/groovy/src/main/groovy/ui/icons/cut.png b/groovy/src/main/groovy/ui/icons/cut.png
new file mode 100644
index 0000000..f215d6f
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/cut.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/cut_red.png b/groovy/src/main/groovy/ui/icons/cut_red.png
new file mode 100644
index 0000000..85bb2f0
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/cut_red.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/disk.png b/groovy/src/main/groovy/ui/icons/disk.png
new file mode 100644
index 0000000..99d532e
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/disk.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/find.png b/groovy/src/main/groovy/ui/icons/find.png
new file mode 100644
index 0000000..1547479
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/find.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/folder_page.png b/groovy/src/main/groovy/ui/icons/folder_page.png
new file mode 100644
index 0000000..1ef6e11
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/folder_page.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/page.png b/groovy/src/main/groovy/ui/icons/page.png
new file mode 100644
index 0000000..03ddd79
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/page.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/page_copy.png b/groovy/src/main/groovy/ui/icons/page_copy.png
new file mode 100644
index 0000000..195dc6d
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/page_copy.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/page_paste.png b/groovy/src/main/groovy/ui/icons/page_paste.png
new file mode 100644
index 0000000..968f073
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/page_paste.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/script_go.png b/groovy/src/main/groovy/ui/icons/script_go.png
new file mode 100644
index 0000000..8e154e2
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/script_go.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/icons/text_replace.png b/groovy/src/main/groovy/ui/icons/text_replace.png
new file mode 100644
index 0000000..877f82f
--- /dev/null
+++ b/groovy/src/main/groovy/ui/icons/text_replace.png
Binary files differ
diff --git a/groovy/src/main/groovy/ui/package.html b/groovy/src/main/groovy/ui/package.html
new file mode 100644
index 0000000..f50b9c9
--- /dev/null
+++ b/groovy/src/main/groovy/ui/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.ui.*</title>
+  </head>
+  <body>
+    <p>An interactive command line terminal along with a Swing console for evaluating Groovy scripts.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/ui/text/FindReplaceUtility.java b/groovy/src/main/groovy/ui/text/FindReplaceUtility.java
new file mode 100644
index 0000000..ca6e011
--- /dev/null
+++ b/groovy/src/main/groovy/ui/text/FindReplaceUtility.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.ui.text;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.GridLayout;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.FocusAdapter;
+import java.awt.event.FocusEvent;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+import java.awt.event.TextEvent;
+import java.awt.event.TextListener;
+
+import java.util.EventListener;
+
+import javax.swing.Action;
+import javax.swing.AbstractAction;
+import javax.swing.BoxLayout;
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JRootPane;
+import javax.swing.KeyStroke;
+
+import javax.swing.event.EventListenerList;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.JTextComponent;
+import javax.swing.text.Segment;
+
+/**
+ *
+ * @author Evan "Hippy" Slatis
+ */
+public final class FindReplaceUtility {
+    
+    public static final String FIND_ACTION_COMMAND = "Find";
+    
+    public static final String REPLACE_ACTION_COMMAND = "Replace";
+    
+    public static final String REPLACE_ALL_ACTION_COMMAND = "Replace All";
+    
+    public static final String CLOSE_ACTION_COMMAND = "Close";
+    
+    public static final Action FIND_ACTION = new FindAction();
+    
+    private static final JDialog FIND_REPLACE_DIALOG = new JDialog();
+        
+    private static final JPanel TEXT_FIELD_PANEL = new JPanel(new GridLayout(2, 1));
+        
+    private static final JPanel ENTRY_PANEL = new JPanel();
+    
+    private static final JPanel FIND_PANEL = new JPanel();
+    private static final JLabel FIND_LABEL = new JLabel("Find What:    ");
+    private static final JComboBox FIND_FIELD = new JComboBox();
+    
+    private static final JPanel REPLACE_PANEL = new JPanel();
+    private static final JLabel REPLACE_LABEL = new JLabel("Replace With:");
+    private static final JComboBox REPLACE_FIELD = new JComboBox();
+
+    private static final JPanel BUTTON_PANEL = new JPanel();
+    private static final JButton FIND_BUTTON = new JButton();
+    private static final JButton REPLACE_BUTTON = new JButton();
+    private static final JButton REPLACE_ALL_BUTTON = new JButton();
+    private static final JButton CLOSE_BUTTON = new JButton();
+    
+    private static final Action CLOSE_ACTION = new CloseAction();
+    private static final Action REPLACE_ACTION = new ReplaceAction();
+
+    private static final JPanel CHECK_BOX_PANEL = new JPanel(new GridLayout(3, 1));
+    private static final JCheckBox MATCH_CASE_CHECKBOX = new JCheckBox("Match Case      ");
+    private static final JCheckBox IS_BACKWARDS_CHECKBOX = new JCheckBox("Search Backwards");
+    private static final JCheckBox WRAP_SEARCH_CHECKBOX = new JCheckBox("Wrap Search     ");
+    
+    private static JTextComponent textComponent;
+    private static AttributeSet attributeSet;
+    
+    private static int findReplaceCount;
+    private static String lastAction;
+    
+    private static final EventListenerList EVENT_LISTENER_LIST = new EventListenerList();
+    
+    // the document segment
+    private static final Segment SEGMENT = new Segment();
+ 
+    private static final FocusAdapter TEXT_FOCUS_LISTENER = new FocusAdapter() {
+        public void focusGained(FocusEvent fe) {
+            textComponent = (JTextComponent)fe.getSource();
+            attributeSet =
+                textComponent.getDocument().getDefaultRootElement().getAttributes();
+        }
+    };
+
+    static {
+        FIND_REPLACE_DIALOG.setResizable(false);
+        FIND_REPLACE_DIALOG.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+        // is next line needed at all?
+        /* KeyStroke keyStroke = */ KeyStroke.getKeyStroke("enter");
+        KeyAdapter keyAdapter = new KeyAdapter() {            
+            public void keyTyped(KeyEvent ke) {
+                if (ke.getKeyChar() == KeyEvent.VK_ENTER) {
+                    FIND_BUTTON.doClick();
+                }
+            }
+        };
+        FIND_PANEL.setLayout(new FlowLayout(FlowLayout.RIGHT));
+        FIND_PANEL.add(FIND_LABEL);
+        FIND_PANEL.add(FIND_FIELD);
+        FIND_FIELD.addItem("");
+        FIND_FIELD.setEditable(true);
+        FIND_FIELD.getEditor().getEditorComponent().addKeyListener(keyAdapter);
+        Dimension d = FIND_FIELD.getPreferredSize();
+        d.width = 225;
+        FIND_FIELD.setPreferredSize(d);
+        
+        REPLACE_PANEL.add(REPLACE_LABEL);
+        REPLACE_PANEL.add(REPLACE_FIELD);
+        REPLACE_FIELD.setEditable(true);
+        REPLACE_FIELD.getEditor().getEditorComponent().addKeyListener(keyAdapter);
+        REPLACE_FIELD.setPreferredSize(d);
+        
+        TEXT_FIELD_PANEL.setLayout(new BoxLayout(TEXT_FIELD_PANEL, BoxLayout.Y_AXIS));
+        TEXT_FIELD_PANEL.add(FIND_PANEL);
+        TEXT_FIELD_PANEL.add(REPLACE_PANEL);
+        
+        ENTRY_PANEL.add(TEXT_FIELD_PANEL);
+        FIND_REPLACE_DIALOG.getContentPane().add(ENTRY_PANEL, BorderLayout.WEST);
+
+        CHECK_BOX_PANEL.add(MATCH_CASE_CHECKBOX);
+        
+        CHECK_BOX_PANEL.add(IS_BACKWARDS_CHECKBOX);
+        
+        CHECK_BOX_PANEL.add(WRAP_SEARCH_CHECKBOX);
+                
+        ENTRY_PANEL.add(CHECK_BOX_PANEL);
+        ENTRY_PANEL.setLayout(new BoxLayout(ENTRY_PANEL, BoxLayout.Y_AXIS));
+
+        REPLACE_ALL_BUTTON.setAction(new ReplaceAllAction());
+        REPLACE_ALL_BUTTON.setHorizontalAlignment(JButton.CENTER);
+        d = REPLACE_ALL_BUTTON.getPreferredSize();
+        
+        BUTTON_PANEL.setLayout(new BoxLayout(BUTTON_PANEL, BoxLayout.Y_AXIS));
+        FIND_BUTTON.setAction(FIND_ACTION);
+        FIND_BUTTON.setPreferredSize(d);
+        FIND_BUTTON.setHorizontalAlignment(JButton.CENTER);
+        JPanel panel = new JPanel();
+        panel.add(FIND_BUTTON);
+        BUTTON_PANEL.add(panel);
+        FIND_REPLACE_DIALOG.getRootPane().setDefaultButton(FIND_BUTTON);
+        
+        REPLACE_BUTTON.setAction(REPLACE_ACTION);
+        REPLACE_BUTTON.setPreferredSize(d);
+        REPLACE_BUTTON.setHorizontalAlignment(JButton.CENTER);
+        panel = new JPanel();
+        panel.add(REPLACE_BUTTON);
+        BUTTON_PANEL.add(panel);
+        
+        panel = new JPanel();
+        panel.add(REPLACE_ALL_BUTTON);
+        BUTTON_PANEL.add(panel);
+        
+        CLOSE_BUTTON.setAction(CLOSE_ACTION);
+        CLOSE_BUTTON.setPreferredSize(d);
+        CLOSE_BUTTON.setHorizontalAlignment(JButton.CENTER);
+        panel = new JPanel();
+        panel.add(CLOSE_BUTTON);
+        BUTTON_PANEL.add(panel);
+        FIND_REPLACE_DIALOG.getContentPane().add(BUTTON_PANEL);
+        
+        KeyStroke stroke = (KeyStroke) CLOSE_ACTION.getValue(Action.ACCELERATOR_KEY);
+        JRootPane rPane = FIND_REPLACE_DIALOG.getRootPane();
+        rPane.getInputMap(JButton.WHEN_IN_FOCUSED_WINDOW).put(stroke, "exit");
+        rPane.getActionMap().put("exit", CLOSE_ACTION);
+    }
+
+    // Singleton
+    private FindReplaceUtility() {
+    }
+    
+    public static void addTextListener(TextListener tl) {
+        EVENT_LISTENER_LIST.add(TextListener.class, tl);
+    }
+    
+    private static void fireTextEvent() {
+        EventListener[] lstrs =
+            EVENT_LISTENER_LIST.getListeners(TextListener.class);
+        if (lstrs != null && lstrs.length > 0) {
+            TextEvent te =
+                new TextEvent(FIND_REPLACE_DIALOG, TextEvent.TEXT_VALUE_CHANGED);
+            for (int i = 0; i < lstrs.length; i++) {
+                ((TextListener)lstrs[i]).textValueChanged(te);
+            }
+        }
+    }
+    
+    /**
+     * @return the last action
+     */    
+    public static String getLastAction() {
+        return lastAction;
+    }
+    
+    /**
+     * @return the replacement count
+     */    
+    public static int getReplacementCount() {
+        return findReplaceCount;
+    }
+    
+    /**
+     * @return the search text
+     */    
+    public static String getSearchText() {
+        return (String) FIND_FIELD.getSelectedItem();
+    }
+    
+    /**
+     * @param textComponent the text component to listen to
+     */    
+    public static void registerTextComponent(JTextComponent textComponent) {
+        textComponent.addFocusListener(TEXT_FOCUS_LISTENER);
+    }
+    
+    public static void removeTextListener(TextListener tl) {
+        EVENT_LISTENER_LIST.remove(TextListener.class, tl);
+    }
+    
+    /**
+     * Find and select the next searchable matching text.
+     *
+     * @param reverse look forwards or backwards
+     * @param pos the starting index to start finding from
+     * @return the location of the next selected, or -1 if not found
+     */
+    private static int findNext(boolean reverse, int pos) {
+        boolean backwards = IS_BACKWARDS_CHECKBOX.isSelected();
+        backwards = backwards ? !reverse : reverse;
+        
+        String pattern = (String) FIND_FIELD.getSelectedItem();
+        if (pattern != null && pattern.length() > 0) {
+            try {
+                Document doc = textComponent.getDocument();
+                doc.getText(0, doc.getLength(), SEGMENT);
+            }
+            catch (Exception e) {
+                // should NEVER reach here
+                e.printStackTrace();
+            }
+
+            pos += textComponent.getSelectedText() == null ? 
+                (backwards ? -1 : 1) : 0;
+
+            char first = backwards ?
+                pattern.charAt(pattern.length() - 1) : pattern.charAt(0);
+            char oppFirst = Character.isUpperCase(first) ? 
+                Character.toLowerCase(first) : Character.toUpperCase(first);
+            int start = pos;
+            boolean wrapped = WRAP_SEARCH_CHECKBOX.isSelected();
+            int end = backwards ? 0 : SEGMENT.getEndIndex();
+            pos += backwards ? -1 : 1;
+            
+        	int length = textComponent.getDocument().getLength();
+        	if (pos > length) {
+        		pos = wrapped ? 0 : length;
+        	}
+            
+            boolean found = false;
+            while (!found && (backwards ? pos > end : pos < end)) {
+                found = !MATCH_CASE_CHECKBOX.isSelected() && SEGMENT.array[pos] == oppFirst;
+                found = found ? found : SEGMENT.array[pos] == first;
+                
+                if (found) {
+                    pos += backwards ? -(pattern.length() - 1) : 0;
+                    for (int i = 0; found && i < pattern.length(); i++) {
+                        char c = pattern.charAt(i);
+                        found =  SEGMENT.array[pos + i] == c;
+                        if (!MATCH_CASE_CHECKBOX.isSelected() && !found) {
+                            c = Character.isUpperCase(c) ? 
+                                Character.toLowerCase(c) :
+                                Character.toUpperCase(c);
+                            found =  SEGMENT.array[pos + i] == c;
+                        }
+                    }
+                }
+                
+                if (!found) {
+                    pos += backwards ? -1 : 1;
+
+                    if (pos == end && wrapped) {
+                        pos = backwards ? SEGMENT.getEndIndex() : 0;
+                        end = start;
+                        wrapped = false;
+                    }
+                }
+            }
+            pos = found ? pos : -1;
+        }
+        
+        return pos;
+    }
+    
+    private static void setListStrings() {
+        Object findObject = FIND_FIELD.getSelectedItem();
+        Object replaceObject = REPLACE_FIELD.isShowing() ?
+            (String) REPLACE_FIELD.getSelectedItem() : "";
+            
+        if (findObject != null && replaceObject != null) {
+            boolean found = false;
+            for (int i = 0; !found && i < FIND_FIELD.getItemCount(); i++) {
+                found = FIND_FIELD.getItemAt(i).equals(findObject);
+            }
+            if (!found) {
+                FIND_FIELD.insertItemAt(findObject, 0);
+                if (FIND_FIELD.getItemCount() > 7) {
+                    FIND_FIELD.removeItemAt(7);
+                }
+            }
+            
+            if (REPLACE_FIELD.isShowing()) {
+                found = false;
+                for (int i = 0; !found && i < REPLACE_FIELD.getItemCount(); i++) {
+                    found = REPLACE_FIELD.getItemAt(i).equals(findObject);
+                }
+                if (!found) {
+                    REPLACE_FIELD.insertItemAt(replaceObject, 0);
+                    if (REPLACE_FIELD.getItemCount() > 7) {
+                        REPLACE_FIELD.removeItemAt(7);
+                    }
+                }
+            }
+        }
+
+    }
+    
+    public static void showDialog() {
+        showDialog(false);
+    }
+    
+    /**
+     * @param isReplace show a replace dialog rather than a find dialog if true
+     */    
+    public static void showDialog(boolean isReplace) {
+        String title = isReplace ? REPLACE_ACTION_COMMAND : FIND_ACTION_COMMAND;
+        FIND_REPLACE_DIALOG.setTitle(title);
+        
+        String text = textComponent.getSelectedText();
+        if (text == null) {
+        	text = "";
+        }
+        FIND_FIELD.getEditor().setItem(text);
+        FIND_FIELD.getEditor().selectAll();
+                
+        REPLACE_PANEL.setVisible(isReplace);
+        REPLACE_ALL_BUTTON.setVisible(isReplace);
+        CLOSE_BUTTON.setVisible(isReplace);
+
+        Action action = isReplace ?
+                REPLACE_ACTION : CLOSE_ACTION;
+        REPLACE_BUTTON.setAction(action);
+
+        REPLACE_BUTTON.setPreferredSize(null);
+        Dimension d = isReplace ? 
+            REPLACE_ALL_BUTTON.getPreferredSize() :
+            REPLACE_BUTTON.getPreferredSize();
+        FIND_BUTTON.setPreferredSize(d);
+        REPLACE_BUTTON.setPreferredSize(d);
+        CLOSE_BUTTON.setPreferredSize(d);
+
+        FIND_REPLACE_DIALOG.invalidate();
+        FIND_REPLACE_DIALOG.repaint();
+        FIND_REPLACE_DIALOG.pack();
+        
+        java.awt.Frame[] frames = java.awt.Frame.getFrames();
+        for (int i = 0; i < frames.length; i++) {
+            if (frames[i].isFocused()) {
+                FIND_REPLACE_DIALOG.setLocationRelativeTo(frames[i]);
+            }
+        }
+        
+        FIND_REPLACE_DIALOG.setVisible(true);
+        FIND_FIELD.requestFocusInWindow();
+    }
+    
+    /**
+     * @param textComponent the text component to stop listening to
+     */
+    public static void unregisterTextComponent(JTextComponent textComponent) {
+        textComponent.removeFocusListener(TEXT_FOCUS_LISTENER);
+    }
+    
+    private static class FindAction extends AbstractAction {
+        
+        public FindAction() {
+            putValue(Action.NAME, FIND_ACTION_COMMAND);
+            putValue(Action.ACTION_COMMAND_KEY, FIND_ACTION_COMMAND);
+            putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_F));
+        }
+        
+        public void actionPerformed(ActionEvent ae) {
+            lastAction = FIND_ACTION_COMMAND;
+            findReplaceCount = 0;
+            
+            if (FIND_REPLACE_DIALOG.isVisible() &&
+                FIND_REPLACE_DIALOG.getTitle().equals(FIND_ACTION_COMMAND)) {
+            }
+
+            int pos = textComponent.getSelectedText() == null ? 
+                textComponent.getCaretPosition() : 
+                textComponent.getSelectionStart();
+            
+            boolean reverse = (ae.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
+            pos = findNext(reverse, pos);
+            
+            if (pos > -1) {
+                String pattern = (String) FIND_FIELD.getSelectedItem();
+                textComponent.select(pos, pos + pattern.length());
+                findReplaceCount = 1;
+            }
+            
+            setListStrings();
+            
+            fireTextEvent();
+        }
+    }
+    
+    private static class ReplaceAction extends AbstractAction {
+        
+        public ReplaceAction() {
+            putValue(Action.NAME, REPLACE_ACTION_COMMAND);
+            putValue(Action.ACTION_COMMAND_KEY, REPLACE_ACTION_COMMAND);
+            putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_R));
+        }
+
+        public void actionPerformed(ActionEvent ae) {
+            lastAction = ae.getActionCommand();
+            findReplaceCount = 0;
+            
+            int pos = textComponent.getSelectedText() == null ? 
+                textComponent.getCaretPosition() : 
+                textComponent.getSelectionStart();
+
+            pos = findNext(false, pos - 1);
+
+            if (pos > -1) {
+                String find = (String) FIND_FIELD.getSelectedItem();
+                String replace = (String) REPLACE_FIELD.getSelectedItem();
+                replace = replace == null ? "" : replace;
+                Document doc = textComponent.getDocument();
+                try {
+                    doc.remove(pos, find.length());
+                    doc.insertString(pos, replace, attributeSet);
+
+                    int last = pos;
+                    pos = findNext(false, pos);
+                    if (pos > -1) {
+                        textComponent.select(pos, pos + find.length());
+                    }
+                    else {
+                        textComponent.setCaretPosition(last + replace.length());
+                    }
+                }
+                catch (BadLocationException ble) {
+                    ble.printStackTrace();
+                }
+
+                findReplaceCount = 1;
+            }
+            setListStrings();
+            
+            fireTextEvent();
+        }
+    }
+    
+    private static class ReplaceAllAction extends AbstractAction {
+        
+        public ReplaceAllAction() {
+            putValue(Action.NAME, REPLACE_ALL_ACTION_COMMAND);
+            putValue(Action.ACTION_COMMAND_KEY, REPLACE_ALL_ACTION_COMMAND);
+            putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_A));
+        }
+
+        public void actionPerformed(ActionEvent ae) {
+            lastAction = ae.getActionCommand();
+            findReplaceCount = 0;
+            
+            int last = textComponent.getSelectedText() == null ? 
+                textComponent.getCaretPosition() : 
+                textComponent.getSelectionStart();
+
+            int pos = findNext(false, last - 1);
+
+            String find = (String) FIND_FIELD.getSelectedItem();
+            String replace = (String) REPLACE_FIELD.getSelectedItem();
+            replace = replace == null ? "" : replace;
+            while (pos > -1) {
+                Document doc = textComponent.getDocument();
+                try {
+                    doc.remove(pos, find.length());
+                    doc.insertString(pos, replace, attributeSet);
+
+                    last = pos;
+                    pos = findNext(false, pos);
+                }
+                catch (BadLocationException ble) {
+                    ble.printStackTrace();
+                }
+
+                findReplaceCount++;
+            }
+            
+            if (pos > -1) {
+                textComponent.select(pos, pos + find.length());
+            }
+            else {
+                textComponent.setCaretPosition(last + replace.length());
+            }
+            setListStrings();
+            
+            fireTextEvent();
+        }
+    }
+    
+    private static class CloseAction extends AbstractAction {
+        
+        public CloseAction() {
+            putValue(Action.NAME, CLOSE_ACTION_COMMAND);
+            putValue(Action.ACTION_COMMAND_KEY, CLOSE_ACTION_COMMAND);
+            putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_C));
+            putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke("ESCAPE"));
+        }
+
+        public void actionPerformed(ActionEvent ae) {
+            FIND_REPLACE_DIALOG.dispose();
+        }
+    }
+
+    public static void dispose() {
+        FIND_REPLACE_DIALOG.dispose();
+    }
+}
diff --git a/groovy/src/main/groovy/ui/text/GroovyFilter.java b/groovy/src/main/groovy/ui/text/GroovyFilter.java
new file mode 100644
index 0000000..1f6e141
--- /dev/null
+++ b/groovy/src/main/groovy/ui/text/GroovyFilter.java
@@ -0,0 +1,257 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.ui.text;

+

+import java.awt.Color;

+

+import java.awt.event.ActionEvent;

+import java.awt.event.KeyEvent;

+

+import javax.swing.AbstractAction;

+import javax.swing.Action;

+import javax.swing.KeyStroke;

+

+import javax.swing.text.BadLocationException;

+import javax.swing.text.DefaultStyledDocument;

+import javax.swing.text.Element;

+import javax.swing.text.JTextComponent;

+import javax.swing.text.Segment;

+import javax.swing.text.Style;

+import javax.swing.text.StyleConstants;

+import javax.swing.text.StyledDocument;

+import javax.swing.text.StyleContext;

+

+

+/**

+ *

+ * @author Evan "Hippy" Slatis

+ */

+public class GroovyFilter extends StructuredSyntaxDocumentFilter {

+    

+    // java tab policy action

+    private static final Action AUTO_TAB_ACTION = new AutoTabAction();

+    

+    // Style names

+    public static final String COMMENT = "comment";

+    public static final String SLASH_STAR_COMMENT = "/\\*(?s:.)*?(?:\\*/|\\z)";

+    public static final String SLASH_SLASH_COMMENT =  "//.*";

+    public static final String QUOTES =

+    	"(?ms:\"{3}(?!\\\"{1,3}).*?(?:\"{3}|\\z))|(?:\"{1}(?!\\\").*?(?:\"|\\Z))";

+    public static final String SINGLE_QUOTES =

+    	"(?ms:'{3}(?!'{1,3}).*?(?:'{3}|\\z))|(?:'[^'].*?(?:'|\\z))";

+    public static final String SLASHY_QUOTES = "/[^/*].*?/";

+    public static final String DIGIT = "\\d+?[efld]?";

+    

+    public static final String IDENT = "[\\w\\$&&[\\D]][\\w\\$]*";

+    public static final String OPERATION = "[\\w\\$&&[\\D]][\\w\\$]* *\\(";

+    public static final String LEFT_PARENS = "\\(";

+    

+    private static final Color COMMENT_COLOR = 

+        Color.LIGHT_GRAY.darker().darker();

+    

+    

+    public static final String RESERVED_WORD = "reserved";

+    public static final String[] RESERVED_WORDS =   {"\\babstract\\b",

+                                                    "\\bassert\\b",

+                                                    "\\bdefault\\b",

+                                                    "\\bif\\b",

+                                                    "\\bprivate\\b",

+                                                    "\\bthis\\b",

+                                                    "\\bboolean\\b",

+                                                    "\\bdo\\b",

+                                                    "\\bimplements\\b",

+                                                    "\\bprotected\\b",

+                                                    "\\bthrow\\b",

+                                                    "\\bbreak\\b",

+                                                    "\\bdouble\\b",

+                                                    "\\bimport\\b",

+                                                    "\\bpublic\\b",

+                                                    "\\bthrows\\b",

+                                                    "\\bbyte\\b",

+                                                    "\\belse\\b",

+                                                    "\\binstanceof\\b",

+                                                    "\\breturn\\b",

+                                                    "\\btransient\\b",

+                                                    "\\bcase\\b",

+                                                    "\\bextends\\b",

+                                                    "\\bint\\b",

+                                                    "\\bshort\\b",

+                                                    "\\btry\\b",

+                                                    "\\bcatch\\b",

+                                                    "\\bfinal\\b",

+                                                    "\\binterface\\b",

+                                                    "\\benum\\b",

+                                                    "\\bstatic\\b",

+                                                    "\\bvoid\\b",

+                                                    "\\bchar\\b",

+                                                    "\\bfinally\\b",

+                                                    "\\blong\\b",

+                                                    "\\bstrictfp\\b",

+                                                    "\\bvolatile\\b",

+                                                    "\\bclass\\b",

+                                                    "\\bfloat\\b",

+                                                    "\\bnative\\b",

+                                                    "\\bsuper\\b",

+                                                    "\\bwhile\\b",

+                                                    "\\bconst\\b",

+                                                    "\\bfor\\b",

+                                                    "\\bnew\\b",

+                                                    "\\bswitch\\b",

+                                                    "\\bcontinue\\b",

+                                                    "\\bgoto\\b",

+                                                    "\\bpackage\\b",

+                                                    "\\bdef\\b",

+                                                    "\\bas\\b",

+                                                    "\\bin\\b",

+                                                    "\\bsynchronized\\b",

+                                                    "\\bnull\\b"};

+    

+    /**

+     * Creates a new instance of GroovyFilter

+     */

+    public GroovyFilter(DefaultStyledDocument doc) {

+        super(doc);

+        init();

+    }

+    

+    private void init() {

+        StyleContext styleContext = StyleContext.getDefaultStyleContext();

+        Style defaultStyle = styleContext.getStyle(StyleContext.DEFAULT_STYLE);

+        

+        Style comment = styleContext.addStyle(COMMENT, defaultStyle);

+        StyleConstants.setForeground(comment, COMMENT_COLOR);

+        StyleConstants.setItalic(comment, true);

+        

+        Style quotes = styleContext.addStyle(QUOTES, defaultStyle);

+        StyleConstants.setForeground(quotes, Color.MAGENTA.darker().darker());

+        

+        Style charQuotes = styleContext.addStyle(SINGLE_QUOTES, defaultStyle);

+        StyleConstants.setForeground(charQuotes, Color.GREEN.darker().darker());

+        

+        Style slashyQuotes = styleContext.addStyle(SLASHY_QUOTES, defaultStyle);

+        StyleConstants.setForeground(slashyQuotes, Color.ORANGE.darker());

+        

+        Style digit = styleContext.addStyle(DIGIT, defaultStyle);

+        StyleConstants.setForeground(digit, Color.RED.darker());

+        

+        Style operation = styleContext.addStyle(OPERATION, defaultStyle);

+        StyleConstants.setBold(operation, true);

+        

+        Style ident = styleContext.addStyle(IDENT, defaultStyle);

+        

+        Style reservedWords = styleContext.addStyle(RESERVED_WORD, defaultStyle);

+        StyleConstants.setBold(reservedWords, true);

+        StyleConstants.setForeground(reservedWords, Color.BLUE.darker().darker());

+        

+        Style leftParens = styleContext.addStyle(IDENT, defaultStyle);

+        

+        getRootNode().putStyle(SLASH_STAR_COMMENT, comment);

+        getRootNode().putStyle(SLASH_SLASH_COMMENT, comment);

+        getRootNode().putStyle(QUOTES, quotes);

+        getRootNode().putStyle(SINGLE_QUOTES, charQuotes);

+        getRootNode().putStyle(SLASHY_QUOTES, slashyQuotes);

+        getRootNode().putStyle(DIGIT, digit);

+        

+        getRootNode().putStyle(OPERATION, operation);

+        StructuredSyntaxDocumentFilter.LexerNode node = createLexerNode();

+        node.putStyle(RESERVED_WORDS, reservedWords);

+        node.putStyle(LEFT_PARENS, leftParens);

+        getRootNode().putChild(OPERATION, node);

+        

+        getRootNode().putStyle(IDENT, ident);

+        node = createLexerNode();

+        node.putStyle(RESERVED_WORDS, reservedWords);

+        getRootNode().putChild(IDENT, node);

+    }

+    

+    public static void installAutoTabAction(JTextComponent tComp) {

+        tComp.getActionMap().put("GroovyFilter-autoTab", AUTO_TAB_ACTION);

+        KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false);

+        tComp.getInputMap().put(keyStroke, "GroovyFilter-autoTab");

+    }

+    

+    private static class AutoTabAction extends AbstractAction {

+        

+        private StyledDocument doc;

+        private final Segment segment = new Segment();

+        private final StringBuffer buffer = new StringBuffer();

+        

+        public void actionPerformed(ActionEvent ae) {

+            JTextComponent tComp = (JTextComponent)ae.getSource();

+            if (tComp.getDocument() instanceof StyledDocument) {

+                doc = (StyledDocument)tComp.getDocument();

+                try {

+                    doc.getText(0, doc.getLength(), segment);

+                }

+                catch (Exception e) {

+                    // should NEVER reach here

+                    e.printStackTrace();

+                }

+                int offset = tComp.getCaretPosition();

+                int index = findTabLocation(offset);

+                buffer.delete(0, buffer.length());

+                buffer.append('\n');

+                if (index > -1) {

+                    for (int i = 0; i < index + 4; i++) {

+                        buffer.append(' ');

+                    }

+                }

+                try {

+                    doc.insertString(offset, buffer.toString(),

+                                 doc.getDefaultRootElement().getAttributes());

+                }

+                catch (BadLocationException ble) {

+                    ble.printStackTrace();

+                }

+            }

+        }

+    

+        public int findTabLocation(int offset) {  

+

+            // find first {

+            boolean cont = true;

+            while (offset > -1 && cont) {

+                Element el = doc.getCharacterElement(offset);

+                Object color =

+                    el.getAttributes().getAttribute(StyleConstants.Foreground);

+                if (!COMMENT_COLOR.equals(color)) {

+                    cont = segment.array[offset] != '{' &&

+                           segment.array[offset] != '}';

+                }

+                offset -= cont ? 1 : 0;

+            }

+

+            if (offset > -1 && segment.array[offset] == '{') {

+                while (offset > -1 &&

+                       !Character.isWhitespace(segment.array[offset--])){

+                }

+            }

+

+            int index = offset < 0 || segment.array[offset] == '}' ? -4 : 0;

+            if (offset > -1) {

+                Element top = doc.getDefaultRootElement();

+                offset = top.getElement(top.getElementIndex(offset)).getStartOffset();

+

+                while (Character.isWhitespace(segment.array[offset++])) {

+                    index++;

+                }

+            }

+

+            return index;

+        }

+    }

+}

diff --git a/groovy/src/main/groovy/ui/text/StructuredSyntaxDocumentFilter.java b/groovy/src/main/groovy/ui/text/StructuredSyntaxDocumentFilter.java
new file mode 100644
index 0000000..c150162
--- /dev/null
+++ b/groovy/src/main/groovy/ui/text/StructuredSyntaxDocumentFilter.java
@@ -0,0 +1,553 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.ui.text;

+

+import javax.swing.text.*;

+import java.nio.CharBuffer;

+import java.util.*;

+import java.util.regex.Matcher;

+import java.util.regex.Pattern;

+

+/**

+ *

+ * @author Evan "Hippy" Slatis

+ */

+public class StructuredSyntaxDocumentFilter extends DocumentFilter {

+    

+    public static final String TAB_REPLACEMENT = "    ";

+    

+    private static final MLComparator ML_COMPARATOR = new MLComparator();

+

+    /**

+     * The root of the lexical parsing tree.

+     */

+    protected LexerNode lexer = new LexerNode(true);

+    

+    // The styled document the filter parses

+    protected DefaultStyledDocument styledDocument; 

+    

+    // the document buffer and segment

+    private Segment segment = new Segment();

+    private CharBuffer buffer;

+    

+    /**

+     * The position tree of multi-line comments.

+     */ 

+    protected TreeSet mlTextRunSet = new TreeSet(ML_COMPARATOR);

+    

+    // Ensures not adding any regexp with capturing groups

+    private static void checkRegexp(String regexp) {

+        String checking = regexp.replaceAll("\\\\\\(", "X").replaceAll("\\(\\?", "X");

+        int checked = checking.indexOf('(');

+        if (checked > -1) {

+            String msg = "Only non-capturing groups allowed:\r\n" +

+                         regexp + "\r\n";

+            for (int i = 0; i < checked; i++) {

+                msg += " ";

+            }

+            msg += "^";

+            throw new IllegalArgumentException(msg);

+        }

+    }

+    

+    /**

+     * Creates a new instance of StructuredSyntaxDocumentFilter

+     * @param document the styled document to parse

+     */

+    public StructuredSyntaxDocumentFilter(DefaultStyledDocument document) {

+        this.styledDocument = document;

+    }

+    

+    private int calcBeginParse(int offset) {

+        MultiLineRun mlr = getMultiLineRun(offset);

+        if (mlr != null) {

+            // means we're in middle of mlr, so start at beginning of mlr

+            offset = mlr.start();

+        }

+        else {

+            // otherwise, earliest position in line not part of mlr

+            offset = styledDocument.getParagraphElement(offset).getStartOffset();

+            mlr = getMultiLineRun(offset);

+            offset = mlr == null ? offset : mlr.end() + 1;

+        }

+        

+        return offset;

+    }

+    

+    private int calcEndParse(int offset) {

+        MultiLineRun mlr = getMultiLineRun(offset);

+        if (mlr != null) {

+            // means we're in middle of mlr, so end is at end of mlr

+            offset = mlr.end();

+        }

+        else {

+            // otherwise, latest position in line not part of mlr

+            offset = styledDocument.getParagraphElement(offset).getEndOffset();

+            mlr = getMultiLineRun(offset);

+            offset = mlr == null ? offset : mlr.end();

+        }

+        

+        return offset;

+    }

+    

+    /**

+     * Create a new LexerNode for adding to root.

+     *

+     * @return a new LexerNode

+     */

+    public LexerNode createLexerNode() {

+        return new LexerNode(false);

+    }

+    

+    // given an offset, return the mlr it resides in

+    private MultiLineRun getMultiLineRun(int offset) {

+        MultiLineRun ml = null;

+        if (offset > 0) {

+            Integer os = new Integer(offset);

+

+            SortedSet set = mlTextRunSet.headSet(os);

+            if (!set.isEmpty()) {

+                ml = (MultiLineRun)set.last();

+                ml = ml.end() >= offset ? ml : null;

+            }

+        }

+

+        return ml;

+    }

+    

+    /**

+     * Get the root node for lexing the document.   Children can be added such

+     * that matching patterns can be further parsed if required.

+     *

+     * @return the root lexing node.  

+     */

+    public LexerNode getRootNode() {

+        return lexer;

+    }

+    

+    /**

+     * Insert a string into the document, and then parse it if the parser has been

+     * set.

+     *

+     * @param fb

+     * @param offset

+     * @param text

+     * @param attrs

+     * @throws BadLocationException

+     */    

+    public void insertString(DocumentFilter.FilterBypass fb, int offset,

+                             String text, AttributeSet attrs)

+        throws BadLocationException {

+        // remove problem meta characters returns

+        text = replaceMetaCharacters(text);

+        

+        fb.insertString(offset, text, attrs);

+        

+        // start on the string that was inserted

+        parseDocument(offset, text.length());

+    }

+    

+    /**

+     * Parse the Document to update the character styles given an initial start

+     * position.  Called by the filter after it has updated the text. 

+     *

+     * @param offset

+     * @param length

+     * @throws BadLocationException

+     */

+    protected void parseDocument(int offset, int length) throws BadLocationException {

+        // intialize the segment with the complete document so the segment doesn't

+        // have an underlying gap in the buffer

+        styledDocument.getText(0, styledDocument.getLength(), segment);

+        

+        buffer = CharBuffer.wrap(segment.array).asReadOnlyBuffer();

+        

+        // initialize the lexer if necessary

+        if (!lexer.isInitialized()) {

+            // prime the parser and reparse whole document

+            lexer.initialize();

+            offset = 0;

+            length = styledDocument.getLength();

+        }

+        else {

+            int end = offset + length;

+            offset = calcBeginParse(offset);

+            length = calcEndParse(end) - offset;

+            

+            // clean the tree by ensuring multi line styles are reset in area

+            // of parsing

+            SortedSet set = mlTextRunSet.subSet(new Integer(offset),

+                                                new Integer(offset + length));

+            if (set != null) {

+                set.clear();

+            }

+        }

+        

+        // parse the document

+        lexer.parse(buffer, offset, length);

+    }

+

+    /**

+     * Remove a string from the document, and then parse it if the parser has been

+     * set.

+     *

+     * @param fb

+     * @param offset

+     * @param length

+     * @throws BadLocationException

+     */    

+    public void remove(DocumentFilter.FilterBypass fb, int offset, int length)

+        throws BadLocationException {

+        // FRICKIN' HACK!!!!! For some reason, deleting a string at offset 0

+        // does not get done properly, so first replace and remove after parsing

+        if (offset == 0 && length != fb.getDocument().getLength()) {

+            fb.replace(0, length, "\n", lexer.defaultStyle);

+            

+            // start on either side of the removed text

+            parseDocument(offset, 2);

+            fb.remove(offset, 1);

+        }

+        else {

+            fb.remove(offset, length);

+            

+            // start on either side of the removed text

+            if (offset + 1 < fb.getDocument().getLength()) {

+                parseDocument(offset, 1);

+            }

+            else if (offset - 1 > 0) {

+                parseDocument(offset - 1, 1);

+            }

+            else {

+                // empty text

+                mlTextRunSet.clear();

+            }

+        }

+

+    }

+

+    /**

+     * Replace a string in the document, and then parse it if the parser has been

+     * set.

+     *

+     * @param fb

+     * @param offset

+     * @param length

+     * @param text

+     * @param attrs

+     * @throws BadLocationException

+     */    

+    public void replace(DocumentFilter.FilterBypass fb, int offset, 

+                        int length, String text, AttributeSet attrs)

+        throws BadLocationException {

+        // remove problem meta characters returns

+        text = replaceMetaCharacters(text);

+        

+        fb.replace(offset, length, text, attrs);

+        

+        // start on the text that was replaced

+        parseDocument(offset, text.length());

+    }

+    

+    // tabs with spaces (I hate tabs)

+    private String replaceMetaCharacters(String string) {

+        // just in case remove carriage returns

+        string = string.replaceAll("\\t", TAB_REPLACEMENT);

+        return string;

+    }

+    

+    public final class LexerNode {

+        

+        private Style defaultStyle;

+    

+        private Map styleMap = new LinkedHashMap();

+        private Map children = new HashMap();

+

+        private Matcher matcher;

+        private List groupList = new ArrayList();

+        

+        private boolean initialized;

+        

+        private CharBuffer lastBuffer;

+

+        /*

+         * Creates a new instance of LexerNode 

+         */

+        LexerNode(boolean isParent) {

+            StyleContext sc = StyleContext.getDefaultStyleContext();

+            defaultStyle = sc.getStyle(StyleContext.DEFAULT_STYLE);

+        }

+    

+        private String buildRegexp(String[] regexps) {

+            String regexp = "";

+

+            for (int i = 0; i < regexps.length; i++) {

+                regexp += "|" + regexps[i];

+            }

+

+            // ensure leading '|' is removed

+            return regexp.substring(1);

+        }

+        

+        public Style getDefaultStyle() {

+            return defaultStyle;

+        }

+

+        private void initialize() {

+            matcher = null;

+            groupList.clear();

+            groupList.add(null);

+            

+            Iterator iter = styleMap.keySet().iterator();

+            String regexp = "";

+            while (iter.hasNext()) {

+                String nextRegexp = (String)iter.next();

+                regexp += "|(" + nextRegexp + ")";

+                // have to compile regexp first so that it will match

+                groupList.add(Pattern.compile(nextRegexp).pattern());

+            }

+            if (!regexp.equals("")) {

+                matcher = Pattern.compile(regexp.substring(1)).matcher("");

+                

+                iter = children.values().iterator();

+                while (iter.hasNext()) {

+                    ((LexerNode)iter.next()).initialize();

+                }

+            }

+            initialized = true;

+        }

+        

+        /**

+         * @return true if initialised

+         */        

+        public boolean isInitialized() {

+            return initialized;

+        }

+

+        /**

+         * @param buffer

+         * @param offset

+         * @param length

+         * @throws BadLocationException

+         */        

+        public void parse(CharBuffer buffer, int offset, int length)

+            throws BadLocationException {

+            // get the index of where we can start to look for an exit:

+            // i.e. after the end of the length of the segment, when we find 

+            // that text in question already is set properly, we can stop

+            // parsing

+            int checkPoint = offset + length;

+            

+            // reset the matcher and start parsing string

+            if (lastBuffer != buffer) {

+                matcher.reset(buffer);

+                lastBuffer = buffer;

+            }

+            

+            // the start and end indices of a match in the Matcher looking

+            int matchEnd = offset;

+            Style style = null;

+            while (matchEnd < checkPoint && matcher.find(offset)) {

+                // when we get something other than -1, we know which regexp

+                // matched; the 0 group is the complete expression of the 

+                // matcher, which would always return a hit based on the above

+                // while condition

+                int groupNum = 0;

+                while ((offset = matcher.start(++groupNum)) == -1){

+                }

+                

+                // if the matching offset is not the same as the end of the 

+                // previous match, we have extra text not matched, so set to 

+                // the default style of this lexer node

+                if (offset != matchEnd) {

+                    offset = offset > checkPoint ? checkPoint : offset; 

+                    styledDocument.setCharacterAttributes(matchEnd,

+                                                          offset - matchEnd,

+                                                          defaultStyle,

+                                                          true);

+                    if (offset >= checkPoint) {

+                        return;

+                    }

+                }

+

+                // track the end of the matching string 

+                matchEnd = matcher.end(groupNum);

+

+                // retrieve the proper style from groupNum of the groupList and

+                // styleMap, then set the attributes of the matching string

+                style = (Style)styleMap.get((String)groupList.get(groupNum));

+                styledDocument.setCharacterAttributes(offset,

+                                                      matchEnd - offset,

+                                                      style, true);

+

+                // if the match was multiline, which we'll know if they span

+                // multiple paragraph elements, the mark it (this list was cleaned

+                // above in parseDocument())

+                if (styledDocument.getParagraphElement(offset).getStartOffset() !=

+                    styledDocument.getParagraphElement(matchEnd).getStartOffset()) {

+                    // mark a ml run

+                    MultiLineRun mlr = new MultiLineRun(offset, matchEnd);

+                    mlTextRunSet.add(mlr);

+                }

+                

+                // parse the child regexps, if any, within a matched block

+                LexerNode node = (LexerNode)children.get(groupList.get(groupNum));

+                if (node != null) {

+                    node.parse(buffer, offset, matchEnd - offset);

+                }

+                

+                // set the offset to start where we left off

+                offset = matchEnd;

+            }

+            if (matchEnd < checkPoint) {

+                // if we finished before hitting the end of the checkpoint from

+                // no mroe matches, then set ensure the text is reset to the

+                // defaultStyle

+                styledDocument.setCharacterAttributes(matchEnd,

+                                                      checkPoint - matchEnd,

+                                                      defaultStyle,

+                                                      true);

+            }

+        }

+

+        /**

+         *

+         * @param regexp

+         * @param node

+         */        

+        public void putChild(String regexp, LexerNode node) {

+            node.defaultStyle = (Style)styleMap.get(regexp);

+            

+            // have to compile regexp first so that it will match

+            children.put(Pattern.compile(regexp).pattern(), node);

+            initialized = false;

+        }

+

+        /**

+         * @param regexps

+         * @param node

+         */        

+        public void putChild(String[] regexps, LexerNode node) {

+            putChild(buildRegexp(regexps), node);

+        }

+

+        /**

+         * @param regexp

+         * @param style

+         */        

+        public void putStyle(String regexp, Style style) {

+            checkRegexp(regexp);

+            styleMap.put(regexp, style);

+            initialized = false;

+        }

+

+        /**

+         * @param regexps

+         * @param style

+         */        

+        public void putStyle(String regexps[], Style style) {

+            putStyle(buildRegexp(regexps), style);

+        }

+

+        /**

+         * @param regexp

+         */        

+        public void removeChild(String regexp) {

+            children.remove(regexp);

+        }

+

+        /**

+         * @param regexp

+         */        

+        public void removeStyle(String regexp) {

+            styleMap.remove(regexp);

+            children.remove(regexp);

+        }

+

+        /**

+         * @param regexps

+         */        

+        public void removeStyle(String regexps[]) {

+            removeStyle(buildRegexp(regexps));

+        }

+        

+        public void setDefaultStyle(Style style) {

+            defaultStyle = style;

+        }

+    }

+    

+    protected class MultiLineRun {

+        

+        private Position start;

+        private Position end;

+        private int delimeterSize;

+        

+        public MultiLineRun(int start, int end) throws BadLocationException {

+            this(start, end, 2);

+        }

+        

+        public MultiLineRun(int start, int end, int delimeterSize) throws BadLocationException {

+            if (start > end) {

+                String msg = "Start offset is after end: ";

+                throw new BadLocationException(msg, start);

+            }

+            if (delimeterSize < 1) {

+                String msg = "Delimiters be at least size 1: " + 

+                              delimeterSize;

+                throw new IllegalArgumentException(msg);

+            }

+            this.start = styledDocument.createPosition(start);

+            this.end = styledDocument.createPosition(end);

+            this.delimeterSize = delimeterSize;

+        }

+        

+        public int getDelimeterSize() {

+            return delimeterSize;

+        }

+        

+        public int end() {

+            return end.getOffset();

+        }

+        

+        public int length() {

+            return end.getOffset() - start.getOffset();

+        }

+        

+        public int start() {

+            return start.getOffset();

+        }

+        

+        public String toString() {

+            return start.toString() + " " + end.toString();

+        }

+        

+    }

+

+    private static class MLComparator implements Comparator {

+        

+        public int compare(Object obj, Object obj1) {

+            return valueOf(obj) - valueOf(obj1);

+        }

+        

+        private int valueOf(Object obj) {

+            return obj instanceof Integer ? 

+                    ((Integer)obj).intValue() : 

+                    (obj instanceof MultiLineRun) ?

+                        ((MultiLineRun)obj).start() :

+                        ((Position)obj).getOffset();

+        }

+    }

+}

diff --git a/groovy/src/main/groovy/ui/text/StructuredSyntaxHandler.java b/groovy/src/main/groovy/ui/text/StructuredSyntaxHandler.java
new file mode 100644
index 0000000..1f3ce8d
--- /dev/null
+++ b/groovy/src/main/groovy/ui/text/StructuredSyntaxHandler.java
@@ -0,0 +1,141 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.ui.text;

+

+import java.awt.Font;

+

+import org.xml.sax.Attributes;

+import org.xml.sax.SAXException;

+import org.xml.sax.SAXParseException;

+

+import org.xml.sax.helpers.DefaultHandler;

+

+/**

+ *

+ * @author Evan "Hippy" Slatis

+ */

+public class StructuredSyntaxHandler extends DefaultHandler {

+    

+    //StyleConstants.

+    public static final String REGEXP = "regexp";

+    public static final String STYLE = "style";

+    

+    public static final String ALIGN_CENTER = "ALIGN_CENTER";

+    public static final String ALIGN_JUSTIFIED = "ALIGN_JUSTIFIED";

+    public static final String ALIGN_LEFT = "ALIGN_LEFT";

+    public static final String ALIGN_RIGHT = "ALIGN_RIGHT";

+

+    public static final String ALIGNMENT = "alignment";

+    public static final String BACKGROUND = "background";

+    public static final String BIDI_LEVEL = "bidiLevel";

+    public static final String BOLD = "bold";

+    public static final String COMPONENT_ATTRIBUTE = "componentAttribute";

+    public static final String COMPONENT_ELEMENT_NAME = "componentElementName";

+    public static final String COMPOSED_TEXT_ATTRIBUTE = "composedTextAttribute";

+    public static final String FIRST_LINE_INDENT = "firstLineIndent";

+    public static final String FONT_FAMILY = "fontFamily";

+    public static final String FONT_SIZE = "fontSize";

+    public static final String FOREGROUND = "foreground";

+    public static final String ICON_ATTRIBUTE = "iconAttribute";

+    public static final String ICON_ELEMENT_NAME = "iconElementName";

+    public static final String ITALIC = "italic";

+    public static final String LEFT_INDENT = "leftIndent";

+    public static final String LINE_SPACING = "lineSpacing";

+    public static final String MODEL_ATTRIBUTE = "modelAttribute";

+    public static final String NAME_ATTRIBUTE = "nameAttribute";

+    public static final String ORIENTATION = "orientation";

+    public static final String RESOLVE_ATTRIBUTE = "resolveAttribute";

+    public static final String RIGHT_INDENT = "rightIndent";

+    public static final String SPACE_ABOVE = "spaceAbove";

+    public static final String SPACE_BELOW = "spaceBelow";

+    public static final String STRIKE_THROUGH = "strikeThrough";

+    public static final String SUBSCRIPT = "subscript";

+    public static final String SUPERSCRIPT = "superscript";

+    public static final String TAB_SET = "tabSet";

+    public static final String UNDERLINE = "underline";

+    

+    private StructuredSyntaxDocumentFilter.LexerNode currentNode;

+    private StructuredSyntaxDocumentFilter.LexerNode parentNode;

+    

+    private final StructuredSyntaxDocumentFilter filter;

+    

+    private Font font;

+

+    /**

+     * Creates a new instance of MasterFrameHandler

+     * @param filter

+     */

+    public StructuredSyntaxHandler(StructuredSyntaxDocumentFilter filter) {

+        this.filter = filter;

+    }

+    

+    /**

+     * @param ch

+     * @param start

+     * @param length

+     */    

+    public void characters(char[] ch, int start, int length) {

+    }

+    

+    /**

+     * @throws SAXException

+     */    

+    public void endDocument() throws SAXException {

+        super.endDocument();

+    }

+    

+    /**

+     * @param uri

+     * @param localName

+     * @param qName

+     * @throws SAXException

+     */    

+    public void endElement(String uri,

+                           String localName,

+                           String qName) throws SAXException {

+    }

+    

+    /**

+     * @param e

+     * @throws SAXException

+     */    

+    public void	error(SAXParseException e) throws SAXException {

+        throw new SAXException("Line: " + e.getLineNumber() + " message: " + e.getMessage());

+    }

+    

+    /**

+     * @throws SAXException

+     */    

+    public void startDocument() throws SAXException {

+        super.startDocument();

+        currentNode = filter.getRootNode();

+    }

+    

+    /**

+     * @param uri

+     * @param localName

+     * @param qName

+     * @param attributes

+     * @throws SAXException

+     */    

+    public void startElement(String uri,

+                             String localName,

+                             String qName,

+                             Attributes attributes) throws SAXException {

+        super.startElement(uri, localName, qName, attributes);

+    }

+}

diff --git a/groovy/src/main/groovy/ui/text/StructuredSyntaxResources.java b/groovy/src/main/groovy/ui/text/StructuredSyntaxResources.java
new file mode 100644
index 0000000..5058ea5
--- /dev/null
+++ b/groovy/src/main/groovy/ui/text/StructuredSyntaxResources.java
@@ -0,0 +1,82 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.ui.text;

+

+import java.awt.Font;

+import java.awt.Toolkit;

+

+import java.awt.datatransfer.Clipboard;

+

+/**

+ * Contains all the basic resources and values used by the utility frame work

+ * framework.

+ *

+ * @author Evan "Hippy" Slatis

+ */

+public final class StructuredSyntaxResources {

+

+    // ==================================================

+    // ClipBoard

+    // ==================================================

+    

+    public static final Clipboard SYSTEM_CLIPBOARD;

+    static {

+        Clipboard systemClipboard = null;

+        try {

+            // if we don't have access to the system clipboard, will throw

+            // a security exception

+            SecurityManager mgr = System.getSecurityManager();

+            if (mgr != null) {

+                mgr.checkSystemClipboardAccess();

+            }

+            systemClipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

+        }

+        catch (SecurityException e) {

+            // means we can't get to system clipbard, so create app level one

+            systemClipboard = new Clipboard("UIResourceMgr");

+        }

+        catch (Exception e) {

+            e.printStackTrace();               

+        }

+        SYSTEM_CLIPBOARD = systemClipboard;

+    }

+

+    // =====================================================

+    // STANDARD FONTS

+    // =====================================================

+

+    public static final Font LARGE_FONT = Font.decode("Arial-24");

+    public static final Font MEDIUM_FONT = Font.decode("Arial-18");

+    public static final Font SMALL_FONT = Font.decode("Arial-12");

+    

+    public static final Font EDITOR_FONT = Font.decode("Monospaced-12");

+

+    // =====================================================

+    // UNDO/REDO NAMES

+    // =====================================================

+

+    public static final String UNDO = "Undo";

+    public static final String REDO = "Redo";

+    public static final String PRINT = "Print";

+    public static final String FIND = "Find";

+    public static final String FIND_NEXT = "Find Next";

+    public static final String REPLACE = "Replace";

+        

+    // singleton

+    private StructuredSyntaxResources() {

+    }

+}

diff --git a/groovy/src/main/groovy/ui/text/TextEditor.java b/groovy/src/main/groovy/ui/text/TextEditor.java
new file mode 100644
index 0000000..c39e856
--- /dev/null
+++ b/groovy/src/main/groovy/ui/text/TextEditor.java
@@ -0,0 +1,546 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.ui.text;

+

+import java.awt.Color;

+import java.awt.Component;

+import java.awt.Cursor;

+import java.awt.Dimension;

+import java.awt.Font;

+import java.awt.FontMetrics;

+import java.awt.Graphics;

+import java.awt.Graphics2D;

+import java.awt.Rectangle;

+import java.awt.Toolkit;

+

+import java.awt.event.ActionEvent;

+import java.awt.event.KeyEvent;

+import java.awt.event.MouseAdapter;

+import java.awt.event.MouseEvent;

+

+import java.awt.print.Pageable;

+import java.awt.print.PageFormat;

+import java.awt.print.Paper;

+import java.awt.print.Printable;

+import java.awt.print.PrinterException;

+import java.awt.print.PrinterJob;

+

+import java.util.Calendar;

+

+import java.util.regex.Pattern;

+

+import javax.swing.Action;

+import javax.swing.AbstractAction;

+import javax.swing.ActionMap;

+import javax.swing.InputMap;

+import javax.swing.JTextPane;

+import javax.swing.KeyStroke;

+import javax.swing.SwingUtilities;

+

+import javax.swing.plaf.ComponentUI;

+

+import javax.swing.text.Caret;

+import javax.swing.text.BadLocationException;

+import javax.swing.text.DefaultCaret;

+import javax.swing.text.DefaultEditorKit;

+import javax.swing.text.Document;

+import javax.swing.text.Element;

+import javax.swing.text.JTextComponent;

+import javax.swing.text.StyledDocument;

+import javax.swing.text.Utilities;

+

+/**

+ * A simple text pane that is printable and wrapping is optional.

+ *

+ * @author Evan "Hippy" Slatis

+ */

+public class TextEditor extends JTextPane implements Pageable, Printable {

+    

+    public static final String FIND = "Find...";

+    public static final String FIND_NEXT = "Find Next";

+    public static final String FIND_PREVIOUS = "Find Previous";

+    public static final String REPLACE = "Replace...";

+    

+    private static final String TABBED_SPACES = "    ";

+    private static final Pattern TAB_BACK_PATTERN = 

+        Pattern.compile("^(([\t])|(    )|(   )|(  )|( ))", Pattern.MULTILINE);

+    private static final Pattern LINE_START = 

+        Pattern.compile("^", Pattern.MULTILINE);

+    

+    private static final JTextPane PRINT_PANE = new JTextPane();

+    private static final Dimension PRINT_SIZE = new Dimension();

+    

+    private static Toolkit toolkit = Toolkit.getDefaultToolkit();

+    private static boolean isOvertypeMode;

+

+    private Caret defaultCaret;

+    private Caret overtypeCaret;

+    

+    private static final PageFormat PAGE_FORMAT;

+    static {

+        PrinterJob job = PrinterJob.getPrinterJob();

+        PAGE_FORMAT = job.defaultPage();

+    }

+    

+    private int numPages;

+        

+    private int lastUpdate;

+    

+    private MouseAdapter mouseAdapter =

+        new MouseAdapter() {

+            Cursor cursor;

+            public void mouseEntered(MouseEvent me) {

+                if (contains(me.getPoint())) {

+                    cursor = getCursor();

+                    Cursor curs =

+                        Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR);

+                    getRootPane().getLayeredPane().setCursor(curs);

+                }

+                else {

+                    getRootPane().getLayeredPane().setCursor(cursor);

+                }

+            }

+            public void mouseExited(MouseEvent me) {

+                getRootPane().getLayeredPane().setCursor(null);

+            }

+        };

+        

+    // unwrapped property

+    private boolean unwrapped;

+    

+    // tabsAsSpaces property

+    private boolean tabsAsSpaces;

+    

+    // multiLineTab property

+    private boolean multiLineTab;

+    

+    // searchable property

+    private boolean searchable = true;

+    

+    /**

+     * Creates a new instance of TextEditor

+     */

+    public TextEditor() {

+        this(false);

+    }

+

+    /**

+     * Creates a new instance of TextEditor

+     */

+    public TextEditor(boolean tabsAsSpaces) {

+        this(tabsAsSpaces, false);

+    }

+

+    /**

+     * Creates a new instance of TextEditor

+     */

+    public TextEditor(boolean tabsAsSpaces, boolean multiLineTab) {

+        this(multiLineTab, tabsAsSpaces, false);

+    }

+

+    /**

+     * Creates a new instance of TextEditor

+     */

+    public TextEditor(boolean tabsAsSpaces, boolean multiLineTab, boolean unwrapped) {

+        this.tabsAsSpaces = tabsAsSpaces;

+        this.multiLineTab = multiLineTab;

+        this.unwrapped = unwrapped;

+        

+        // remove and replace the delete action to another spot so ctrl H later

+        // on is strictly for showing the fand & replace dialog

+        ActionMap aMap = getActionMap();

+        Action action = null;

+        do {

+            action = action == null ? 

+                aMap.get(DefaultEditorKit.deletePrevCharAction) : null;

+            aMap.remove(DefaultEditorKit.deletePrevCharAction);

+            aMap = aMap.getParent();

+        } while (aMap != null);

+        aMap = getActionMap();

+        KeyStroke keyStroke =

+            KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0, false);

+        InputMap iMap = getInputMap();

+        iMap.put(keyStroke, "delete");

+        aMap.put("delete", action);

+    

+        // set all the actions

+        action = new FindAction();

+        aMap.put(FIND, action);

+        keyStroke = 

+            KeyStroke.getKeyStroke(KeyEvent.VK_F, KeyEvent.CTRL_MASK, false);

+        iMap.put(keyStroke, FIND);

+    

+        aMap.put(FIND_NEXT, FindReplaceUtility.FIND_ACTION);

+        keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0, false);

+        iMap.put(keyStroke, FIND_NEXT);

+    

+        aMap.put(FIND_PREVIOUS, FindReplaceUtility.FIND_ACTION);

+        keyStroke =

+            KeyStroke.getKeyStroke(KeyEvent.VK_F3, KeyEvent.SHIFT_MASK, false);

+        iMap.put(keyStroke, FIND_PREVIOUS);

+    

+        action = new TabAction();

+        aMap.put("TextEditor-tabAction", action);

+        keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0, false);

+        iMap.put(keyStroke, "TextEditor-tabAction");

+    

+        action = new ShiftTabAction();

+        aMap.put("TextEditor-shiftTabAction", action);

+        keyStroke =

+            KeyStroke.getKeyStroke(KeyEvent.VK_TAB, KeyEvent.SHIFT_MASK, false);

+        iMap.put(keyStroke, "TextEditor-shiftTabAction");

+    

+        action = new ReplaceAction();

+        getActionMap().put(REPLACE, action);

+        keyStroke =

+            KeyStroke.getKeyStroke(KeyEvent.VK_H, KeyEvent.CTRL_MASK, false);

+        do {

+            iMap.remove(keyStroke);

+            iMap = iMap.getParent();

+        } while (iMap != null);

+        getInputMap().put(keyStroke, REPLACE);

+        

+        setAutoscrolls(true);

+        

+        defaultCaret = getCaret();

+        overtypeCaret = new OvertypeCaret();

+        overtypeCaret.setBlinkRate(defaultCaret.getBlinkRate());

+    }

+    

+    public void addNotify() {

+        super.addNotify();

+        addMouseListener(mouseAdapter);

+        

+        FindReplaceUtility.registerTextComponent(this);

+    }

+

+    public int getNumberOfPages() {

+        StyledDocument doc = (StyledDocument)getDocument();

+        

+        Paper paper = PAGE_FORMAT.getPaper();

+        

+        numPages =

+            (int)Math.ceil(getSize().getHeight() / paper.getImageableHeight());

+        

+        return numPages;

+    }

+    

+    public PageFormat getPageFormat(int pageIndex) throws IndexOutOfBoundsException {

+        return PAGE_FORMAT;

+    }

+    

+    public Printable getPrintable(int param) throws IndexOutOfBoundsException {

+        return this;

+    }

+    

+    public int print(Graphics graphics, PageFormat pageFormat, int page)

+        throws PrinterException {

+        if (page < numPages) {

+            StyledDocument doc = (StyledDocument)getDocument();

+            Paper paper = pageFormat.getPaper();

+

+            // initialize the PRINT_PANE (need this so that wrapping

+            // can take place)

+            PRINT_PANE.setDocument(getDocument());

+

+            PRINT_SIZE.setSize(paper.getImageableWidth(),

+                               getSize().getHeight());

+            PRINT_PANE.setSize(PRINT_SIZE);

+

+            // translate the graphics origin upwards so the area of the page we

+            // want to print is in the origin; the clipping region auto set

+            // will take care of the rest

+            double y = -(page * paper.getImageableHeight()) + paper.getImageableY();

+            

+            ((Graphics2D)graphics).translate(paper.getImageableX(), y);

+            

+            // print the text with its own routines

+            PRINT_PANE.print(graphics);

+            

+            // translate the graphics object back to reality in the y dimension

+            // so we can print a page number

+            ((Graphics2D)graphics).translate(0, -y);

+            Rectangle rect = graphics.getClipBounds();

+            graphics.setClip(rect.x, 0, rect.width, (int)paper.getHeight() + 100);

+            

+            // get the name of the pane (or user name) and the time for the header

+            Calendar cal = Calendar.getInstance();

+            String header = cal.getTime().toString().trim();

+            String name = getName() == null ? 

+                System.getProperty("user.name").trim() : getName().trim();

+            String pageStr = String.valueOf(page + 1);

+            

+            Font font = Font.decode("Monospaced 8");

+            graphics.setFont(font);

+            FontMetrics fm = graphics.getFontMetrics(font);

+            int width = SwingUtilities.computeStringWidth(fm, header);

+            ((Graphics2D)graphics).drawString(header,

+                                              (float)(paper.getImageableWidth()/2 - width/2),

+                                              (float)paper.getImageableY()/2 + fm.getHeight());

+            

+            ((Graphics2D)graphics).translate(0, paper.getImageableY() - fm.getHeight());

+            double height = paper.getImageableHeight() + paper.getImageableY()/2;

+            width = SwingUtilities.computeStringWidth(fm, name);

+            ((Graphics2D)graphics).drawString(name,

+                                              (float)(paper.getImageableWidth()/2 - width/2),

+                                              (float)height - fm.getHeight()/2);

+            

+            ((Graphics2D)graphics).translate(0, fm.getHeight());

+            width = SwingUtilities.computeStringWidth(fm, pageStr);

+            ((Graphics2D)graphics).drawString(pageStr,

+                                              (float)(paper.getImageableWidth()/2 - width/2),

+                                              (float)height - fm.getHeight()/2);

+

+            return Printable.PAGE_EXISTS;

+        }

+        return Printable.NO_SUCH_PAGE;

+    }

+    

+    public boolean getScrollableTracksViewportWidth(){

+        boolean bool = super.getScrollableTracksViewportWidth();

+        if (unwrapped) {

+            Component parent = this.getParent();

+            ComponentUI ui = this.getUI();

+            int uiWidth = ui.getPreferredSize(this).width;

+            int parentWidth = parent.getSize().width;

+            bool = (parent != null) ?

+                (ui.getPreferredSize(this).width < parent.getSize().width) : true;

+        }

+        return bool;

+    }

+    

+    public boolean isMultiLineTabbed() {

+        return multiLineTab;

+    }

+    

+    public static boolean isOvertypeMode() {

+        return isOvertypeMode;

+    }

+    

+    public boolean isTabsAsSpaces() {

+        return tabsAsSpaces;

+    }

+    

+    public boolean isUnwrapped() {

+        return unwrapped;

+    }

+    

+    protected void processKeyEvent(KeyEvent e)

+    {

+        super.processKeyEvent(e);

+

+        //  Handle release of Insert key to toggle overtype/insert mode

+        if (e.getID() == KeyEvent.KEY_RELEASED &&

+            e.getKeyCode() == KeyEvent.VK_INSERT) {

+            setOvertypeMode(!isOvertypeMode());

+        }

+    }

+

+    public void removeNotify() {

+        super.removeNotify();

+        removeMouseListener(mouseAdapter);

+        FindReplaceUtility.unregisterTextComponent(this);

+    }

+

+    public void replaceSelection(String text) {

+        //  Implement overtype mode by selecting the character at the current

+        //  caret position

+        if (isOvertypeMode()) {

+            int pos = getCaretPosition();

+

+            if (getSelectedText() == null && pos < getDocument().getLength()) {

+                moveCaretPosition(pos + 1);

+            }

+        }

+

+        super.replaceSelection(text);

+    }

+

+    public void setBounds(int x, int y, int width, int height) {

+        if (unwrapped) {

+            Dimension size = this.getPreferredSize();

+            super.setBounds(x, y,

+                            Math.max(size.width, width),

+                            Math.max(size.height, height));

+        }

+        else {

+            super.setBounds(x, y, width, height);

+        }

+    }

+    

+    /**

+     * @param multiLineTab

+     */    

+    public void isMultiLineTabbed(boolean multiLineTab) {

+        this.multiLineTab = multiLineTab;

+    }

+    

+    /**

+     * @param tabsAsSpaces

+     */    

+    public void isTabsAsSpaces(boolean tabsAsSpaces) {

+        this.tabsAsSpaces = tabsAsSpaces;

+    }

+

+    /**

+     * Set the caret to use depending on overtype/insert mode

+     *

+     * @param isOvertypeMode

+     */    

+    public void setOvertypeMode(boolean isOvertypeMode) {

+        TextEditor.isOvertypeMode = isOvertypeMode;

+        int pos = getCaretPosition();

+

+        if (isOvertypeMode()) {

+            setCaret(overtypeCaret);

+        }

+        else {

+            setCaret(defaultCaret);

+        }

+

+        setCaretPosition(pos);

+    }

+    

+    /**

+     * @param unwrapped

+     */    

+    public void setUnwrapped(boolean unwrapped) {

+        this.unwrapped = unwrapped;

+    }

+    

+    private class FindAction extends AbstractAction {        

+        public void actionPerformed(ActionEvent ae) {

+            FindReplaceUtility.showDialog();

+        }

+    }

+    

+    private class ReplaceAction extends AbstractAction {

+        public void actionPerformed(ActionEvent ae) {

+            FindReplaceUtility.showDialog(true);

+        }

+    }

+    

+    private class ShiftTabAction extends AbstractAction {        

+        public void actionPerformed(ActionEvent ae) {

+            try {

+                if (multiLineTab && TextEditor.this.getSelectedText() != null) {

+                    Document doc = TextEditor.this.getDocument();

+                    int end = Utilities.getRowEnd(TextEditor.this,

+                                                  getSelectionEnd());

+                    TextEditor.this.setSelectionEnd(end);

+                    

+                    Element el =

+                        Utilities.getParagraphElement(TextEditor.this,

+                                                      getSelectionStart());

+                    int start = el.getStartOffset();

+                    TextEditor.this.setSelectionStart(start);

+                    

+                    

+                    // remove text and reselect the text

+                    String text = tabsAsSpaces ?

+                        TAB_BACK_PATTERN.matcher(getSelectedText()).replaceAll("") :

+                        getSelectedText().replaceAll("^\t", "");

+                    

+                    TextEditor.this.replaceSelection(text);

+                    

+                    TextEditor.this.select(start, start + text.length());

+                }

+            }

+            catch (Exception e) {

+                e.printStackTrace();

+            }

+        }

+    }

+    

+    private class TabAction extends AbstractAction {

+        public void actionPerformed(ActionEvent ae) {

+            try {

+                Document doc = TextEditor.this.getDocument();

+                String text = tabsAsSpaces ? TABBED_SPACES : "\t";

+                if (multiLineTab && getSelectedText() != null) {

+                    int end = Utilities.getRowEnd(TextEditor.this,

+                                                  getSelectionEnd());

+                    TextEditor.this.setSelectionEnd(end);

+                    

+                    Element el =

+                        Utilities.getParagraphElement(TextEditor.this,

+                                                      getSelectionStart());

+                    int start = el.getStartOffset();

+                    TextEditor.this.setSelectionStart(start);

+                    

+                    String toReplace = TextEditor.this.getSelectedText();

+                    toReplace = LINE_START.matcher(toReplace).replaceAll(text);

+                    TextEditor.this.replaceSelection(toReplace);

+                    TextEditor.this.select(start, start + toReplace.length());

+                }

+                else {

+                    int pos = TextEditor.this.getCaretPosition();

+                    doc.insertString(pos, text, null);

+                }

+            }

+            catch (Exception e) {

+                e.printStackTrace();

+            }

+        }

+    }

+    

+    /**

+     *  Paint a horizontal line the width of a column and 1 pixel high

+     */

+    private class OvertypeCaret extends DefaultCaret {

+        //The overtype caret will simply be a horizontal line one pixel high

+        // (once we determine where to paint it)

+        public void paint(Graphics g) {

+            if (isVisible()) {

+                try {

+                    JTextComponent component = getComponent();

+                    Rectangle r =

+                        component.getUI().modelToView(component, getDot());

+                    Color c = g.getColor();

+                    g.setColor(component.getBackground());

+                    g.setXORMode(component.getCaretColor());

+                    r.setBounds(r.x, r.y,

+                                g.getFontMetrics().charWidth('w'),

+                                g.getFontMetrics().getHeight());

+                    g.fillRect(r.x, r.y, r.width, r.height);

+                    g.setPaintMode();

+                    g.setColor(c);

+                }

+                catch (BadLocationException e) {

+                    e.printStackTrace();

+                }

+            }

+        }

+

+        /*

+         *  Damage must be overridden whenever the paint method is overridden

+         *  (The damaged area is the area the caret is painted in. We must

+         *  consider the area for the default caret and this caret)

+         */

+        protected synchronized void damage(Rectangle r) {

+            if (r != null) {

+                JTextComponent component = getComponent();

+                x = r.x;

+                y = r.y;

+                Font font = component.getFont();

+                width = component.getFontMetrics(font).charWidth('w');

+                height = r.height;

+                repaint();

+            }

+        }

+    }

+}

diff --git a/groovy/src/main/groovy/ui/text/TextUndoManager.java b/groovy/src/main/groovy/ui/text/TextUndoManager.java
new file mode 100644
index 0000000..5392926
--- /dev/null
+++ b/groovy/src/main/groovy/ui/text/TextUndoManager.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.ui.text;
+
+import java.beans.PropertyChangeListener;
+
+import javax.swing.event.SwingPropertyChangeSupport;
+import javax.swing.event.UndoableEditEvent;
+import javax.swing.undo.CompoundEdit;
+import javax.swing.undo.UndoManager;
+import javax.swing.undo.UndoableEdit;
+
+/**
+ * To use this, simply drop this as an UndoableEditListener into your document,
+ * and then create actions to call undo/redo as needed (checking can undo/redo
+ * first, of course).
+ *
+ * @author Evan "Hippy" Slatis
+ */
+public class TextUndoManager extends UndoManager {
+	
+    private SwingPropertyChangeSupport propChangeSupport =
+        new SwingPropertyChangeSupport(this);
+
+    private StructuredEdit compoundEdit = new StructuredEdit();
+
+    private long firstModified;
+    
+    private UndoableEdit modificationMarker = editToBeUndone();
+    
+    /**
+     * Creates a new instance of TextUndoManager.
+     */
+    public TextUndoManager() {
+    }
+    
+    public void addPropertyChangeListener(PropertyChangeListener pcl) {
+        propChangeSupport.addPropertyChangeListener(pcl);
+    }
+    
+    public void die() {
+        boolean undoable = canUndo();
+        super.die();
+        firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo());
+    }
+    
+    public void discardAllEdits() {
+        boolean undoable = canUndo();
+        boolean redoable = canRedo();
+        
+        boolean changed = hasChanged();
+        super.discardAllEdits();
+        modificationMarker = editToBeUndone();
+        
+        firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo());
+        firePropertyChangeEvent(UndoManager.UndoName, redoable, canRedo());
+    }
+    
+    protected void firePropertyChangeEvent(String name,
+                                           boolean oldValue,
+                                           boolean newValue) {
+        propChangeSupport.firePropertyChange(name, oldValue, newValue);
+    }
+    
+    public boolean hasChanged() {
+        return modificationMarker != editToBeUndone();
+    }
+    
+    public void redo() throws javax.swing.undo.CannotRedoException {
+        compoundEdit.end();
+        
+        if (firstModified == 0) {
+            firstModified = ((StructuredEdit)editToBeRedone()).editedTime();
+        }
+        
+        boolean undoable = canUndo();
+        
+        boolean changed = hasChanged();
+        super.redo();
+        
+        firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo());
+    }
+    
+    protected void redoTo(UndoableEdit edit) {
+        compoundEdit.end();
+        
+        if (firstModified == 0) {
+            firstModified = ((StructuredEdit)editToBeRedone()).editedTime();
+        }
+        
+        boolean undoable = canUndo();
+        
+        boolean changed = hasChanged();
+        super.redoTo(edit);
+        
+        firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo());
+        
+    }
+    
+    public void removePropertyChangeListener(PropertyChangeListener pcl) {
+        propChangeSupport.removePropertyChangeListener(pcl);
+    }
+    
+    public void reset() {
+        boolean changed = modificationMarker != editToBeUndone();
+        if (changed) {
+            modificationMarker = editToBeUndone();
+        }
+    }
+    
+    protected void trimEdits(int from, int to) {
+        boolean undoable = canUndo();
+        boolean redoable = canRedo();
+        
+        boolean changed = hasChanged();
+        super.trimEdits(from, to);
+        
+        firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo());
+        firePropertyChangeEvent(UndoManager.RedoName, redoable, canRedo());
+    }
+
+    public void undo() throws javax.swing.undo.CannotUndoException {
+        compoundEdit.end();
+        
+        UndoableEdit edit = editToBeUndone();
+        if (((StructuredEdit)editToBeUndone()).editedTime() ==
+            firstModified) {
+            firstModified = 0;
+        }
+        else if (firstModified == 0) {
+            firstModified = ((StructuredEdit)editToBeUndone()).editedTime();
+        }
+        
+        boolean redoable = canRedo();
+        boolean changed = hasChanged();
+        super.undo();
+        firePropertyChangeEvent(UndoManager.RedoName, redoable, canRedo());
+    }
+
+    public void undoableEditHappened(UndoableEditEvent uee) {
+        UndoableEdit edit = uee.getEdit();
+        boolean undoable = canUndo();
+
+        long editTime = System.currentTimeMillis();
+
+        if (firstModified == 0 || 
+            editTime - compoundEdit.editedTime() > 700) {
+            compoundEdit.end();
+            compoundEdit = new StructuredEdit();
+        }
+        compoundEdit.addEdit(edit);
+        
+        firstModified = firstModified == 0 ?
+            compoundEdit.editedTime() : firstModified;
+
+        if (lastEdit() != compoundEdit) {
+            boolean changed = hasChanged();
+            addEdit(compoundEdit);
+            firePropertyChangeEvent(UndoManager.UndoName, undoable, canUndo());
+        }
+
+    }
+    
+    private class StructuredEdit extends CompoundEdit {
+        
+        private long editedTime;
+        
+        public boolean addEdit(UndoableEdit edit) {
+            boolean result = super.addEdit(edit);
+            if (result && editedTime == 0) {
+                editedTime = System.currentTimeMillis();
+            }
+            return result;
+        }
+
+        public boolean canUndo() {
+            return edits.size() > 0;
+        }
+        
+        protected long editedTime() {
+            return editedTime;
+        }
+        
+        public boolean isInProgress() {
+            return false;
+        }
+    }    
+}
diff --git a/groovy/src/main/groovy/ui/text/package.html b/groovy/src/main/groovy/ui/text/package.html
new file mode 100644
index 0000000..9a7e41f
--- /dev/null
+++ b/groovy/src/main/groovy/ui/text/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.ui.text.*</title>
+  </head>
+  <body>
+    <p>Text processing helpers for the interactive command line terminal.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/ui/view/BasicContentPane.groovy b/groovy/src/main/groovy/ui/view/BasicContentPane.groovy
new file mode 100644
index 0000000..60f23d1
--- /dev/null
+++ b/groovy/src/main/groovy/ui/view/BasicContentPane.groovy
@@ -0,0 +1,79 @@
+package groovy.ui.view

+

+import groovy.ui.ConsoleTextEditor

+import java.awt.*

+import java.awt.image.BufferedImage

+import static javax.swing.JSplitPane.VERTICAL_SPLIT

+import javax.swing.text.Style

+import javax.swing.text.StyleContext

+import javax.swing.text.StyledDocument

+

+splitPane(id: 'splitPane', resizeWeight: 0.50F,

+    orientation: VERTICAL_SPLIT)

+{

+    widget(new ConsoleTextEditor(), border:emptyBorder(0), id:'inputEditor', constraints:BorderLayout.CENTER)

+    scrollPane(border:emptyBorder(0)) {

+        textPane(id: 'outputArea',

+            editable: false,

+            background: new Color(255,255,218),

+            font:new Font("Monospaced", Font.PLAIN, 12),

+            border:emptyBorder(4)

+        )

+    }

+}

+

+

+inputArea = inputEditor.textEditor

+// attach ctrl-enter to input area

+// need to wrap in actions to keep it from being added as a component

+actions {

+    container(inputArea, font:new Font("Monospaced", Font.PLAIN, 12), border:emptyBorder(4)) {

+        action(runAction)

+        action(runSelectionAction)

+    }

+}

+

+// add styles to the output area, shuold this be moved into SwingBuidler somehow?

+outputArea.setFont(new Font("Monospaced", outputArea.font.style, outputArea.font.size))

+StyledDocument doc = outputArea.getStyledDocument();

+

+Style defStyle = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);

+

+def applyStyle = {Style style, values -> values.each{k, v -> style.addAttribute(k, v)}}

+

+Style regular = doc.addStyle("regular", defStyle);

+applyStyle(regular, styles.regular)

+

+promptStyle = doc.addStyle("prompt", regular)

+applyStyle(promptStyle, styles.prompt)

+

+commandStyle = doc.addStyle("command", regular);

+applyStyle(commandStyle, styles.command)

+

+outputStyle = doc.addStyle("output", regular);

+applyStyle(outputStyle, styles.output)

+

+resultStyle = doc.addStyle("result", regular)

+applyStyle(resultStyle, styles.result)

+

+// redo styles for editor

+doc = inputArea.getStyledDocument()

+StyleContext styleContext = StyleContext.getDefaultStyleContext()

+styles.each {styleName, defs ->

+    Style style = styleContext.getStyle(styleName)

+    if (style) {

+        applyStyle(style, defs)

+    }

+}

+

+// set the preferred size of the input and output areas

+// this is a good enough solution, there are margins and scrollbars and such to worry about for 80x12x2

+Graphics g = GraphicsEnvironment.localGraphicsEnvironment.createGraphics (new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB))

+FontMetrics fm = g.getFontMetrics(outputArea.font)

+outputArea.preferredSize = [

+    fm.charWidth(0x77) * 81,

+    (fm.getHeight() + fm.leading) * 12] as Dimension

+

+//inputArea.setFont(outputArea.font)

+inputEditor.preferredSize = outputArea.preferredSize

+

diff --git a/groovy/src/main/groovy/ui/view/BasicMenuBar.groovy b/groovy/src/main/groovy/ui/view/BasicMenuBar.groovy
new file mode 100644
index 0000000..6381433
--- /dev/null
+++ b/groovy/src/main/groovy/ui/view/BasicMenuBar.groovy
@@ -0,0 +1,60 @@
+package groovy.ui.view

+

+menuBar {

+    menu(text: 'File', mnemonic: 'F') {

+        menuItem(newFileAction)

+        menuItem(newWindowAction)

+        menuItem(openAction)

+        separator()

+        menuItem(saveAction)

+        menuItem(saveAsAction)

+        separator()

+        menuItem(printAction)

+        separator()

+        menuItem(exitAction)

+    }

+

+    menu(text: 'Edit', mnemonic: 'E') {

+        menuItem(undoAction)

+        menuItem(redoAction)

+        separator()

+        menuItem(cutAction)

+        menuItem(copyAction)

+        menuItem(pasteAction)

+        separator()

+        menuItem(findAction)

+        menuItem(findNextAction)

+        menuItem(findPreviousAction)

+        menuItem(replaceAction)

+        separator()

+        menuItem(selectAllAction)

+    }

+

+    menu(text: 'View', mnemonic: 'V') {

+        menuItem(clearOutputAction)

+        separator()

+        menuItem(largerFontAction)

+        menuItem(smallerFontAction)

+        separator()

+        checkBoxMenuItem(captureStdOutAction, selected: controller.captureStdOut)

+        checkBoxMenuItem(fullStackTracesAction, selected: controller.fullStackTraces)

+        checkBoxMenuItem(showToolbarAction, selected: controller.showToolbar)

+    }

+

+    menu(text: 'History', mnemonic: 'I') {

+        menuItem(historyPrevAction)

+        menuItem(historyNextAction)

+    }

+

+    menu(text: 'Script', mnemonic: 'S') {

+        menuItem(runAction)

+        menuItem(runSelectionAction)

+        separator()

+        menuItem(inspectLastAction)

+        menuItem(inspectVariablesAction)

+    }

+

+    menu(text: 'Help', mnemonic: 'H') {

+        menuItem(aboutAction)

+    }

+}

diff --git a/groovy/src/main/groovy/ui/view/BasicStatusBar.groovy b/groovy/src/main/groovy/ui/view/BasicStatusBar.groovy
new file mode 100644
index 0000000..aa47dbe
--- /dev/null
+++ b/groovy/src/main/groovy/ui/view/BasicStatusBar.groovy
@@ -0,0 +1,22 @@
+package groovy.ui.view

+

+import java.awt.BorderLayout

+import java.awt.GridBagConstraints

+import javax.swing.SwingConstants

+

+panel(id: 'statusPanel', constraints: BorderLayout.SOUTH) {

+    gridBagLayout()

+    separator(constraints:gbc(gridwidth:GridBagConstraints.REMAINDER, fill:GridBagConstraints.HORIZONTAL))

+    label('Welcome to Groovy.',

+        id: 'status',

+        constraints:gbc(weightx:1.0,

+            anchor:GridBagConstraints.WEST,

+            fill:GridBagConstraints.HORIZONTAL,

+            insets: [1,3,1,3])

+    )

+    separator(orientation:SwingConstants.VERTICAL, constraints:gbc(fill:GridBagConstraints.VERTICAL))

+    label('1:1',

+        id: 'rowNumAndColNum',

+        constraints:gbc(insets: [1,3,1,3])

+    )

+}

diff --git a/groovy/src/main/groovy/ui/view/BasicToolBar.groovy b/groovy/src/main/groovy/ui/view/BasicToolBar.groovy
new file mode 100644
index 0000000..75401d0
--- /dev/null
+++ b/groovy/src/main/groovy/ui/view/BasicToolBar.groovy
@@ -0,0 +1,25 @@
+package groovy.ui.view

+

+import javax.swing.SwingConstants

+import java.awt.BorderLayout

+

+toolBar(id:'toolbar', rollover:true, visible:controller.showToolbar, constraints:BorderLayout.NORTH) {

+    button(newFileAction, text:null)

+    button(openAction, text:null)

+    button(saveAction, text:null)

+    separator(orientation:SwingConstants.VERTICAL)

+    button(undoAction, text:null)

+    button(redoAction, text:null)

+    separator(orientation:SwingConstants.VERTICAL)

+    button(cutAction, text:null)

+    button(copyAction, text:null)

+    button(pasteAction, text:null)

+    separator(orientation:SwingConstants.VERTICAL)

+    button(findAction, text:null)

+    button(replaceAction, text:null)

+    separator(orientation:SwingConstants.VERTICAL)

+    button(historyPrevAction, text:null)

+    button(historyNextAction, text:null)

+    separator(orientation:SwingConstants.VERTICAL)

+    button(runAction, text:null)

+}

diff --git a/groovy/src/main/groovy/ui/view/Defaults.groovy b/groovy/src/main/groovy/ui/view/Defaults.groovy
new file mode 100644
index 0000000..ff4aac6
--- /dev/null
+++ b/groovy/src/main/groovy/ui/view/Defaults.groovy
@@ -0,0 +1,58 @@
+package groovy.ui.view

+

+import groovy.ui.text.GroovyFilter

+import java.awt.Color

+import javax.swing.text.StyleConstants

+import javax.swing.text.StyleContext

+

+menuBarClass     = groovy.ui.view.BasicMenuBar

+contentPaneClass = groovy.ui.view.BasicContentPane

+toolBarClass     = groovy.ui.view.BasicToolBar

+statusBarClass   = groovy.ui.view.BasicStatusBar

+

+styles = [

+    // output window styles

+    regular: [

+            (StyleConstants.FontFamily): 'Monospaced',

+        ],

+    prompt: [

+            (StyleConstants.Foreground): new Color(0, 128, 0),

+        ],

+    command: [

+            (StyleConstants.Foreground): Color.BLUE,

+        ],

+    output: [:],

+    result: [

+            (StyleConstants.Foreground): Color.BLUE,

+            (StyleConstants.Background): Color.YELLOW,

+        ],

+

+    // syntax highlighting styles

+    (StyleContext.DEFAULT_STYLE) : [

+            (StyleConstants.FontFamily): 'Monospaced',

+        ],

+    (GroovyFilter.COMMENT): [

+            (StyleConstants.Foreground): Color.LIGHT_GRAY.darker().darker(),

+            (StyleConstants.Italic) : true,

+        ],

+    (GroovyFilter.QUOTES): [

+            (StyleConstants.Foreground): Color.MAGENTA.darker().darker(),

+        ],

+    (GroovyFilter.SINGLE_QUOTES): [

+            (StyleConstants.Foreground): Color.GREEN.darker().darker(),

+        ],

+    (GroovyFilter.SLASHY_QUOTES): [

+            (StyleConstants.Foreground): Color.ORANGE.darker(),

+        ],

+    (GroovyFilter.DIGIT): [

+            (StyleConstants.Foreground): Color.RED.darker(),

+        ],

+    (GroovyFilter.OPERATION): [

+            (StyleConstants.Bold): true,

+        ],

+    (GroovyFilter.IDENT): [:],

+    (GroovyFilter.RESERVED_WORD): [

+        (StyleConstants.Bold): true,

+        (StyleConstants.Foreground): Color.BLUE.darker().darker(),

+    ],

+]

diff --git a/groovy/src/main/groovy/ui/view/GTKDefaults.groovy b/groovy/src/main/groovy/ui/view/GTKDefaults.groovy
new file mode 100644
index 0000000..8c7849a
--- /dev/null
+++ b/groovy/src/main/groovy/ui/view/GTKDefaults.groovy
@@ -0,0 +1,54 @@
+package groovy.ui.view
+
+import javax.swing.JComponent
+import javax.swing.text.StyleConstants
+import javax.swing.text.StyleContext
+import org.codehaus.groovy.runtime.InvokerHelper
+
+build(Defaults)
+
+// change font to DejaVu Sans Mono, much clearer
+styles.regular[StyleConstants.FontFamily] = 'DejaVu Sans Mono'
+styles[StyleContext.DEFAULT_STYLE][StyleConstants.FontFamily] = 'DejaVu Sans Mono'
+
+// possibly change look and feel
+if (System.properties['java.version'] =~ /^1\.5/) {
+    // GTK wasn't where it needed to be in 1.5, especially with toolbars
+    // use metal instead
+    lookAndFeel('metal', boldFonts:false)
+    
+    // we also need to turn on anti-alising ourselves
+    key = InvokerHelper.getProperty('com.sun.java.swing.SwingUtilities2' as Class,
+        'AA_TEXT_PROPERTY_KEY')
+    addAttributeDelegate {builder, node, attributes ->
+        if (node instanceof JComponent) {
+            node.putClientProperty(key, new Boolean(true));
+        }
+    }
+}
+
+// some current distros (Ubuntu 7.10) have broken printing support :(
+// detect it and disable it
+try {
+    pj = java.awt.print.PrinterJob.getPrinterJob()
+    ps = pj.getPrintService()
+    ps.getAttributes()
+    docFlav  = (ps.getSupportedDocFlavors() as List).find {it.mimeType == 'application/vnd.cups-postscript' }
+    attrset = ps.getAttributes()
+    orient = attrset.get(javax.print.attribute.standard.OrientationRequested) ?: 
+             ps.getDefaultAttributeValue(javax.print.attribute.standard.OrientationRequested)
+    ps.isAttributeValueSupported(orient, docFlav, attrset)
+} catch (NullPointerException npe) {
+    //print will bomb out... replace with disabled print action
+    action(id: 'printAction',

+        name: 'Print...',
+        closure: controller.&print,

+        mnemonic: 'P',

+        accelerator: shortcut('P'),

+        shortDescription: 'Printing does not work in Java with this version of CUPS',

+        enabled: false

+    )

+}
+
+
+
diff --git a/groovy/src/main/groovy/ui/view/MacOSXDefaults.groovy b/groovy/src/main/groovy/ui/view/MacOSXDefaults.groovy
new file mode 100644
index 0000000..ed6bb9e
--- /dev/null
+++ b/groovy/src/main/groovy/ui/view/MacOSXDefaults.groovy
@@ -0,0 +1,58 @@
+package groovy.ui.view

+

+import groovy.ui.text.GroovyFilter

+import java.awt.Color

+import javax.swing.text.StyleConstants

+

+build(Defaults)

+

+// menu bar tweaks

+System.setProperty("apple.laf.useScreenMenuBar", "true")

+System.setProperty("com.apple.mrj.application.apple.menu.about.name", "GroovyConsole")

+

+// redo output styles

+styles = [

+    // output window styles

+    regular: [

+            (StyleConstants.FontFamily): "Monaco",

+        ],

+    prompt: [

+            (StyleConstants.Foreground): Color.LIGHT_GRAY,

+        ],

+    command: [

+            (StyleConstants.Foreground): Color.GRAY,

+        ],

+    output: [:],

+    result: [

+            (StyleConstants.Foreground): Color.WHITE,

+            (StyleConstants.Background): Color.BLACK,

+        ],

+

+    // syntax highlighting styles

+    (GroovyFilter.COMMENT): [

+            (StyleConstants.Foreground): Color.LIGHT_GRAY.darker().darker(),

+            (StyleConstants.Italic) : true,

+        ],

+    (GroovyFilter.QUOTES): [

+            (StyleConstants.Foreground): Color.MAGENTA.darker().darker(),

+        ],

+    (GroovyFilter.SINGLE_QUOTES): [

+            (StyleConstants.Foreground): Color.GREEN.darker().darker(),

+        ],

+    (GroovyFilter.SLASHY_QUOTES): [

+            (StyleConstants.Foreground): Color.ORANGE.darker(),

+        ],

+    (GroovyFilter.DIGIT): [

+            (StyleConstants.Foreground): Color.RED.darker(),

+        ],

+    (GroovyFilter.OPERATION): [

+            (StyleConstants.Bold): true,

+        ],

+    (GroovyFilter.IDENT): [:],

+    (GroovyFilter.RESERVED_WORD): [

+        (StyleConstants.Bold): true,

+        (StyleConstants.Foreground): Color.BLUE.darker().darker(),

+    ],

+]

+

+menuBarClass = MacOSXMenuBar
\ No newline at end of file
diff --git a/groovy/src/main/groovy/ui/view/MacOSXMenuBar.groovy b/groovy/src/main/groovy/ui/view/MacOSXMenuBar.groovy
new file mode 100644
index 0000000..1f7bf7b
--- /dev/null
+++ b/groovy/src/main/groovy/ui/view/MacOSXMenuBar.groovy
@@ -0,0 +1,85 @@
+package groovy.ui.view

+

+menuBar {

+    menu(text: 'File', mnemonic: 'F') {

+        menuItem(newFileAction, icon:null)

+        menuItem(newWindowAction, icon:null)

+        menuItem(openAction, icon:null)

+        separator()

+        menuItem(saveAction, icon:null)

+        menuItem(saveAsAction, icon:null)

+        separator()

+        menuItem(printAction, icon:null)

+    }

+

+    menu(text: 'Edit', mnemonic: 'E') {

+        menuItem(undoAction, icon:null)

+        menuItem(redoAction, icon:null)

+        separator()

+        menuItem(cutAction, icon:null)

+        menuItem(copyAction, icon:null)

+        menuItem(pasteAction, icon:null)

+        separator()

+        menuItem(findAction, icon:null)

+        menuItem(findNextAction, icon:null)

+        menuItem(findPreviousAction, icon:null)

+        menuItem(replaceAction, icon:null)

+        separator()

+        menuItem(selectAllAction, icon:null)

+    }

+

+    menu(text: 'View', mnemonic: 'V') {

+        menuItem(clearOutputAction, icon:null)

+        separator()

+        menuItem(largerFontAction, icon:null)

+        menuItem(smallerFontAction, icon:null)

+        separator()

+        checkBoxMenuItem(captureStdOutAction, selected: controller.captureStdOut)

+        checkBoxMenuItem(fullStackTracesAction, selected: controller.fullStackTraces)

+        checkBoxMenuItem(showToolbarAction, selected: controller.showToolbar)

+    }

+

+    menu(text: 'History', mnemonic: 'I') {

+        menuItem(historyPrevAction, icon:null)

+        menuItem(historyNextAction, icon:null)

+    }

+

+    menu(text: 'Script', mnemonic: 'S') {

+        menuItem(runAction, icon:null)

+        menuItem(runSelectionAction, icon:null)

+        separator()

+        menuItem(inspectLastAction, icon:null)

+        menuItem(inspectVariablesAction, icon:null)

+    }

+}

+

+static handler = false

+if (!handler) {

+

+handler = build("""

+package groovy.ui

+

+import com.apple.mrj.*

+

+class ConsoleMacOsSupport implements MRJQuitHandler, MRJAboutHandler {

+

+	def quitHandler

+	def aboutHandler

+

+	public void handleAbout() {

+		aboutHandler()

+	}

+

+	public void handleQuit() {

+		quitHandler()

+	}

+

+}

+

+def handler = new ConsoleMacOsSupport(quitHandler:controller.&exit, aboutHandler:controller.&showAbout)

+MRJApplicationUtils.registerAboutHandler(handler)

+MRJApplicationUtils.registerQuitHandler(handler)

+

+return handler

+""", new GroovyClassLoader(this.class.classLoader))

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/ui/view/WindowsDefaults.groovy b/groovy/src/main/groovy/ui/view/WindowsDefaults.groovy
new file mode 100644
index 0000000..047c5b1
--- /dev/null
+++ b/groovy/src/main/groovy/ui/view/WindowsDefaults.groovy
@@ -0,0 +1,26 @@
+package groovy.ui.view

+

+import javax.swing.JComponent

+import javax.swing.text.StyleConstants

+import javax.swing.text.StyleContext

+import org.codehaus.groovy.runtime.InvokerHelper

+

+build(Defaults)

+

+// change fonts for vista

+if ((System.properties['os.version'] as Float) >= 6.0) {

+    // Vista/Server 2008 or later

+    styles.regular[StyleConstants.FontFamily] = 'Consolas'

+    styles[StyleContext.DEFAULT_STYLE][StyleConstants.FontFamily] = 'Consolas'

+

+    // in JDK 1.5 we need to turn on anti-aliasing so consolas looks better

+    if (System.properties['java.version'] =~ /^1\.5/) {

+        key = InvokerHelper.getProperty('com.sun.java.swing.SwingUtilities2' as Class,

+            'AA_TEXT_PROPERTY_KEY')

+        addAttributeDelegate {builder, node, attributes ->

+            if (node instanceof JComponent) {

+                node.putClientProperty(key, new Boolean(true));

+            }

+        }

+    }

+}

diff --git a/groovy/src/main/groovy/util/AbstractFactory.java b/groovy/src/main/groovy/util/AbstractFactory.java
new file mode 100644
index 0000000..275d610
--- /dev/null
+++ b/groovy/src/main/groovy/util/AbstractFactory.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.util;

+

+import java.util.Map;

+

+/**

+ * @author Andres Almiray <aalmiray@users.sourceforge.com>

+ */ 

+public abstract class AbstractFactory implements Factory {

+    public boolean isLeaf() {

+        return false;

+    }

+

+    public boolean onHandleNodeAttributes( FactoryBuilderSupport builder, Object node,

+            Map attributes ) {

+        return true;

+    }

+

+    public void onNodeCompleted( FactoryBuilderSupport builder, Object parent, Object node ) {

+

+    }

+

+    public void setParent( FactoryBuilderSupport builder, Object parent, Object child ) {

+

+    }

+

+    public void setChild( FactoryBuilderSupport builder, Object parent, Object child ) {

+

+    }

+

+}

diff --git a/groovy/src/main/groovy/util/AllTestSuite.java b/groovy/src/main/groovy/util/AllTestSuite.java
new file mode 100644
index 0000000..e46431b
--- /dev/null
+++ b/groovy/src/main/groovy/util/AllTestSuite.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util;
+
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.Script;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.runtime.ScriptTestAdapter;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.logging.Logger;
+
+/**
+ * AllTestSuite can be used in extension of GroovyTestSuite to execute TestCases written in Groovy
+ * from inside a Java IDE.
+ * AllTestSuite collects all files below a given directory that comply to a given pattern.
+ * From these files, a TestSuite is constructed that can be run via an IDE graphical Test runner.
+ * The files are assumed to be Groovy source files and be either a TestCase or a Script that can
+ * be wrapped transparently into a TestCase.
+ * The directory and the pattern can be set via System properties (see this classes' constants for details.)
+ *
+ * When setting the loglevel of this class to FINEST, all file loading will be logged.
+ *
+ * See also groovy.util.AllTestSuiteTest.groovy
+ * @author Dierk Koenig based on a prototype by Andrew Glover
+ * @author Paul King
+ * todo: dk: make FileNameFinder injectable
+ */
+public class AllTestSuite extends TestSuite {
+
+    /** The System Property to set as base directory for collection of Test Cases.
+     * The pattern will be used as an Ant fileset include basedir.
+     * Key is "groovy.test.dir".
+     * Default value is "./test/".
+     */
+    public static final String SYSPROP_TEST_DIR = "groovy.test.dir";
+
+    /** The System Property to set as the filename pattern for collection of Test Cases.
+     * The pattern will be used as Regular Expression pattern applied with the find
+     * operator against each candidate file.path.
+     * Key is "groovy.test.pattern".
+     * Default value is "Test.groovy".
+     */
+    public static final String SYSPROP_TEST_PATTERN = "groovy.test.pattern";
+
+    /** The System Property to set as a filename excludes pattern for collection of Test Cases.
+     * When non-empty, the pattern will be used as Regular Expression pattern applied with the
+     * find operator against each candidate file.path.
+     * Key is "groovy.test.excludesPattern".
+     * Default value is "".
+     */
+    public static final String SYSPROP_TEST_EXCLUDES_PATTERN = "groovy.test.excludesPattern";
+
+    private static final Logger LOG = Logger.getLogger(AllTestSuite.class.getName());
+    private static final ClassLoader JAVA_LOADER = AllTestSuite.class.getClassLoader();
+    private static final GroovyClassLoader GROOVY_LOADER = new GroovyClassLoader(JAVA_LOADER);
+
+    private static final String[] EMPTY_ARGS = new String[]{};
+    private static IFileNameFinder finder = null;
+
+    static { // this is only needed since the Groovy Build compiles *.groovy files after *.java files
+        try {
+            Class finderClass = Class.forName("groovy.util.FileNameFinder");
+            finder = (IFileNameFinder) finderClass.newInstance();
+        } catch (Exception e) {
+            throw new RuntimeException("Cannot find and instantiate class FileNameFinder", e);
+        }
+    }
+
+    public static Test suite() {
+        String basedir = System.getProperty(SYSPROP_TEST_DIR, "./test/");
+        String pattern = System.getProperty(SYSPROP_TEST_PATTERN, "**/*Test.groovy");
+        String excludesPattern = System.getProperty(SYSPROP_TEST_EXCLUDES_PATTERN, "");
+        return suite(basedir, pattern);
+    }    
+
+    public static Test suite(String basedir, String pattern) {
+        return suite(basedir, pattern, "");
+    }
+
+    public static Test suite(String basedir, String pattern, String excludesPattern) {
+        AllTestSuite suite = new AllTestSuite();
+        String fileName = "";
+        try {
+            Collection filenames = excludesPattern.length() > 0
+                    ? finder.getFileNames(basedir, pattern, excludesPattern)
+                    : finder.getFileNames(basedir, pattern);
+            for (Iterator iter = filenames.iterator(); iter.hasNext();) {
+                fileName = (String) iter.next();
+                LOG.finest("trying to load "+ fileName);
+                suite.loadTest(fileName);
+            }
+        } catch (CompilationFailedException e1) {
+            e1.printStackTrace();
+            throw new RuntimeException("CompilationFailedException when loading "+fileName, e1);
+        } catch (IOException e2) {
+            throw new RuntimeException("IOException when loading "+fileName, e2);
+        }
+        return suite;
+    }
+
+    protected void loadTest(String fileName) throws CompilationFailedException, IOException {
+        Class type = compile(fileName);
+        if (!Test.class.isAssignableFrom(type) && Script.class.isAssignableFrom(type)) {
+            addTest(new ScriptTestAdapter(type, EMPTY_ARGS));
+        } else {
+            addTestSuite(type);
+        }
+    }
+
+    protected Class compile(String fileName) throws CompilationFailedException, IOException {
+        return GROOVY_LOADER.parseClass(new File(fileName));
+    }
+}
diff --git a/groovy/src/main/groovy/util/AntBuilder.java b/groovy/src/main/groovy/util/AntBuilder.java
new file mode 100644
index 0000000..a2a3f36
--- /dev/null
+++ b/groovy/src/main/groovy/util/AntBuilder.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+
+import groovy.xml.QName;
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.helper.AntXMLContext;
+import org.apache.tools.ant.helper.ProjectHelper2;
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.codehaus.groovy.ant.FileScanner;
+import org.xml.sax.Attributes;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.AttributesImpl;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Allows Ant tasks to be used with GroovyMarkup 
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>, changes by Dierk Koenig (dk)
+ * @version $Revision$
+ */
+public class AntBuilder extends BuilderSupport {
+
+    private static final Class[] ADD_TASK_PARAM_TYPES = { String.class };
+
+    private final Logger log = Logger.getLogger(getClass().getName());
+    private Project project;
+    private final AntXMLContext antXmlContext;
+    private final ProjectHelper2.ElementHandler antElementHandler = new ProjectHelper2.ElementHandler();
+    private final Target collectorTarget;
+    private Object lastCompletedNode;
+
+
+
+    public AntBuilder() {
+        this(createProject());
+    }
+
+    public AntBuilder(final Project project) {
+        this(project, new Target());
+    }
+
+    public AntBuilder(final Project project, final Target owningTarget) {
+        this.project = project;
+
+        this.project.setDefaultInputStream(System.in);
+        this.project.setInputHandler(new DefaultInputHandler());
+
+        collectorTarget = owningTarget;
+        antXmlContext = new AntXMLContext(project);
+        collectorTarget.setProject(project);
+        antXmlContext.setCurrentTarget(collectorTarget);
+        antXmlContext.setLocator(new AntBuilderLocator());
+        
+        // FileScanner is a Groovy hack (utility?)
+        project.addDataTypeDefinition("fileScanner", FileScanner.class);
+    }
+
+    /**
+     * <p>
+     * Set the input stream handling policy.
+     * This method can also be used to define a different input stream for text input.
+     * </p>
+     *
+     * <p>
+     * <b>Remark</b>: This policy has been introduced to fix GROOVY-2362.
+     * With the upgrade to Ant 1.7 from Ant 1.6.5, it was not possible to call the input task twice
+     * without getting errors. By using a default input handler and a DemuxInputStream,
+     * it seems to solve the problem.
+     * </p>
+     *
+     * @param in input stream to use for reading input from the command-line
+     */
+    public void setInputStreamHandlingPolicy(InputStream in) {
+        this.project.setDefaultInputStream(in);
+        this.project.setInputHandler(new DefaultInputHandler());
+        System.setIn(new DemuxInputStream(this.project));
+    }
+
+    public AntBuilder(final Task parentTask) {
+    	this(parentTask.getProject(), parentTask.getOwningTarget());
+    	
+    	// define "owning" task as wrapper to avoid having tasks added to the target
+    	// but it needs to be an UnknownElement and no access is available from
+    	// task to its original UnknownElement 
+        final UnknownElement ue = new UnknownElement(parentTask.getTaskName());
+        ue.setProject(parentTask.getProject());
+        ue.setTaskType(parentTask.getTaskType());
+        ue.setTaskName(parentTask.getTaskName());
+        ue.setLocation(parentTask.getLocation());
+        ue.setOwningTarget(parentTask.getOwningTarget());
+        ue.setRuntimeConfigurableWrapper(parentTask.getRuntimeConfigurableWrapper());
+        parentTask.getRuntimeConfigurableWrapper().setProxy(ue);
+    	antXmlContext.pushWrapper(parentTask.getRuntimeConfigurableWrapper());
+    }
+
+    /**#
+     * Gets the Ant project in which the tasks are executed
+     * @return the project
+     */
+    public Project getProject() {
+        return project;
+    }
+
+    /**
+     * @return Factory method to create new Project instances
+     */
+    protected static Project createProject() {
+        Project project = new Project();
+        BuildLogger logger = new NoBannerLogger();
+
+        logger.setMessageOutputLevel(org.apache.tools.ant.Project.MSG_INFO);
+        logger.setOutputPrintStream(System.out);
+        logger.setErrorPrintStream(System.err);
+
+        project.addBuildListener(logger);
+
+        project.init();
+        project.getBaseDir();
+        return project;
+    }
+
+    protected void setParent(Object parent, Object child) {
+    }
+
+    
+    /**
+     * We don't want to return the node as created in {@link #createNode(Object, Map, Object)}
+     * but the one made ready by {@link #nodeCompleted(Object, Object)}
+     * @see groovy.util.BuilderSupport#doInvokeMethod(java.lang.String, java.lang.Object, java.lang.Object)
+     */
+    protected Object doInvokeMethod(String methodName, Object name, Object args) {
+    	super.doInvokeMethod(methodName, name, args);
+    	
+
+    	// return the completed node
+    	return lastCompletedNode;
+    }
+
+    /**
+     * Determines, when the ANT Task that is represented by the "node" should perform.
+     * Node must be an ANT Task or no "perform" is called.
+     * If node is an ANT Task, it performs right after complete contstruction.
+     * If node is nested in a TaskContainer, calling "perform" is delegated to that
+     * TaskContainer.
+     * @param parent note: null when node is root
+     * @param node the node that now has all its children applied
+     */
+    protected void nodeCompleted(final Object parent, final Object node) {
+
+    	antElementHandler.onEndElement(null, null, antXmlContext);
+
+    	lastCompletedNode = node;
+        if (parent != null) {
+            log.finest("parent is not null: no perform on nodeCompleted");
+            return; // parent will care about when children perform
+        }
+        
+        // as in Target.execute()
+        if (node instanceof Task) {
+            Object task = node;
+            // "Unwrap" the UnknownElement to return the real task to the calling code
+            if (node instanceof UnknownElement) {
+            	final UnknownElement unknownElement = (UnknownElement) node;
+            	unknownElement.maybeConfigure();
+                task = unknownElement.getRealThing();
+            }
+            
+            lastCompletedNode = task;
+            // UnknownElement may wrap everything: task, path, ...
+            if (task instanceof Task) {
+                // save original input stream
+                InputStream originalIn = System.in;
+                DemuxInputStream inputStream = new DemuxInputStream(this.project);
+                System.setIn(inputStream);
+
+                ((Task) task).perform();
+
+                // restore original input stream
+                System.setIn(originalIn);
+            }
+        }
+        else {
+            final RuntimeConfigurable r = (RuntimeConfigurable) node;
+            r.maybeConfigure(project);
+        }
+    }
+
+    protected Object createNode(Object tagName) {
+        return createNode(tagName, Collections.EMPTY_MAP);
+    }
+
+    protected Object createNode(Object name, Object value) {
+        Object task = createNode(name);
+        setText(task, value.toString());
+        return task;
+    }
+
+    protected Object createNode(Object name, Map attributes, Object value) {
+        Object task = createNode(name, attributes);
+        setText(task, value.toString());
+        return task;
+    }
+    
+    /**
+     * Builds an {@link Attributes} from a {@link Map}
+     * 
+     * @param attributes the attributes to wrap
+     * @return the wrapped attributes
+     */
+    protected static Attributes buildAttributes(final Map attributes) {
+    	final AttributesImpl attr = new AttributesImpl();
+    	for (final Iterator iter=attributes.entrySet().iterator(); iter.hasNext(); ) {
+    		final Map.Entry entry = (Map.Entry) iter.next();
+    		final String attributeName = (String) entry.getKey();
+    		final String attributeValue = String.valueOf(entry.getValue());
+    		attr.addAttribute(null, attributeName, attributeName, "CDATA", attributeValue);
+    	}
+    	return attr;
+    }
+
+    protected Object createNode(final Object name, final Map attributes) {
+
+        String tagName = name.toString();
+        String ns = "";
+
+        if(name instanceof QName) {
+            QName q = (QName)name;
+            tagName = q.getLocalPart();
+            ns = q.getNamespaceURI();
+        }
+
+        try
+		{
+			antElementHandler.onStartElement(ns, tagName, tagName, buildAttributes(attributes), antXmlContext);
+		}
+		catch (final SAXParseException e)
+		{
+            log.log(Level.SEVERE, "Caught: " + e, e);
+		}
+    	
+		final RuntimeConfigurable wrapper = (RuntimeConfigurable) antXmlContext.getWrapperStack().lastElement();
+    	return wrapper.getProxy();
+    }
+
+    protected void setText(Object task, String text) {
+    	final char[] characters = text.toCharArray();
+        try {
+          	antElementHandler.characters(characters, 0, characters.length, antXmlContext);
+        }
+        catch (final SAXParseException e) {
+            log.log(Level.WARNING, "SetText failed: " + task + ". Reason: " + e, e);
+        }
+    }
+
+    public Project getAntProject() {
+        return project;
+    }
+}
+
+/**
+ * Would be nice to retrieve location information (from AST?).
+ * In a first time, without info
+ */
+class AntBuilderLocator implements Locator {
+	public int getColumnNumber()
+	{
+		return 0;
+	}
+	public int getLineNumber()
+	{
+		return 0;
+	}
+	public String getPublicId()
+	{
+		return "";
+	}
+	public String getSystemId()
+	{
+		return "";
+	}
+}
diff --git a/groovy/src/main/groovy/util/BuilderSupport.java b/groovy/src/main/groovy/util/BuilderSupport.java
new file mode 100644
index 0000000..2389ad9
--- /dev/null
+++ b/groovy/src/main/groovy/util/BuilderSupport.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+import groovy.lang.MissingMethodException;
+
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * An abstract base class for creating arbitrary nested trees of objects
+ * or events
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class BuilderSupport extends GroovyObjectSupport {
+
+    private Object current;
+    private Closure nameMappingClosure;
+    private final BuilderSupport proxyBuilder;
+
+    public BuilderSupport() {
+        this.proxyBuilder = this;
+    }
+
+    public BuilderSupport(BuilderSupport proxyBuilder) {
+        this(null, proxyBuilder);
+    }
+
+    public BuilderSupport(Closure nameMappingClosure, BuilderSupport proxyBuilder) {
+        this.nameMappingClosure = nameMappingClosure;
+        this.proxyBuilder = proxyBuilder;
+    }
+
+    /**
+     * Convenience method when no arguments are required
+     * @return the result of the call
+     * @param methodName the name of the method to invoke
+     */
+    public Object invokeMethod(String methodName) {
+        return invokeMethod(methodName, null);
+    }
+
+    public Object invokeMethod(String methodName, Object args) {
+        Object name = getName(methodName);
+        return doInvokeMethod(methodName, name, args);
+    }
+
+    protected Object doInvokeMethod(String methodName, Object name, Object args) {
+        Object node = null;
+        Closure closure = null;
+        List list = InvokerHelper.asList(args);
+
+        //System.out.println("Called invokeMethod with name: " + name + " arguments: " + list);
+
+        switch (list.size()) {
+        		case 0:
+	    	            node = proxyBuilder.createNode(name);
+        		    break;
+        	    	case 1:
+        	    	{
+        	    	    	Object object = list.get(0);
+        	    	    	if (object instanceof Map) {
+        	    	    	    node = proxyBuilder.createNode(name, (Map) object);
+        	    	    	} else if (object instanceof Closure) {
+        	    	    	    closure = (Closure) object;
+        	    	    	    node = proxyBuilder.createNode(name);
+        	    	    	} else {
+        	    	    	    node = proxyBuilder.createNode(name, object);
+        	    	    	}
+        	    	}
+        	    	break;
+        	    	case 2:
+        	    	{
+        	    	    Object object1 = list.get(0);
+    	    	        Object object2 = list.get(1);
+        	    	    if (object1 instanceof Map) {
+        	    	        if (object2 instanceof Closure) {
+        	    	            closure = (Closure) object2;
+        	    	            node = proxyBuilder.createNode(name, (Map) object1);
+        	    	        } else {
+        	    	            node = proxyBuilder.createNode(name, (Map) object1, object2);
+        	    	        }
+        	    	    } else {
+        	    	        if (object2 instanceof Closure) {
+        	    	            closure = (Closure) object2;
+        	    	            node = proxyBuilder.createNode(name, object1);
+				} else if (object2 instanceof Map) {
+				    node = proxyBuilder.createNode(name, (Map) object2, object1);
+        	    	        } else {
+				    throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
+				}
+        	    	    }
+        	    	}
+        	    	break;
+        	    	case 3:
+        	    	{
+        	    	    Object arg0 = list.get(0);
+        	    	    Object arg1 = list.get(1);
+        	    	    Object arg2 = list.get(2);
+        	    	    if (arg0 instanceof Map && arg2 instanceof Closure) {
+        	    	        closure = (Closure) arg2;
+        	    	        node = proxyBuilder.createNode(name, (Map) arg0, arg1);
+			    } else if (arg1 instanceof Map && arg2 instanceof Closure) {
+        	    	        closure = (Closure) arg2;
+        	    	        node = proxyBuilder.createNode(name, (Map) arg1, arg0);
+			    } else {
+				throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
+			   }
+        	    	}
+        	    	break;
+        	    	default:
+        	    	{
+			    throw new MissingMethodException(name.toString(), getClass(), list.toArray(), false);
+			}
+
+        }
+
+        if (current != null) {
+            proxyBuilder.setParent(current, node);
+        }
+
+        if (closure != null) {
+            // push new node on stack
+            Object oldCurrent = current;
+            current = node;
+
+            // lets register the builder as the delegate
+            setClosureDelegate(closure, node);
+            closure.call();
+
+            current = oldCurrent;
+        }
+
+        proxyBuilder.nodeCompleted(current, node);
+        return proxyBuilder.postNodeCompletion(current, node);
+    }
+
+    /**
+     * A strategy method to allow derived builders to use
+     * builder-trees and switch in different kinds of builders.
+     * This method should call the setDelegate() method on the closure
+     * which by default passes in this but if node is-a builder
+     * we could pass that in instead (or do something wacky too)
+     *
+     * @param closure the closure on which to call setDelegate()
+     * @param node the node value that we've just created, which could be
+     * a builder
+     */
+    protected void setClosureDelegate(Closure closure, Object node) {
+        closure.setDelegate(this);
+    }
+
+    protected abstract void setParent(Object parent, Object child);
+    protected abstract Object createNode(Object name);
+    protected abstract Object createNode(Object name, Object value);
+    protected abstract Object createNode(Object name, Map attributes);
+    protected abstract Object createNode(Object name, Map attributes, Object value);
+
+    /**
+     * A hook to allow names to be converted into some other object
+     * such as a QName in XML or ObjectName in JMX.
+     *
+     * @param methodName the name of the desired method
+     * @return the object representing the name
+     */
+    protected Object getName(String methodName) {
+        if (nameMappingClosure != null) {
+            return nameMappingClosure.call(methodName);
+        }
+        return methodName;
+    }
+
+
+    /**
+     * A hook to allow nodes to be processed once they have had all of their
+     * children applied.
+     *
+     * @param node the current node being processed
+     * @param parent the parent of the node being processed
+     */
+    protected void nodeCompleted(Object parent, Object node) {
+    }
+
+    /**
+     * A hook to allow nodes to be processed once they have had all of their
+     * children applied and allows the actual node object that represents
+     * the Markup element to be changed
+     *
+     * @param node the current node being processed
+     * @param parent the parent of the node being processed
+     * @return the node, possibly new, that represents the markup element
+     */
+    protected Object postNodeCompletion(Object parent, Object node) {
+        return node;
+    }
+
+    protected Object getCurrent() {
+        return current;
+    }
+
+    protected void setCurrent(Object current) {
+        this.current = current;
+    }
+}
diff --git a/groovy/src/main/groovy/util/CharsetToolkit.java b/groovy/src/main/groovy/util/CharsetToolkit.java
new file mode 100644
index 0000000..5153e4d
--- /dev/null
+++ b/groovy/src/main/groovy/util/CharsetToolkit.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util;
+
+import java.io.*;
+import java.nio.charset.Charset;
+import java.util.Collection;
+
+/**
+ * <p>Utility class to guess the encoding of a given text file.</p>
+ *
+ * <p>Unicode files encoded in UTF-16 (low or big endian) or UTF-8 files
+ * with a Byte Order Marker are correctly discovered. For UTF-8 files with no BOM, if the buffer
+ * is wide enough, the charset should also be discovered.</p>
+ *
+ * <p>A byte buffer of 4KB is usually sufficient to be able to guess the encoding.</p>
+ *
+ * <p>Usage:</p>
+ * <pre>
+ * // guess the encoding
+ * Charset guessedCharset = CharsetToolkit.guessEncoding(file, 4096);
+ *
+ * // create a reader with the correct charset
+ * CharsetToolkit toolkit = new CharsetToolkit(file);
+ * BufferedReader reader = toolkit.getReader();
+ *
+ * // read the file content
+ * String line;
+ * while ((line = br.readLine())!= null)
+ * {
+ *     System.out.println(line);
+ * }
+ * </pre>
+ *
+ * @author Guillaume Laforge
+ */
+public class CharsetToolkit {
+    private byte[] buffer;
+    private Charset defaultCharset;
+    private Charset charset;
+    private boolean enforce8Bit = true;
+    private final File file;
+    private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+    /**
+     * Constructor of the <code>CharsetToolkit</code> utility class.
+     *
+     * @param file of which we want to know the encoding.
+     */
+    public CharsetToolkit(File file) throws IOException {
+        this.file = file;
+        this.defaultCharset = getDefaultSystemCharset();
+        this.charset = null;
+        InputStream input = new FileInputStream(file);
+        try {
+            byte[] bytes = new byte[4096];
+            int bytesRead = input.read(bytes);
+            if (bytesRead == -1) {
+                this.buffer = EMPTY_BYTE_ARRAY;
+            }
+            else if (bytesRead < 4096) {
+                byte[] bytesToGuess = new byte[bytesRead];
+                System.arraycopy(bytes, 0, bytesToGuess, 0, bytesRead);
+                this.buffer = bytesToGuess;
+            }
+            else {
+                this.buffer = bytes;
+            }
+        } finally {
+            try {input.close();} catch (IOException e){
+                // IGNORE
+            }
+        }
+    }
+
+    /**
+     * Defines the default <code>Charset</code> used in case the buffer represents
+     * an 8-bit <code>Charset</code>.
+     *
+     * @param defaultCharset the default <code>Charset</code> to be returned by <code>guessEncoding()</code>
+     * if an 8-bit <code>Charset</code> is encountered.
+     */
+    public void setDefaultCharset(Charset defaultCharset) {
+        if (defaultCharset != null)
+            this.defaultCharset = defaultCharset;
+        else
+            this.defaultCharset = getDefaultSystemCharset();
+    }
+
+    public Charset getCharset() {
+        if (this.charset == null)
+            this.charset = guessEncoding();
+        return charset;
+    }
+
+    /**
+     * If US-ASCII is recognized, enforce to return the default encoding, rather than US-ASCII.
+     * It might be a file without any special character in the range 128-255, but that may be or become
+     * a file encoded with the default <code>charset</code> rather than US-ASCII.
+     *
+     * @param enforce a boolean specifying the use or not of US-ASCII.
+     */
+    public void setEnforce8Bit(boolean enforce) {
+        this.enforce8Bit = enforce;
+    }
+
+    /**
+     * Gets the enforce8Bit flag, in case we do not want to ever get a US-ASCII encoding.
+     *
+     * @return a boolean representing the flag of use of US-ASCII.
+     */
+    public boolean getEnforce8Bit() {
+        return this.enforce8Bit;
+    }
+
+    /**
+     * Retrieves the default Charset
+     */
+    public Charset getDefaultCharset() {
+        return defaultCharset;
+    }
+
+    /**
+     * <p>Guess the encoding of the provided buffer.</p>
+     * If Byte Order Markers are encountered at the beginning of the buffer, we immidiately
+     * return the charset implied by this BOM. Otherwise, the file would not be a human
+     * readable text file.</p>
+     *
+     * <p>If there is no BOM, this method tries to discern whether the file is UTF-8 or not.
+     * If it is not UTF-8, we assume the encoding is the default system encoding
+     * (of course, it might be any 8-bit charset, but usually, an 8-bit charset is the default one).</p>
+     *
+     * <p>It is possible to discern UTF-8 thanks to the pattern of characters with a multi-byte sequence.</p>
+     * <pre>
+     * UCS-4 range (hex.)        UTF-8 octet sequence (binary)
+     * 0000 0000-0000 007F       0xxxxxxx
+     * 0000 0080-0000 07FF       110xxxxx 10xxxxxx
+     * 0000 0800-0000 FFFF       1110xxxx 10xxxxxx 10xxxxxx
+     * 0001 0000-001F FFFF       11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
+     * 0020 0000-03FF FFFF       111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+     * 0400 0000-7FFF FFFF       1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
+     * </pre>
+     * <p>With UTF-8, 0xFE and 0xFF never appear.</p>
+     *
+     * @return the Charset recognized.
+     */
+    private Charset guessEncoding() {
+        // if the file has a Byte Order Marker, we can assume the file is in UTF-xx
+        // otherwise, the file would not be human readable
+        if (hasUTF8Bom())
+            return Charset.forName("UTF-8");
+        if (hasUTF16LEBom())
+            return Charset.forName("UTF-16LE");
+        if (hasUTF16BEBom())
+            return Charset.forName("UTF-16BE");
+
+        // if a byte has its most significant bit set, the file is in UTF-8 or in the default encoding
+        // otherwise, the file is in US-ASCII
+        boolean highOrderBit = false;
+
+        // if the file is in UTF-8, high order bytes must have a certain value, in order to be valid
+        // if it's not the case, we can assume the encoding is the default encoding of the system
+        boolean validU8Char = true;
+
+        // TODO the buffer is not read up to the end, but up to length - 6
+
+        int length = buffer.length;
+        int i = 0;
+        while (i < length - 6) {
+            byte b0 = buffer[i];
+            byte b1 = buffer[i + 1];
+            byte b2 = buffer[i + 2];
+            byte b3 = buffer[i + 3];
+            byte b4 = buffer[i + 4];
+            byte b5 = buffer[i + 5];
+            if (b0 < 0) {
+                // a high order bit was encountered, thus the encoding is not US-ASCII
+                // it may be either an 8-bit encoding or UTF-8
+                highOrderBit = true;
+                // a two-bytes sequence was encoutered
+                if (isTwoBytesSequence(b0)) {
+                    // there must be one continuation byte of the form 10xxxxxx,
+                    // otherwise the following characteris is not a valid UTF-8 construct
+                    if (!isContinuationChar(b1))
+                        validU8Char = false;
+                    else
+                        i++;
+                }
+                // a three-bytes sequence was encoutered
+                else if (isThreeBytesSequence(b0)) {
+                    // there must be two continuation bytes of the form 10xxxxxx,
+                    // otherwise the following characteris is not a valid UTF-8 construct
+                    if (!(isContinuationChar(b1) && isContinuationChar(b2)))
+                        validU8Char = false;
+                    else
+                        i += 2;
+                }
+                // a four-bytes sequence was encoutered
+                else if (isFourBytesSequence(b0)) {
+                    // there must be three continuation bytes of the form 10xxxxxx,
+                    // otherwise the following characteris is not a valid UTF-8 construct
+                    if (!(isContinuationChar(b1) && isContinuationChar(b2) && isContinuationChar(b3)))
+                        validU8Char = false;
+                    else
+                        i += 3;
+                }
+                // a five-bytes sequence was encoutered
+                else if (isFiveBytesSequence(b0)) {
+                    // there must be four continuation bytes of the form 10xxxxxx,
+                    // otherwise the following characteris is not a valid UTF-8 construct
+                    if (!(isContinuationChar(b1)
+                        && isContinuationChar(b2)
+                        && isContinuationChar(b3)
+                        && isContinuationChar(b4)))
+                        validU8Char = false;
+                    else
+                        i += 4;
+                }
+                // a six-bytes sequence was encoutered
+                else if (isSixBytesSequence(b0)) {
+                    // there must be five continuation bytes of the form 10xxxxxx,
+                    // otherwise the following characteris is not a valid UTF-8 construct
+                    if (!(isContinuationChar(b1)
+                        && isContinuationChar(b2)
+                        && isContinuationChar(b3)
+                        && isContinuationChar(b4)
+                        && isContinuationChar(b5)))
+                        validU8Char = false;
+                    else
+                        i += 5;
+                }
+                else
+                    validU8Char = false;
+            }
+            if (!validU8Char)
+                break;
+            i++;
+        }
+        // if no byte with an high order bit set, the encoding is US-ASCII
+        // (it might have been UTF-7, but this encoding is usually internally used only by mail systems)
+        if (!highOrderBit) {
+            // returns the default charset rather than US-ASCII if the enforce8Bit flag is set.
+            if (this.enforce8Bit)
+                return this.defaultCharset;
+            else
+                return Charset.forName("US-ASCII");
+        }
+        // if no invalid UTF-8 were encountered, we can assume the encoding is UTF-8,
+        // otherwise the file would not be human readable
+        if (validU8Char)
+            return Charset.forName("UTF-8");
+        // finally, if it's not UTF-8 nor US-ASCII, let's assume the encoding is the default encoding
+        return this.defaultCharset;
+    }
+
+    /**
+     * If the byte has the form 10xxxxx, then it's a continuation byte of a multiple byte character;
+     *
+     * @param b a byte.
+     * @return true if it's a continuation char.
+     */
+    private static boolean isContinuationChar(byte b) {
+        return -128 <= b && b <= -65;
+    }
+
+    /**
+     * If the byte has the form 110xxxx, then it's the first byte of a two-bytes sequence character.
+     *
+     * @param b a byte.
+     * @return true if it's the first byte of a two-bytes sequence.
+     */
+    private static boolean isTwoBytesSequence(byte b) {
+        return -64 <= b && b <= -33;
+    }
+
+    /**
+     * If the byte has the form 1110xxx, then it's the first byte of a three-bytes sequence character.
+     *
+     * @param b a byte.
+     * @return true if it's the first byte of a three-bytes sequence.
+     */
+    private static boolean isThreeBytesSequence(byte b) {
+        return -32 <= b && b <= -17;
+    }
+
+    /**
+     * If the byte has the form 11110xx, then it's the first byte of a four-bytes sequence character.
+     *
+     * @param b a byte.
+     * @return true if it's the first byte of a four-bytes sequence.
+     */
+    private static boolean isFourBytesSequence(byte b) {
+        return -16 <= b && b <= -9;
+    }
+
+    /**
+     * If the byte has the form 11110xx, then it's the first byte of a five-bytes sequence character.
+     *
+     * @param b a byte.
+     * @return true if it's the first byte of a five-bytes sequence.
+     */
+    private static boolean isFiveBytesSequence(byte b) {
+        return -8 <= b && b <= -5;
+    }
+
+    /**
+     * If the byte has the form 1110xxx, then it's the first byte of a six-bytes sequence character.
+     *
+     * @param b a byte.
+     * @return true if it's the first byte of a six-bytes sequence.
+     */
+    private static boolean isSixBytesSequence(byte b) {
+        return -4 <= b && b <= -3;
+    }
+
+    /**
+     * Retrieve the default charset of the system.
+     *
+     * @return the default <code>Charset</code>.
+     */
+    public static Charset getDefaultSystemCharset() {
+        return Charset.forName(System.getProperty("file.encoding"));
+    }
+
+    /**
+     * Has a Byte Order Marker for UTF-8 (Used by Microsoft's Notepad and other editors).
+     *
+     * @return true if the buffer has a BOM for UTF8.
+     */
+    public boolean hasUTF8Bom() {
+        if (buffer.length >= 3)
+            return (buffer[0] == -17 && buffer[1] == -69 && buffer[2] == -65);
+        else
+            return false;
+    }
+
+    /**
+     * Has a Byte Order Marker for UTF-16 Low Endian
+     * (ucs-2le, ucs-4le, and ucs-16le).
+     *
+     * @return true if the buffer has a BOM for UTF-16 Low Endian.
+     */
+    public boolean hasUTF16LEBom() {
+        if (buffer.length >= 2)
+            return (buffer[0] == -1 && buffer[1] == -2);
+        else
+            return false;
+    }
+
+    /**
+     * Has a Byte Order Marker for UTF-16 Big Endian
+     * (utf-16 and ucs-2).
+     *
+     * @return true if the buffer has a BOM for UTF-16 Big Endian.
+     */
+    public boolean hasUTF16BEBom() {
+        if (buffer.length >= 2)
+            return (buffer[0] == -2 && buffer[1] == -1);
+        else
+            return false;
+    }
+
+    /**
+     * Gets a <code>BufferedReader</code> (indeed a <code>LineNumberReader</code>) from the <code>File</code>
+     * specified in the constructor of <code>CharsetToolkit</code> using the charset discovered by the
+     * method <code>guessEncoding()</code>.
+     *
+     * @return a <code>BufferedReader</code>
+     * @throws FileNotFoundException if the file is not found.
+     */
+    public BufferedReader getReader() throws FileNotFoundException {
+        LineNumberReader reader = new LineNumberReader(new InputStreamReader(new FileInputStream(file), getCharset()));
+        if (hasUTF8Bom() || hasUTF16LEBom() || hasUTF16BEBom()) {
+            try {
+                reader.read();
+            }
+            catch (IOException e) {
+                // should never happen, as a file with no content
+                // but with a BOM has at least one char
+            }
+        }
+        return reader;
+    }
+
+    /**
+     * Retrieves all the available <code>Charset</code>s on the platform,
+     * among which the default <code>charset</code>.
+     *
+     * @return an array of <code>Charset</code>s.
+     */
+    public static Charset[] getAvailableCharsets() {
+        Collection collection = Charset.availableCharsets().values();
+        return (Charset[]) collection.toArray(new Charset[collection.size()]);
+    }
+}
diff --git a/groovy/src/main/groovy/util/CliBuilder.groovy b/groovy/src/main/groovy/util/CliBuilder.groovy
new file mode 100644
index 0000000..7e880e4
--- /dev/null
+++ b/groovy/src/main/groovy/util/CliBuilder.groovy
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util
+
+import org.apache.commons.cli.*
+import org.codehaus.groovy.runtime.InvokerHelper
+
+
+/**
+    Supported Option Properties:
+    argName:        String
+    longOpt:        String
+    args:           int
+    optionalArg:    boolean
+    required:       boolean
+    type:           Object
+    valueSeparator: char
+
+    @see org.apache.commons.cli.Option for meaning of the parameters.
+    @see CliBuilderTest for example usages.
+    @author Dierk Koenig
+*/
+
+class CliBuilder {
+
+    // default settings: use setters to override
+    String usage             = 'groovy'
+    CommandLineParser parser = new PosixParser()
+    HelpFormatter formatter  = new HelpFormatter()
+    PrintWriter writer       = new PrintWriter(System.out)
+
+    Options options          = new Options()
+
+    /**
+        Recognize all one-character method calls as option specifications.
+    */
+    def invokeMethod(String name, Object args){
+        if (1 == name.size()) {
+            options.addOption(option(name, args[0], args[1]))
+            return null
+        }
+        return InvokerHelper.getMetaClass(this).invokeMethod(this, name, args)
+    }
+
+    /**
+        Make options accessible from command line args with parser (default: Posix).
+        Returns null on bad command lines.
+    */
+    OptionAccessor parse(args) {
+        try {
+            return new OptionAccessor( parser.parse(options, args as String[], true) )
+        } catch (ParseException pe) {
+            writer.println("error: " + pe.getMessage())
+            usage()
+            return null
+        }
+    }
+
+    /**
+        Print the usage message with writer (default: System.out) and formatter (default: HelpFormatter)
+    */
+    void usage(){
+        formatter.printHelp(writer, formatter.defaultWidth, usage, '', options, formatter.defaultLeftPad, formatter.defaultDescPad, '')
+        writer.flush()
+    }
+
+    // implementation details -------------------------------------
+
+    /**
+        How to create an option from the specification.
+    */
+    Option option (shortname, Map details, info){
+        Option option = new Option( shortname, info)
+        details.each { key, value ->
+            if (key == 'optionalArg')       // surprising that this extra handling is needed
+                option.setOptionalArg(value)
+            else
+                option[key] = value
+        }
+        return option
+    }
+}
+
+class OptionAccessor {
+    CommandLine inner
+    OptionAccessor(CommandLine inner){
+        this.inner = inner
+    }
+    def invokeMethod(String name, Object args){
+        return InvokerHelper.getMetaClass(inner).invokeMethod(inner, name, args)
+    }
+    def getProperty(String name) {
+        def methodname = 'getOptionValue'
+        if (name.size() > 1 && name.endsWith('s')) {
+            name = name[0..-2]
+            methodname += 's'
+        }
+        if (name.size() == 1) name = name as char
+        def result = InvokerHelper.getMetaClass(inner).invokeMethod(inner, methodname, name)
+        if (null == result) result = inner.hasOption(name)
+        if (result instanceof String[]) result = result.toList()
+        return result
+    }
+    List arguments() {
+        inner.getArgs().toList()
+    }
+}
diff --git a/groovy/src/main/groovy/util/ClosureComparator.java b/groovy/src/main/groovy/util/ClosureComparator.java
new file mode 100644
index 0000000..4b55c14
--- /dev/null
+++ b/groovy/src/main/groovy/util/ClosureComparator.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.Closure;
+import java.util.Comparator;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+/**
+ * A Comparator which uses a closure to compare 2 values being equal
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ClosureComparator implements Comparator {
+
+    Closure closure;
+
+    public ClosureComparator(Closure closure) {
+        this.closure = closure;
+    }
+
+    public int compare(Object object1, Object object2) {
+        Object value = closure.call(new Object[] {object1, object2});
+        return DefaultTypeTransformation.intUnbox(value);
+    }
+}
diff --git a/groovy/src/main/groovy/util/ConfigObject.groovy b/groovy/src/main/groovy/util/ConfigObject.groovy
new file mode 100644
index 0000000..9b0ece2
--- /dev/null
+++ b/groovy/src/main/groovy/util/ConfigObject.groovy
@@ -0,0 +1,246 @@
+/* Copyright 2006-2007 Graeme Rocher
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util
+
+
+/**
+* A ConfigObject at a simple level is a Map that creates configuration entries (other ConfigObjects) when referencing them.
+* This means that navigating to foo.bar.stuff will not return null but nested ConfigObjects which are of course empty maps
+* The Groovy truth can be used to check for the existance of "real" entries.
+*
+* @author Graeme Rocher
+* @since 1.1
+*/
+class ConfigObject extends LinkedHashMap implements Writable {
+
+    // would be better to have these availabe as constants from Groovy, but couldn't find
+    static final KEYWORDS = ['class', 'extends', 'implements', 'package','return','def',
+                             'try','finally','this','new','catch','switch','case','default','while','if',
+                             'else','elseif','private','protected','final','for','in','byte','short','break',
+                             'instanceof','synchronized','const','float','null','throws','do','super','with',
+                             'threadsafe','transient','native','interface','any','double','volatile','as',
+                             'assert','goto','enum','int','boolean','char','false','true','static','abstract',
+                             'continue','import','void','long']
+
+    static final TAB_CHARACTER = '\t'
+
+	/**
+	 * The config file that was used when parsing this ConfigObject
+	 */
+    URL configFile
+
+    ConfigObject(URL file) {
+        this.configFile = file
+    }
+
+    ConfigObject() {}
+
+    /**
+	 * Writes this config object into a String serialized representation which can later be parsed back using the parse()
+	 * method
+	 *
+     * @see groovy.lang.Writable#writeTo(java.io.Writer)
+     * @see #parse(URL)
+     */ 
+	Writer writeTo(Writer outArg) {
+        def out
+        try {
+            out = new BufferedWriter(outArg)
+            writeConfig("",this, out, 0, false)
+        } finally {
+            out.flush()
+        }
+
+		return outArg
+	}
+                  
+
+    /**
+     * Overrides the default getProperty implementation to create nested ConfigObject instances on demand
+     * for non-existant keys
+     */
+    def getProperty(String name) {  
+		if(name == 'configFile') return this.configFile
+        def prop = get(name)
+        if(prop == null) prop = new ConfigObject(this.configFile)
+        put(name, prop)
+        return prop
+    }
+    
+    /**
+     * A ConfigObject is a tree structure consisting of nested maps. This flattens the maps into
+     * a single level structure like a properties file
+     */
+    Map flatten() {
+        return flatten(null)
+    }
+    /**
+     * Flattens this ConfigObject populating the results into the target Map
+     *
+     * @see ConfigObject#flatten()
+     */
+    Map flatten(Map target) {
+        if(target == null)target = new ConfigObject()
+        populate("", target, this)
+        target
+    }
+
+    /**
+     * Merges the given map with this ConfigObject overriding any matching configuration entries in this ConfigObject
+     *
+     * @param other The ConfigObject to merge with
+     * @return The result of the merge
+     */
+    Map merge(ConfigObject other) {
+        return merge(this,other)
+    }
+
+
+    /**
+     * Converts this ConfigObject into a the java.util.Properties format, flattening the tree structure beforehand
+     * @return A java.util.Properties instance
+     */
+    Properties toProperties() {
+        def props = new Properties()
+        flatten(props)
+        props = convertValuesToString(props)
+        return props
+    }
+
+    /**
+     * Converts this ConfigObject ino the java.util.Properties format, flatten the tree and prefixing all entries with the given prefix
+     * @param prefix The prefix to append before property entries
+     * @return A java.util.Properties instance
+     */
+    Properties toProperties(String prefix) {
+        def props = new Properties()
+        populate("${prefix}.", props, this)
+        props = convertValuesToString(props)
+        return props
+    }
+
+    private merge(Map config, Map other) {
+        for(entry in other) {
+
+            def configEntry = config[entry.key]
+            if(configEntry == null) {
+                config[entry.key] = entry.value
+                continue
+            }
+            else {
+                if(configEntry instanceof Map && configEntry.size() > 0 && entry.value instanceof Map) {
+                    // recurse
+                    merge(configEntry, entry.value)
+               }
+               else {
+                    config[entry.key] = entry.value
+               }
+            }
+        }
+        return config
+
+    }
+
+    private writeConfig(String prefix,ConfigObject map, out, Integer tab, boolean apply) {
+        def space = apply ? TAB_CHARACTER*tab : ''
+        for(key in map.keySet()) {
+            def value = map.get(key)
+
+
+			if(value instanceof ConfigObject) {
+                def dotsInKeys = value.find { entry -> entry.key.indexOf('.') > -1 }
+                def configSize = value.size()
+                def firstKey = value.keySet().iterator().next()
+                def firstValue = value.values().iterator().next()
+                def firstSize
+                if(firstValue instanceof ConfigObject){
+                    firstSize = firstValue.size()
+                }
+                else { firstSize = 1 }
+				if(configSize == 1|| dotsInKeys )  {
+
+                    if(firstSize == 1 && firstValue instanceof ConfigObject) {
+                        key = KEYWORDS.contains(key) ? key.inspect() : key
+                        def writePrefix = "${prefix}${key}.${firstKey}."
+                        writeConfig(writePrefix, firstValue, out, tab, true)
+                    }
+                    else if(!dotsInKeys && firstValue instanceof ConfigObject) {
+                        writeNode(key, space, tab,value, out)
+                    }  else {
+                        for(j in value.keySet()) {
+                            def v2 = value.get(j)
+                            def k2 = j.indexOf('.') > -1 ? j.inspect() : j
+                            if(v2 instanceof ConfigObject) {
+                                key = KEYWORDS.contains(key) ? key.inspect() : key
+                                writeConfig("${prefix}${key}", v2, out, tab, false)
+                            }
+                            else {
+                                writeValue("${key}.${k2}", space, prefix, v2, out)
+                            }
+                        }
+                    }
+
+				}
+				else {
+                    writeNode(key, space,tab, value, out)
+				}
+			}
+			else {
+
+                writeValue(key, space, prefix, value, out)
+			}
+		}
+	}
+
+    private writeValue(key, space, prefix, value, out) {
+        key = key.indexOf('.') > -1 ? key.inspect() : key
+        boolean isKeyword = KEYWORDS.contains(key)
+        key = isKeyword ? key.inspect() : key
+
+        if(!prefix && isKeyword) prefix = "this."
+        out << "${space}${prefix}$key=${value.inspect()}"
+        out.newLine()
+    }
+
+    private writeNode(key, space, tab, value, out) {
+        key = KEYWORDS.contains(key) ? key.inspect() : key
+        out << "${space}$key {"
+        out.newLine()
+        writeConfig("",value, out, tab+1, true)
+        def last = "${space}}"
+        out << last
+        out.newLine()
+    }
+
+    private convertValuesToString(props) {
+        def newProps = [:]
+        for(e in props) {
+            newProps[e.key] = e.value?.toString()
+        }
+        return newProps
+    }
+
+    private populate(suffix, config, map) {
+        for(key in map.keySet()) {
+            def value = map.get(key)
+            if(value instanceof Map) {
+                populate(suffix+"${key}.", config, value)
+            }
+            else {
+                config[suffix+key] = value
+            }
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/util/ConfigSlurper.groovy b/groovy/src/main/groovy/util/ConfigSlurper.groovy
new file mode 100644
index 0000000..bd327c5
--- /dev/null
+++ b/groovy/src/main/groovy/util/ConfigSlurper.groovy
@@ -0,0 +1,265 @@
+/* Copyright 2006-2007 Graeme Rocher
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util
+
+import java.beans.Introspector
+import java.beans.BeanInfo
+
+/**
+* <p>
+* ConfigSlurper is a utility class for reading configuration files defined in the form of Groovy
+* scripts. Configuration settings can be defined using dot notation or scoped using closures
+*
+* <pre><code>
+*   grails.webflow.stateless = true
+*    smtp {
+*        mail.host = 'smtp.myisp.com'
+*        mail.auth.user = 'server'
+*    }
+*    resources.URL = "http://localhost:80/resources"
+* </pre></code>
+*
+* <p>Settings can either be bound into nested maps or onto a specified JavaBean instance. In the case
+* of the latter an error will be thrown if a property cannot be bound.
+*
+* @author Graeme Rocher
+* @since 1.1
+*
+*        <p/>
+*        Created: Jun 19, 2007
+*        Time: 3:53:48 PM
+*/
+
+class ConfigSlurper { 
+
+    private static final ENV_METHOD = "environments"
+    static final ENV_SETTINGS = '__env_settings__'
+    //private BeanInfo bean
+    //private instance
+    GroovyClassLoader classLoader = new GroovyClassLoader()
+    String environment
+    private envMode = false
+	private Map bindingVars
+
+    ConfigSlurper() { }
+
+    /**
+     * Constructs a new ConfigSlurper instance using the given environment
+     * @param env The Environment to use
+     */
+    ConfigSlurper(String env) {
+        this.environment = env
+    }
+
+	/**
+	 * Sets any additional variables that should be placed into the binding when evaluating Config scripts
+	 */
+	void setBinding(Map vars) {
+		this.bindingVars = vars
+	}
+
+    /**
+     * Parses a ConfigObject instances from an instance of java.util.Properties
+     * @param The java.util.Properties instance
+     */
+    ConfigObject parse(Properties properties) {   
+        ConfigObject config = new ConfigObject()
+        for(key in properties.keySet()) {
+            def tokens = key.split(/\./)
+            
+            def current = config
+            def currentToken
+            def last
+            def lastToken
+            for(token in tokens) {
+                last = current
+                lastToken = token
+
+                current = current."${token}"
+                if(!(current instanceof ConfigObject)) break
+            }
+
+            if(current instanceof ConfigObject) {
+                if(last[lastToken]) {
+                    def flattened = last.flatten()
+                    last.clear()
+                    flattened.each { k2, v2 -> last[k2] = v2 }
+                    last[lastToken] = properties.get(key)
+                }
+                else {                    
+                    last[lastToken] = properties.get(key)
+                }
+            }
+            current = config
+        }
+        return config
+    }
+    /**
+     * Parse the given script as a string and return the configuration object
+     *
+     * @see ConfigSlurper#parse(groovy.lang.Script)
+     */
+    ConfigObject parse(String script) {
+        return parse(classLoader.parseClass(script))
+    }
+
+    /**
+     * Create a new instance of the given script class and parse a configuration object from it
+     *
+     * @see ConfigSlurper#parse(groovy.lang.Script)
+     */
+    ConfigObject parse(Class scriptClass) {
+        return parse(scriptClass.newInstance())
+    }
+
+    /**
+     * Parse the given script into a configuration object (a Map)
+     * @param script The script to parse
+     * @return A Map of maps that can be navigating with dot de-referencing syntax to obtain configuration entries
+     */
+    ConfigObject parse(Script script) {
+         return parse(script, null)
+    }
+
+    /**
+     * Parses a Script represented by the given URL into a ConfigObject
+     *
+     * @param scriptLocation The location of the script to parse
+     * @return The ConfigObject instance
+     */
+    ConfigObject parse(URL scriptLocation) {
+        return parse(classLoader.parseClass(scriptLocation.text).newInstance(), scriptLocation)
+    }
+
+    /**
+     * Parses the passed groovy.lang.Script instance using the second argument to allow the ConfigObject
+     * to retain an reference to the original location other Groovy script
+     *
+     * @param script The groovy.lang.Script instance
+     * @param location The original location of the Script as a URL
+     * @return The ConfigObject instance
+     */
+    ConfigObject parse(Script script, URL location) {
+        def config = location ? new ConfigObject(location) : new ConfigObject()
+        GroovySystem.metaClassRegistry.removeMetaClass(script.class)
+        def mc = script.class.metaClass
+        def prefix = ""
+        LinkedList stack = new LinkedList()
+        stack << [config:config,scope:[:]]
+        def pushStack = { co ->
+	        stack << [config:co,scope:stack.last.scope.clone()]
+        }
+        def assignName = { name, co ->
+        	def current = stack.last
+        	current.config[name] = co
+        	current.scope[name] = co
+        }
+        mc.getProperty = { String name ->
+            def current = stack.last
+            def result
+            if(current.config.get(name)) {
+                result = current.config.get(name)
+            } else if(current.scope[name]) {
+                result = current.scope[name]
+            } else {
+                result = new ConfigObject()
+                assignName.call(name,result)
+            }
+            result
+        }
+        mc.invokeMethod = { String name, args ->
+            def result
+            if(args.length == 1 && args[0] instanceof Closure) {
+                if(name == ENV_METHOD) {
+                    try {
+                        envMode = true
+                        args[0].call()
+                    } finally {
+                        envMode = false
+                    }
+                } else if (envMode) {
+                    if(name == environment) {
+                        def co = new ConfigObject()
+                        config[ENV_SETTINGS] = co
+
+                        pushStack.call(co)
+                        try {
+                            envMode = false
+                            args[0].call()
+                        } finally {
+                            envMode = true
+                        }
+                        stack.pop()
+                    }
+                } else {
+                    def co = new ConfigObject()
+                    assignName.call(name, co)
+                    pushStack.call(co)
+                    args[0].call()
+                    stack.pop()
+                }
+            } else if (args.length == 2 && args[1] instanceof Closure) {
+                try {
+                   prefix = name +'.'
+                   assignName.call(name, args[0])
+                   args[1].call()
+                }  finally { prefix = "" }
+            } else {
+                MetaMethod mm = mc.getMetaMethod(name, args)
+                if(mm) {
+                    result = mm.invoke(delegate, args)
+                } else {
+                    throw new MissingMethodException(name, getClass(), args)
+                }
+            }
+            result
+        }
+        script.metaClass = mc
+
+        def setProperty = { String name, value ->
+            assignName.call(prefix+name, value)
+        }                
+     	def binding = new ConfigBinding(setProperty)
+		if(this.bindingVars) {
+			binding.getVariables().putAll(this.bindingVars)
+		}
+        script.binding = binding
+
+
+        script.run()
+
+        def envSettings = config.remove(ENV_SETTINGS)
+        if(envSettings) {
+            config.merge(envSettings)
+        }
+
+        return config
+    }
+
+}
+/**
+ * Since Groovy Script don't support overriding setProperty, we have to using a trick with the Binding to provide this
+ * functionality
+ */
+class ConfigBinding extends Binding {
+    def callable
+    ConfigBinding(Closure c) {
+        this.callable = c
+    }
+
+    void setVariable(String name, Object value) {
+        callable(name, value)
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/util/Eval.java b/groovy/src/main/groovy/util/Eval.java
new file mode 100644
index 0000000..3cfb491
--- /dev/null
+++ b/groovy/src/main/groovy/util/Eval.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyShell;
+
+/**
+ * Allow easy integration from Groovy into Java through convenience methods.
+ * 
+ * @author Dierk Koenig
+ */
+
+public class Eval {
+    /**
+     * @param expression the Groovy expression to evaluate
+     * @return the result of the expression
+     * @throws CompilationFailedException if expression is no proper Groovy
+     */
+    public static Object me(final String expression) throws CompilationFailedException {
+        return me(null, null, expression);
+    }
+
+    /**
+     * evaluate expression and make object available inside the expression as 'symbol'
+     * @param expression the Groovy expression to evaluate
+     * @return the result of the expression
+     * @throws CompilationFailedException if expression is no proper Groovy
+     */
+    public static Object me(final String symbol, final Object object, final String expression) throws CompilationFailedException {
+        Binding b = new Binding();
+        b.setVariable(symbol, object);
+        GroovyShell sh = new GroovyShell(b);
+        return sh.evaluate(expression);
+    }
+
+    /**
+     * evaluate expression and make x available inside the expression as 'x'
+     * @param expression the Groovy expression to evaluate
+     * @return the result of the expression
+     * @throws CompilationFailedException if expression is no proper Groovy
+     */
+    public static Object x(final Object x, final String expression) throws CompilationFailedException {
+        return me("x", x, expression);
+    }
+
+    /**
+     * evaluate expression and make x and y available inside the expression as 'x' and 'y'
+     * @param expression the Groovy expression to evaluate
+     * @return the result of the expression
+     * @throws CompilationFailedException if expression is no proper Groovy
+     */
+    public static Object xy(final Object x, final Object y, final String expression) throws CompilationFailedException {
+        Binding b = new Binding();
+        b.setVariable("x", x);
+        b.setVariable("y", y);
+        GroovyShell sh = new GroovyShell(b);
+        return sh.evaluate(expression);
+    }
+
+    /**
+     * evaluate expression and make x,y,z available inside the expression as 'x','y','z'
+     * @param expression the Groovy expression to evaluate
+     * @return the result of the expression
+     * @throws CompilationFailedException if expression is no proper Groovy
+     */
+    public static Object xyz(final Object x, final Object y, final Object z, final String expression) throws CompilationFailedException {
+        Binding b = new Binding();
+        b.setVariable("x", x);
+        b.setVariable("y", y);
+        b.setVariable("z", z);
+        GroovyShell sh = new GroovyShell(b);
+        return sh.evaluate(expression);
+    }
+}
diff --git a/groovy/src/main/groovy/util/Expando.java b/groovy/src/main/groovy/util/Expando.java
new file mode 100644
index 0000000..6fb5b3c
--- /dev/null
+++ b/groovy/src/main/groovy/util/Expando.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.MetaExpandoProperty;
+import groovy.lang.MissingPropertyException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+
+/**
+ * Represents a dynamically expandable bean.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Hein Meling
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+public class Expando extends GroovyObjectSupport {
+
+    private Map expandoProperties;
+
+    public Expando() {
+    }
+
+    public Expando(Map expandoProperties) {
+        this.expandoProperties = expandoProperties;
+    }
+
+    /**
+     * @return the dynamically expanded properties
+     */
+    public Map getProperties() {
+        if (expandoProperties == null) {
+            expandoProperties = createMap();
+        }
+        return expandoProperties;
+    }
+
+    public List getMetaPropertyValues() {
+        // run through all our current properties and create MetaProperty objects
+        List ret = new ArrayList();
+        Iterator itr = getProperties().entrySet().iterator();
+        while(itr.hasNext()) {
+            Entry entry = (Entry) itr.next();
+            ret.add(new MetaExpandoProperty(entry));
+        }
+		
+        return ret;
+    }
+
+    public Object getProperty(String property) {
+        // always use the expando properties first
+        Object result = getProperties().get(property);
+        if (result!=null) return result;
+        try {
+            return super.getProperty(property);
+        }
+        catch (MissingPropertyException e) {
+            // IGNORE
+        }
+        return null;
+    }
+
+    public void setProperty(String property, Object newValue) {
+        // always use the expando properties
+        getProperties().put(property, newValue);
+    }
+
+    public Object invokeMethod(String name, Object args) {
+        try {
+            return super.invokeMethod(name, args);
+        }
+        catch (GroovyRuntimeException e) {
+            // br should get a "native" property match first. getProperty includes such fall-back logic
+            Object value = this.getProperty(name);
+            if (value instanceof Closure) {
+                Closure closure = (Closure) value;
+                closure.setDelegate(this);
+                return closure.call((Object[]) args);
+            }
+            else {
+                throw e;
+            }
+        }
+        
+    }
+    
+    /**
+     * This allows toString to be overridden by a closure <i>field</i> method attached
+     * to the expando object.
+     * 
+     * @see java.lang.Object#toString()
+     */
+     public String toString() {
+        Object method = getProperties().get("toString");
+        if (method != null && method instanceof Closure) {
+            // invoke overridden toString closure method
+            Closure closure = (Closure) method;
+            closure.setDelegate(this);
+            return closure.call().toString();
+        } else {
+            return expandoProperties.toString();
+        }
+     }
+
+    /**
+     * This allows equals to be overridden by a closure <i>field</i> method attached
+     * to the expando object.
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    public boolean equals(Object obj) {
+        Object method = getProperties().get("equals");
+        if (method != null && method instanceof Closure) {
+            // invoke overridden equals closure method
+            Closure closure = (Closure) method;
+            closure.setDelegate(this);
+            Boolean ret = (Boolean) closure.call(obj);
+            return ret.booleanValue();
+        } else {
+            return super.equals(obj);
+        }
+    }
+
+    /**
+     * This allows hashCode to be overridden by a closure <i>field</i> method attached
+     * to the expando object.
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    public int hashCode() {
+        Object method = getProperties().get("hashCode");
+        if (method != null && method instanceof Closure) {
+            // invoke overridden hashCode closure method
+            Closure closure = (Closure) method;
+            closure.setDelegate(this);
+            Integer ret = (Integer) closure.call();
+            return ret.intValue();
+        } else {
+            return super.hashCode();
+        }
+    }
+
+    /**
+     * Factory method to create a new Map used to store the expando properties map
+     * @return a newly created Map implementation
+     */
+    protected Map createMap() {
+        return new HashMap();
+    }
+
+}
diff --git a/groovy/src/main/groovy/util/Factory.java b/groovy/src/main/groovy/util/Factory.java
new file mode 100644
index 0000000..e9fb883
--- /dev/null
+++ b/groovy/src/main/groovy/util/Factory.java
@@ -0,0 +1,63 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.util;

+

+import java.util.Map;

+

+/**

+ * @author Andres Almiray <aalmiray@users.sourceforge.com>

+ */ 

+public interface Factory {

+    /**

+     *

+     * @return true if no child closures should be processed

+     */

+    boolean isLeaf();

+

+    /**

+     *

+     * @param builder the FactoryBuilder

+     * @param name the name of the node being built

+     * @param value the 'value' argument in the build node

+     * @param attributes the attributes of the build arg

+     * @return the object created for the builder

+     * @throws InstantiationException

+     * @throws IllegalAccessException

+     */

+    Object newInstance( FactoryBuilderSupport builder, Object name, Object value, Map attributes )

+            throws InstantiationException, IllegalAccessException;

+

+    /**

+     *

+     * @param builder the FactoryBuilder

+     * @param node the node (returned from newINstance) to consider the attributes for

+     * @param attributes the attributes, a mutable set

+     * @return true if the factory builder should use standerd bean property matching for the remaining attributes

+     */

+    boolean onHandleNodeAttributes( FactoryBuilderSupport builder, Object node, Map attributes );

+

+    /**

+     *

+     * @param builder the FactoryBuilder

+     * @param parent the parent node (null if 'root')

+     * @param node the node just completed

+     */

+    void onNodeCompleted( FactoryBuilderSupport builder, Object parent, Object node );

+

+    void setParent( FactoryBuilderSupport builder, Object parent, Object child );

+

+    void setChild( FactoryBuilderSupport builder, Object parent, Object child );

+}

diff --git a/groovy/src/main/groovy/util/FactoryBuilderSupport.java b/groovy/src/main/groovy/util/FactoryBuilderSupport.java
new file mode 100644
index 0000000..11a2d7d
--- /dev/null
+++ b/groovy/src/main/groovy/util/FactoryBuilderSupport.java
@@ -0,0 +1,836 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.util;

+

+import groovy.lang.*;

+import org.codehaus.groovy.runtime.InvokerHelper;

+

+import java.util.*;

+import java.util.logging.Level;

+import java.util.logging.Logger;

+

+/**

+ * Mix of BuilderSupport and SwingBuilder's factory support.

+ *

+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>

+ * @author Andres Almiray <aalmiray@users.sourceforge.com>

+ */

+public abstract class FactoryBuilderSupport extends Binding {

+    public static final String CURRENT_FACTORY = "_CURRENT_FACTORY_";

+    public static final String PARENT_FACTORY = "_PARENT_FACTORY_";

+    public static final String PARENT_NODE = "_PARENT_NODE_";

+    public static final String CURRENT_NODE = "_CURRENT_NODE_";

+    public static final String PARENT_CONTEXT = "_PARENT_CONTEXT_";

+    public static final String OWNER = "owner";

+    private static final Logger LOG = Logger.getLogger( FactoryBuilderSupport.class.getName() );

+

+    /**

+     * Throws an exception if value is null.

+     *

+     * @param value the node's value

+     * @param name the node's name

+     */

+    public static void checkValueIsNull( Object value, Object name ) {

+        if( value != null ){

+            throw new RuntimeException( "'" + name + "' elements do not accept a value argument." );

+        }

+    }

+

+    /**

+     * Returns true if type is assignalbe to the value's class, false if value

+     * is null.<br>

+     *

+     * @param value the node's value

+     * @param name the node's name

+     * @param type a Class that may be assignable to the value's class

+     */

+    public static boolean checkValueIsType( Object value, Object name, Class type ) {

+        if( value != null ){

+            if( type.isAssignableFrom( value.getClass() ) ){

+                return true;

+            }else{

+                throw new RuntimeException( "The value argument of '" + name + "' must be of type "

+                        + type.getName() );

+            }

+        }else{

+            return false;

+        }

+    }

+

+    /**

+     * Returns true if type is assignale to the value's class, false if value is

+     * null or a String.<br>

+     *

+     * @param value the node's value

+     * @param name the node's name

+     * @param type a Class that may be assignable to the value's class

+     */

+    public static boolean checkValueIsTypeNotString( Object value, Object name, Class type ) {

+        if( value != null ){

+            if( type.isAssignableFrom( value.getClass() ) ){

+                return true;

+            }else if( value instanceof String ){

+                return false;

+            }else{

+                throw new RuntimeException( "The value argument of '" + name + "' must be of type "

+                        + type.getName() + " or a String." );

+            }

+        }else{

+            return false;

+        }

+    }

+

+    private LinkedList/* <Map<String,Object>> */contexts = new LinkedList/* <Map<String,Object>> */();

+    private LinkedList/* <Closure> */attributeDelegates = new LinkedList/* <Closure> */(); //

+    private List/* <Closure> */disposalClosures = new ArrayList/* <Closure> */(); // because of reverse iteration use ArrayList

+    private Map/* <String,Factory> */factories = new HashMap/* <String,Factory> */();

+    private Closure nameMappingClosure;

+    private FactoryBuilderSupport proxyBuilder;

+    private LinkedList/* <Closure> */preInstantiateDelegates = new LinkedList/* <Closure> */();

+    private LinkedList/* <Closure> */postInstantiateDelegates = new LinkedList/* <Closure> */();

+    private LinkedList/* <Closure> */postNodeCompletionDelegates = new LinkedList/* <Closure> */();

+

+    public FactoryBuilderSupport() {

+        this.proxyBuilder = this;

+    }

+

+    public FactoryBuilderSupport( Closure nameMappingClosure ) {

+        this.nameMappingClosure = nameMappingClosure;

+        this.proxyBuilder = this;

+    }

+

+    /**

+     * Returns the factory map (Unmodifiable Map).

+     */

+    public Map getFactories() {

+        return Collections.unmodifiableMap( proxyBuilder.factories );

+    }

+

+    /**

+     * Returns the context of the current node.

+     */

+    public Map getContext() {

+        if( !proxyBuilder.contexts.isEmpty() ){

+            return (Map) proxyBuilder.contexts.getFirst();

+        }

+        return null;

+    }

+

+    /**

+     * Returns the current node being built.

+     */

+    public Object getCurrent() {

+        if( !proxyBuilder.contexts.isEmpty() ){

+            Map context = (Map) proxyBuilder.contexts.getFirst();

+            return context.get( CURRENT_NODE );

+        }

+        return null;

+    }

+

+    /**

+     * Returns the factory that built the current node.

+     */

+    public Factory getCurrentFactory() {

+        if( !proxyBuilder.contexts.isEmpty() ){

+            Map context = (Map) proxyBuilder.contexts.getFirst();

+            return (Factory) context.get( CURRENT_FACTORY );

+        }

+        return null;

+    }

+

+    /**

+     * Returns the factory of the parent of the current node.

+     */

+    public Factory getParentFactory() {

+        if( !proxyBuilder.contexts.isEmpty() ){

+            Map context = (Map) proxyBuilder.contexts.getFirst();

+            return (Factory) context.get( PARENT_FACTORY );

+        }

+        return null;

+    }

+

+    /**

+     * Returns the parent of the current node.

+     */

+    public Object getParentNode() {

+        if( !proxyBuilder.contexts.isEmpty() ){

+            Map context = (Map) proxyBuilder.contexts.getFirst();

+            return context.get( PARENT_NODE );

+        }

+        return null;

+    }

+

+    /**

+     * Returns the context of the parent of the current node.

+     */

+    public Map getParentContext() {

+        if( !proxyBuilder.contexts.isEmpty() ){

+            Map context = (Map) proxyBuilder.contexts.getFirst();

+            return (Map) context.get( PARENT_CONTEXT );

+        }

+        return null;

+    }

+

+    /**

+     * Convenience method when no arguments are required

+     *

+     * @return the result of the call

+     * @param methodName the name of the method to invoke

+     */

+    public Object invokeMethod( String methodName ) {

+        return proxyBuilder.invokeMethod( methodName, null );

+    }

+

+    public Object invokeMethod( String methodName, Object args ) {

+        Object name = proxyBuilder.getName( methodName );

+        Object result = null;

+        Object previousContext = proxyBuilder.getContext();

+        try{

+            result = proxyBuilder.doInvokeMethod( methodName, name, args );

+        }catch( RuntimeException e ){

+            // remove contexts created after we started

+            if (proxyBuilder.contexts.contains(previousContext)) {

+                while (proxyBuilder.getContext() != previousContext) {

+                    proxyBuilder.popContext();

+                }

+            }

+            throw e;

+        }

+        return result;

+    }

+

+    /**

+     * Add an attribute delegate so it can intercept attributes being set.

+     * Attribute delegates are fired in a FILO pattern, so that nested delegates

+     * get first crack.

+     *

+     * @param attrDelegate

+     */

+    public Closure addAttributeDelegate( Closure attrDelegate ) {

+        proxyBuilder.attributeDelegates.addFirst( attrDelegate );

+        return attrDelegate;

+    }

+

+    /**

+     * Remove the most recently added instance of the attribute delegate.

+     *

+     * @param attrDelegate

+     */

+    public void removeAttributeDelegate( Closure attrDelegate ) {

+        proxyBuilder.attributeDelegates.remove( attrDelegate );

+    }

+

+    /**

+     * Add a preInstantiate delegate so it can intercept nodes before they are

+     * created. PreInstantiate delegates are fired in a FILO pattern, so that

+     * nested delegates get first crack.

+     *

+     * @param delegate

+     */

+    public Closure addPreInstantiateDelegate( Closure delegate ) {

+        proxyBuilder.preInstantiateDelegates.addFirst( delegate );

+        return delegate;

+    }

+

+    /**

+     * Remove the most recently added instance of the preInstantiate delegate.

+     *

+     * @param delegate

+     */

+    public void removePreInstantiateDelegate( Closure delegate ) {

+        proxyBuilder.preInstantiateDelegates.remove( delegate );

+    }

+

+    /**

+     * Add a postInstantiate delegate so it can intercept nodes after they are

+     * created. PostInstantiate delegates are fired in a FILO pattern, so that

+     * nested delegates get first crack.

+     *

+     * @param delegate

+     */

+    public Closure addPostInstantiateDelegate( Closure delegate ) {

+        proxyBuilder.postInstantiateDelegates.addFirst( delegate );

+        return delegate;

+    }

+

+    /**

+     * Remove the most recently added instance of the postInstantiate delegate.

+     *

+     * @param delegate

+     */

+    public void removePostInstantiateDelegate( Closure delegate ) {

+        proxyBuilder.postInstantiateDelegates.remove( delegate );

+    }

+

+    /**

+     * Add a nodeCompletion delegate so it can intercept nodes after they done

+     * with building. NodeCompletion delegates are fired in a FILO pattern, so

+     * that nested delegates get first crack.

+     *

+     * @param delegate

+     */

+    public Closure addPostNodeCompletionDelegate( Closure delegate ) {

+        proxyBuilder.postNodeCompletionDelegates.addFirst( delegate );

+        return delegate;

+    }

+

+    /**

+     * Remove the most recently added instance of the nodeCompletion delegate.

+     *

+     * @param delegate

+     */

+    public void removePostNodeCompletionDelegate( Closure delegate ) {

+        proxyBuilder.postNodeCompletionDelegates.remove( delegate );

+    }

+

+    /**

+     * Registers a factory for a JavaBean.<br>

+     * The JavaBean clas should have a no-args constructor.

+     */

+    public void registerBeanFactory( String theName, final Class beanClass ) {

+        proxyBuilder.registerFactory( theName, new AbstractFactory(){

+            public Object newInstance( FactoryBuilderSupport builder, Object name, Object value,

+                    Map properties ) throws InstantiationException, IllegalAccessException {

+                if( checkValueIsTypeNotString( value, name, beanClass ) ){

+                    return value;

+                }else{

+                    return beanClass.newInstance();

+                }

+            }

+        } );

+    }

+

+    /**

+     * Registers a factory for a node name.

+     */

+    public void registerFactory( String name, Factory factory ) {

+        proxyBuilder.factories.put( name, factory );

+    }

+

+    /**

+     * This method is responsible for instanciating a node and configure its

+     * properties.

+     */

+    protected Object createNode( Object name, Map attributes, Object value ) {

+        Object node = null;

+

+        Factory factory = proxyBuilder.resolveFactory( name, attributes, value );

+        if( factory == null ){

+            LOG.log( Level.WARNING, "Could not find match for name '" + name + "'" );

+            return null;

+        }

+        proxyBuilder.getContext().put( CURRENT_FACTORY, factory );

+        proxyBuilder.preInstantiate( name, attributes, value );

+        try{

+            node = factory.newInstance( this, name, value, attributes );

+            if( node == null ){

+                LOG.log( Level.WARNING, "Factory for name '" + name + "' returned null" );

+                return null;

+            }

+

+            if( LOG.isLoggable( Level.FINE ) ){

+                LOG.fine( "For name: " + name + " created node: " + node );

+            }

+        }catch( Exception e ){

+            throw new RuntimeException( "Failed to create component for '" + name + "' reason: "

+                    + e, e );

+        }

+        proxyBuilder.postInstantiate( name, attributes, node );

+        proxyBuilder.handleNodeAttributes( node, attributes );

+        return node;

+    }

+

+    /**

+     * Returns the Factory associated with name.<br>

+     * This is a hook for subclasses to plugin a custom strategy for mapping

+     * names to factories.

+     */

+    protected Factory resolveFactory( Object name, Map attributes, Object value ) {

+        return (Factory) proxyBuilder.factories.get( name );

+    }

+

+    /**

+     * This method is the workhorse of the builder.

+     */

+    private Object doInvokeMethod( String methodName, Object name, Object args ) {

+        Object node = null;

+        Closure closure = null;

+        List list = InvokerHelper.asList( args );

+

+        if( proxyBuilder.getContexts().isEmpty() ){

+            // should be called on first build method only

+            proxyBuilder.newContext();

+        }

+        switch( list.size() ){

+            case 0:

+                node = proxyBuilder.createNode( name, Collections.EMPTY_MAP, null );

+                break;

+            case 1: {

+                Object object = list.get( 0 );

+                if( object instanceof Map ){

+                    node = proxyBuilder.createNode( name, (Map) object, null );

+                }else if( object instanceof Closure ){

+                    closure = (Closure) object;

+                    node = proxyBuilder.createNode( name, Collections.EMPTY_MAP, null );

+                }else{

+                    node = proxyBuilder.createNode( name, Collections.EMPTY_MAP, object );

+                }

+            }

+                break;

+            case 2: {

+                Object object1 = list.get( 0 );

+                Object object2 = list.get( 1 );

+                if( object1 instanceof Map ){

+                    if( object2 instanceof Closure ){

+                        closure = (Closure) object2;

+                        node = proxyBuilder.createNode( name, (Map) object1, null );

+                    }else{

+                        node = proxyBuilder.createNode( name, (Map) object1, object2 );

+                    }

+                }else{

+                    if( object2 instanceof Closure ){

+                        closure = (Closure) object2;

+                        node = proxyBuilder.createNode( name, Collections.EMPTY_MAP, object1 );

+                    }else if( object2 instanceof Map ){

+                        node = proxyBuilder.createNode( name, (Map) object2, object1 );

+                    }else{

+                        throw new MissingMethodException( name.toString(), getClass(),

+                                list.toArray(), false );

+                    }

+                }

+            }

+                break;

+            case 3: {

+                Object arg0 = list.get( 0 );

+                Object arg1 = list.get( 1 );

+                Object arg2 = list.get( 2 );

+                if( arg0 instanceof Map && arg2 instanceof Closure ){

+                    closure = (Closure) arg2;

+                    node = proxyBuilder.createNode( name, (Map) arg0, arg1 );

+                }else if( arg1 instanceof Map && arg2 instanceof Closure ){

+                    closure = (Closure) arg2;

+                    node = proxyBuilder.createNode( name, (Map) arg1, arg0 );

+                }else{

+                    throw new MissingMethodException( name.toString(), getClass(), list.toArray(),

+                            false );

+                }

+            }

+                break;

+            default: {

+                throw new MissingMethodException( name.toString(), getClass(), list.toArray(),

+                        false );

+            }

+

+        }

+

+        if( node == null ){

+            if( proxyBuilder.getContexts().size() == 1 ){

+                // pop the first context

+                proxyBuilder.popContext();

+            }

+            return node;

+        }

+

+        Object current = proxyBuilder.getCurrent();

+        if( current != null ){

+            proxyBuilder.setParent( current, node );

+        }

+

+        if( closure != null ){

+            if( proxyBuilder.getCurrentFactory().isLeaf() ){

+                throw new RuntimeException( "'" + name + "' doesn't support nesting." );

+            }

+            // push new node on stack

+            Object parentFactory = proxyBuilder.getCurrentFactory();

+            Map parentContext = proxyBuilder.getContext();

+            proxyBuilder.newContext();

+            proxyBuilder.getContext().put( OWNER, closure.getOwner() );

+            proxyBuilder.getContext().put( CURRENT_NODE, node );

+            proxyBuilder.getContext().put( PARENT_FACTORY, parentFactory );

+            proxyBuilder.getContext().put( PARENT_NODE, current );

+            proxyBuilder.getContext().put( PARENT_CONTEXT, parentContext );

+            // lets register the builder as the delegate

+            proxyBuilder.setClosureDelegate( closure, node );

+            closure.call();

+            proxyBuilder.popContext();

+        }

+

+        proxyBuilder.nodeCompleted( current, node );

+        node = proxyBuilder.postNodeCompletion( current, node );

+        if( proxyBuilder.getContexts()

+                .size() == 1 ){

+            // pop the first context

+            proxyBuilder.popContext();

+        }

+        return node;

+    }

+

+    /**

+     * A hook to allow names to be converted into some other object such as a

+     * QName in XML or ObjectName in JMX.

+     *

+     * @param methodName the name of the desired method

+     * @return the object representing the name

+     */

+    protected Object getName( String methodName ) {

+        if( proxyBuilder.nameMappingClosure != null ){

+            return proxyBuilder.nameMappingClosure.call( methodName );

+        }

+        return methodName;

+    }

+

+    /**

+     * Returns the current builder that serves as a proxy.<br>

+     * Proxy builders are useful for changing the building context, thus

+     * enabling mix &amp; match builders.

+     */

+    protected FactoryBuilderSupport getProxyBuilder() {

+        return proxyBuilder;

+    }

+

+    /**

+     * Assigns any existing properties to the node.<br>

+     * It will call attributeDelegates before passing control to the factory

+     * that built the node.

+     */

+    protected void handleNodeAttributes( Object node, Map attributes ) {

+        // first, short circuit

+        if( node == null ){

+            return;

+        }

+

+        for( Iterator iter = proxyBuilder.attributeDelegates.iterator(); iter.hasNext(); ){

+            ((Closure) iter.next()).call( new Object[] { this, node, attributes } );

+        }

+

+        if( proxyBuilder.getCurrentFactory().onHandleNodeAttributes( this, node, attributes ) ){

+            proxyBuilder.setNodeAttributes( node, attributes );

+        }

+    }

+

+    /**

+     * Pushes a new context on the stack.

+     */

+    protected void newContext() {

+        proxyBuilder.contexts.addFirst( new HashMap() );

+    }

+

+    /**

+     * A hook to allow nodes to be processed once they have had all of their

+     * children applied.

+     *

+     * @param node the current node being processed

+     * @param parent the parent of the node being processed

+     */

+    protected void nodeCompleted( Object parent, Object node ) {

+        proxyBuilder.getCurrentFactory().onNodeCompleted( this, parent, node );

+    }

+

+    /**

+     * Removes the last context from the stack.

+     */

+    protected Map popContext() {

+        if( !proxyBuilder.contexts.isEmpty() ){

+            return (Map) proxyBuilder.contexts.removeFirst();

+        }

+        return null;

+    }

+

+    /**

+     * A hook after the factory creates the node and before attributes are set.<br>

+     * It will call any registered postInstantiateDelegates, if you override

+     * this method be sure to call this impl somewhere in your code.

+     */

+    protected void postInstantiate( Object name, Map attributes, Object node ) {

+        for( Iterator iter = proxyBuilder.postInstantiateDelegates.iterator(); iter.hasNext(); ){

+            ((Closure) iter.next()).call( new Object[] { this, node, attributes } );

+        }

+    }

+

+    /**

+     * A hook to allow nodes to be processed once they have had all of their

+     * children applied and allows the actual node object that represents the

+     * Markup element to be changed.<br>

+     * It will call any registered postNodeCompletionDelegates, if you override

+     * this method be sure to call this impl at the end of your code.

+     *

+     * @param node the current node being processed

+     * @param parent the parent of the node being processed

+     * @return the node, possibly new, that represents the markup element

+     */

+    protected Object postNodeCompletion( Object parent, Object node ) {

+        for( Iterator iter = proxyBuilder.postNodeCompletionDelegates.iterator(); iter.hasNext(); ){

+            ((Closure) iter.next()).call( new Object[] { this, parent, node } );

+        }

+

+        return node;

+    }

+

+    /**

+     * A hook before the factory creates the node.<br>

+     * It will call any registered preInstantiateDelegates, if you override this

+     * method be sure to call this impl somewhere in your code.

+     */

+    protected void preInstantiate( Object name, Map attributes, Object value ) {

+        for( Iterator iter = proxyBuilder.preInstantiateDelegates.iterator(); iter.hasNext(); ){

+            ((Closure) iter.next()).call( new Object[] { this, value, attributes } );

+        }

+    }

+

+    /**

+     * Clears the context stack.

+     */

+    protected void reset() {

+        proxyBuilder.contexts.clear();

+    }

+

+    /**

+     * A strategy method to allow derived builders to use builder-trees and

+     * switch in different kinds of builders. This method should call the

+     * setDelegate() method on the closure which by default passes in this but

+     * if node is-a builder we could pass that in instead (or do something wacky

+     * too)

+     *

+     * @param closure the closure on which to call setDelegate()

+     * @param node the node value that we've just created, which could be a

+     *        builder

+     */

+    protected void setClosureDelegate( Closure closure, Object node ) {

+        closure.setDelegate( this );

+    }

+

+    /**

+     * Maps attributes key/values to properties on node.

+     */

+    protected void setNodeAttributes( Object node, Map attributes ) {

+        // set the properties

+        for( Iterator iter = attributes.entrySet()

+                .iterator(); iter.hasNext(); ){

+            Map.Entry entry = (Map.Entry) iter.next();

+            String property = entry.getKey().toString();

+            Object value = entry.getValue();

+            InvokerHelper.setProperty( node, property, value );

+        }

+    }

+

+    /**

+     * Strategy method to stablish parent/child relationships.

+     */

+    protected void setParent( Object parent, Object child ) {

+        proxyBuilder.getCurrentFactory().setParent( this, parent, child );

+        Factory parentFactory = proxyBuilder.getParentFactory();

+        if( parentFactory != null ){

+            parentFactory.setChild( this, parent, child );

+        }

+    }

+

+    /**

+     * Sets the builder to be used as a proxy.

+     */

+    protected void setProxyBuilder( FactoryBuilderSupport proxyBuilder ) {

+        this.proxyBuilder = proxyBuilder;

+    }

+

+    /**

+     * Returns the stack of available contexts.

+     */

+    protected LinkedList getContexts() {

+        return proxyBuilder.contexts;

+    }

+

+    public Object build(Class viewClass) {

+        if (Script.class.isAssignableFrom(viewClass)) {

+            Script script = InvokerHelper.createScript(viewClass, this);

+            return build(script);

+        } else {

+            throw new RuntimeException("Only scripts can be executed via build(Class)");

+        }

+    }

+

+    public Object build(Script script) {

+        synchronized (script) {

+            MetaClass scriptMetaClass = script.getMetaClass();

+            try {

+                script.setMetaClass(new FactoryInterceptorMetaClass(scriptMetaClass, this));

+                script.setBinding(this);

+                return script.run();

+            } finally {

+                script.setMetaClass(scriptMetaClass);

+            }

+        }

+    }

+

+    public Object build(final String script, GroovyClassLoader loader) {

+        return build(loader.parseClass(script));

+    }

+

+    /**

+     * Switches the builder's proxyBuilder during the execution of a closure.<br>

+     * This is useful to temporary change the building context to another builder

+     * without the need for a contrived setup. It will also take care of restoring

+     * the previous proxyBuilder when the execution finishes, even if an exception

+     * was thrown from inside the closure.

+     *

+     * @param builder the temporary builder to switch to as proxyBuilder.

+     * @param closure the closure to be executed under the temporary builder.

+     *

+     * @throws RuntimeException - any exception the closure might have thrown during

+     * execution.

+     * @return the execution result of the closure.

+     */ 

+    public Object withBuilder( FactoryBuilderSupport builder, Closure closure ) {

+        if( builder == null || closure == null ) {

+	    return null;

+	}

+

+	Object result = null;

+        Object previousContext = proxyBuilder.getContext();

+	FactoryBuilderSupport previousProxyBuilder = proxyBuilder;

+	try {

+            proxyBuilder = builder;

+	    closure.setDelegate( builder );

+	    result = closure.call();

+	}

+	catch( RuntimeException e ) {

+            // remove contexts created after we started

+            proxyBuilder = previousProxyBuilder;

+            if (proxyBuilder.contexts.contains(previousContext)) {

+                while (proxyBuilder.getContext() != previousContext) {

+                    proxyBuilder.popContext();

+                }

+            }

+            throw e;

+	}

+	finally {

+            proxyBuilder = previousProxyBuilder;

+	}

+

+        return result;

+    }

+

+    /**

+     * Switches the builder's proxyBuilder during the execution of a closure.<br>

+     * This is useful to temporary change the building context to another builder

+     * without the need for a contrived setup. It will also take care of restoring

+     * the previous proxyBuilder when the execution finishes, even if an exception

+     * was thrown from inside the closure. Additionally it will use the closure's

+     * result as the value for the node identified by 'name'.

+     *

+     * @param builder the temporary builder to switch to as proxyBuilder.

+     * @param name the node to build on the 'parent' builder.

+     * @param closure the closure to be executed under the temporary builder.

+     *

+     * @throws RuntimeException - any exception the closure might have thrown during

+     * execution.

+     * @return a node that responds to value of name with the closure's result as its

+     * value.

+     */

+    public Object withBuilder( FactoryBuilderSupport builder, String name, Closure closure ) {

+       if( name == null ) {

+          return null;

+       }

+       Object result = proxyBuilder.withBuilder( builder, closure );

+       return proxyBuilder.invokeMethod( name, new Object[]{ result });

+    }

+

+    /**

+     * Switches the builder's proxyBuilder during the execution of a closure.<br>

+     * This is useful to temporary change the building context to another builder

+     * without the need for a contrived setup. It will also take care of restoring

+     * the previous proxyBuilder when the execution finishes, even if an exception

+     * was thrown from inside the closure. Additionally it will use the closure's

+     * result as the value for the node identified by 'name' and assign any attributes

+     * that might have been set.

+     *

+     * @param attributes additional properties for the node on the parent builder.

+     * @param builder the temporary builder to switch to as proxyBuilder.

+     * @param name the node to build on the 'parent' builder.

+     * @param closure the closure to be executed under the temporary builder.

+     *

+     * @throws RuntimeException - any exception the closure might have thrown during

+     * execution.

+     * @return a node that responds to value of name with the closure's result as its

+     * value.

+     */

+    public Object withBuilder( Map attributes, FactoryBuilderSupport builder, String name, Closure closure ) {

+       if( name == null ) {

+          return null;

+       }

+       Object result = proxyBuilder.withBuilder( builder, closure );

+       return proxyBuilder.invokeMethod( name, new Object[]{ attributes, result });

+    }

+

+    public void addDisposalClosure(Closure closure) {

+        disposalClosures.add(closure);

+    }

+

+    public void dispose() {

+        for (int i = disposalClosures.size() - 1; i >= 0; i--) {

+            ((Closure)disposalClosures.get(i)).call();

+        }

+    }

+}

+

+class FactoryInterceptorMetaClass extends DelegatingMetaClass {

+

+    FactoryBuilderSupport factory;

+

+    public FactoryInterceptorMetaClass(MetaClass delegate, FactoryBuilderSupport factory) {

+        super(delegate);

+        this.factory = factory;

+    }

+

+    public Object invokeMethod(Object object, String methodName, Object arguments) {

+        try {

+            return delegate.invokeMethod(object, methodName, arguments);

+        } catch (MissingMethodException mme) {

+            // attempt factory resolution

+            try {

+                if (factory.getMetaClass().respondsTo(factory, methodName).isEmpty()) {

+                    // dispatch to fectories if it is not a literal method

+                    return factory.invokeMethod(methodName, arguments);

+                } else {

+                    return InvokerHelper.invokeMethod(factory, methodName, arguments);

+                }

+            } catch (MissingMethodException mme2) {

+                // throw original

+                // should we chain in mme2 somehow?

+                throw mme;

+            }

+        }

+    }

+

+    public Object invokeMethod(Object object, String methodName, Object[] arguments) {

+        try {

+            return delegate.invokeMethod(object, methodName, arguments);

+        } catch (MissingMethodException mme) {

+            // attempt factory resolution

+            try {

+                if (factory.getMetaClass().respondsTo(factory, methodName).isEmpty()) {

+                    // dispatch to fectories if it is not a literal method

+                    return factory.invokeMethod(methodName, arguments);

+                } else {

+                    return InvokerHelper.invokeMethod(factory, methodName, arguments);

+                }

+            } catch (MissingMethodException mme2) {

+                // throw original

+                // should we chain in mme2 somehow?

+                throw mme;

+            }

+        }

+    }

+}

diff --git a/groovy/src/main/groovy/util/FileNameByRegexFinder.groovy b/groovy/src/main/groovy/util/FileNameByRegexFinder.groovy
new file mode 100644
index 0000000..1a70426
--- /dev/null
+++ b/groovy/src/main/groovy/util/FileNameByRegexFinder.groovy
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util
+
+/**
+ * Find files according to a base directory and an includes and excludes pattern.
+ * The include and exclude patterns conform to regex conventions.
+ *
+ *   @author Dierk Koenig
+ *   @author Paul King
+ */
+class FileNameByRegexFinder implements IFileNameFinder{
+
+   List getFileNames(String basedir, String pattern){
+       getFileNames(basedir, pattern, "")
+   }
+   List getFileNames(String basedir, String pattern, String excludesPattern){
+      def result = []
+      new File(basedir).eachFileRecurse {
+          if (it.path =~ pattern && (!excludesPattern || !(it.path =~ excludesPattern))) {
+              result << it.absolutePath
+          }
+      }
+      return result
+   }
+}
diff --git a/groovy/src/main/groovy/util/FileNameFinder.groovy b/groovy/src/main/groovy/util/FileNameFinder.groovy
new file mode 100644
index 0000000..b4f0f40
--- /dev/null
+++ b/groovy/src/main/groovy/util/FileNameFinder.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util
+
+/**
+ * Find files according to a base directory and an includes and excludes pattern.
+ * The include and exclude patterns conform to Ant's fileset pattern conventions.
+ *
+ *   @author Dierk Koenig
+ *   @author Paul King
+ */
+class FileNameFinder implements IFileNameFinder {
+
+    List getFileNames(String basedir, String pattern) {
+        return getFileNames(dir: basedir, includes: pattern)
+    }
+
+    List getFileNames(String basedir, String pattern, String excludesPattern) {
+        return getFileNames(dir: basedir, includes: pattern, excludes: excludesPattern)
+    }
+
+    List getFileNames(Map args) {
+        def ant = new AntBuilder()
+        def scanner = ant.fileScanner {
+            fileset(args)
+        }
+        def fls = []
+        for (f in scanner) {
+            fls << f.getAbsolutePath()
+        }
+        return fls
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/util/GroovyCollections.java b/groovy/src/main/groovy/util/GroovyCollections.java
new file mode 100644
index 0000000..8847894
--- /dev/null
+++ b/groovy/src/main/groovy/util/GroovyCollections.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.util.*;
+
+/**
+ * A Collections utility class
+ *
+ * @author Paul King
+ */
+public class GroovyCollections {
+    /**
+     * Finds all combinations of items from an array of lists.
+     *
+     * @param lists an array of lists
+     * @return a List of the combinations found
+     */
+    public static List combinations(Object[] lists) {
+        return combinations(Arrays.asList(lists));
+    }
+
+    /**
+     * Finds all combinations of items from a collection of lists.
+     *
+     * @param lists a Collection of lists
+     * @return a List of the combinations found
+     */
+    public static List combinations(Collection lists) {
+        List combinations = new ArrayList();
+        for (Iterator outer = lists.iterator(); outer.hasNext();) {
+            Object candidateList = outer.next();
+            List list = (List) DefaultTypeTransformation.castToType(candidateList, List.class);
+            if (combinations.isEmpty()) {
+                for (int i = 0; i < list.size(); i++) {
+                    List l = new ArrayList();
+                    l.add(list.get(i));
+                    combinations.add(l);
+                }
+            } else {
+                List savedCombinations = new ArrayList(combinations);
+                List newCombinations = new ArrayList();
+                for (Iterator inner = list.iterator(); inner.hasNext();) {
+                    Object value = inner.next();
+                    for (Iterator combos = savedCombinations.iterator(); combos.hasNext();) {
+                        List oldlist = new ArrayList((List) combos.next());
+                        oldlist.add(value);
+                        newCombinations.add(oldlist);
+                    }
+                }
+                combinations = newCombinations;
+            }
+        }
+        return combinations;
+    }
+
+    /**
+     * Transposes an array of lists. So,
+     * transpose([['a', 'b'], [1, 2]] as Object[]) == [['a', 1], ['b', 2]].
+     *
+     * @param lists an array of lists
+     * @return a List of the transposed lists
+     */
+    public static List transpose(Object[] lists) {
+        return transpose(Arrays.asList(lists));
+    }
+
+    /**
+     * Transposes a collection of lists. So,
+     * transpose([['a', 'b'], [1, 2]]) == [['a', 1], ['b', 2]].
+     *
+     * @param lists a Collection of lists
+     * @return a List of the transposed lists
+     */
+    public static List transpose(Collection lists) {
+        List result = new ArrayList();
+        if (lists.isEmpty() || lists.size() == 0) return result;
+        int minSize = Integer.MAX_VALUE;
+        for (Iterator outer = lists.iterator(); outer.hasNext();) {
+            Object candidateList = outer.next();
+            List list = (List) DefaultTypeTransformation.castToType(candidateList, List.class);
+            if (list.size() < minSize) minSize = list.size();
+        }
+        if (minSize == 0) return result;
+        for (int i = 0; i < minSize; i++) {
+            result.add(new ArrayList());
+        }
+        for (Iterator outer = lists.iterator(); outer.hasNext();) {
+            Object candidateList = outer.next();
+            List list = (List) DefaultTypeTransformation.castToType(candidateList, List.class);
+            for (int i = 0; i < minSize; i++) {
+                List resultList = (List) result.get(i);
+                resultList.add(list.get(i));
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Selects the minimum value found in an array of items, so
+     * min([2, 4, 6] as Object[]) == 2.
+     *
+     * @param items an array of items
+     * @return the minimum value
+     */
+    public static Object min(Object[] items) {
+        return min(Arrays.asList(items));
+    }
+
+    /**
+     * Selects the minimum value found in a collection of items.
+     *
+     * @param items a Collection
+     * @return the minimum value
+     */
+    public static Object min(Collection items) {
+        Object answer = null;
+        for (Iterator iter = items.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (value != null) {
+                if (answer == null || ScriptBytecodeAdapter.compareLessThan(value, answer)) {
+                    answer = value;
+                }
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Selects the maximum value found in an array of items, so
+     * min([2, 4, 6] as Object[]) == 6.
+     *
+     * @param items an array of items
+     * @return the maximum value
+     */
+    public static Object max(Object[] items) {
+        return max(Arrays.asList(items));
+    }
+
+    /**
+     * Selects the maximum value found in a collection
+     *
+     * @param items a Collection
+     * @return the maximum value
+     */
+    public static Object max(Collection items) {
+        Object answer = null;
+        for (Iterator iter = items.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (value != null) {
+                if (answer == null || ScriptBytecodeAdapter.compareGreaterThan(value, answer)) {
+                    answer = value;
+                }
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Sums all the items from an array of items.
+     *
+     * @param items an array of items
+     * @return the sum of the items
+     */
+    public static Object sum(Object[] items) {
+        return DefaultGroovyMethods.sum(Arrays.asList(items));
+    }
+
+    /**
+     * Sums all the items from a collection of items.
+     *
+     * @param items a collection of items
+     * @return the sum of the items
+     */
+    public static Object sum(Collection items) {
+        return DefaultGroovyMethods.sum(items);
+    }
+
+}
diff --git a/groovy/src/main/groovy/util/GroovyLog.java b/groovy/src/main/groovy/util/GroovyLog.java
new file mode 100644
index 0000000..ef5c93c
--- /dev/null
+++ b/groovy/src/main/groovy/util/GroovyLog.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.GroovyObjectSupport;
+
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+//
+// FIXME: This class really isn't all that useful.  It would be *much* better if there
+//        was a simple log API in groovy to dynamically switch to the logging facade that
+//        is actually installed.
+//
+
+/**
+ * Represents an arbitrary logging service. By default this outputs to
+ * System.out though derivations of this class could log to Jakarta Commons Logging
+ * or log4j or JDK 1.5 logging etc
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class GroovyLog extends GroovyObjectSupport {
+
+    String prefix;
+
+    /** 
+     * Factory method to create new instances 
+     */
+    public static GroovyLog newInstance(Class aClass) {
+        return new GroovyLog(aClass);
+    }
+    
+    public GroovyLog() {
+        this("");
+    }
+
+    public GroovyLog(Class type) {
+        this(type.getName());
+    }
+
+    public GroovyLog(Object obj) {
+        this(obj.getClass());
+    }
+
+    public GroovyLog(String prefix) {
+        //
+        // FIXME: This kinda sucks as a default... shouldn't tack on any [ or : muck
+        //
+
+        this.prefix = (prefix != null && prefix.length() > 0) ? "[" + prefix + ":" : "[";
+    }
+
+    public Object invokeMethod(String name, Object args) {
+        if (args != null && args.getClass().isArray()) {
+            args = DefaultGroovyMethods.join((Object[])args, ",");    
+        }
+
+        //
+        // FIXME: This kinda sucks as an output format, should probably ucase name and then
+        //        warp prefix in [] and then output the args.  Basically what the SimpleLog
+        //        does in JCL.
+        //
+        
+        System.out.println(prefix + name + "] " + args);
+
+        return null;
+    }
+}
diff --git a/groovy/src/main/groovy/util/GroovyMBean.java b/groovy/src/main/groovy/util/GroovyMBean.java
new file mode 100644
index 0000000..84e60a4
--- /dev/null
+++ b/groovy/src/main/groovy/util/GroovyMBean.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.GroovyObjectSupport;
+import groovy.lang.GroovyRuntimeException;
+
+import javax.management.*;
+import java.io.IOException;
+import java.util.*;
+
+/**
+ * A GroovyObject facade for an underlying MBean which acts like a normal
+ * groovy object but which is actually implemented via
+ * an underlying JMX MBean.
+ * Properties and normal method invocations
+ * delegate to the MBeanServer to the actual MBean.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Steve Button
+ * @author Paul King
+ * @version $Revision$
+ */
+public class GroovyMBean extends GroovyObjectSupport {
+    private final MBeanServerConnection server;
+    private final ObjectName name;
+    private MBeanInfo beanInfo;
+    private final boolean ignoreErrors;
+    private final Map operations = new HashMap();
+
+    public GroovyMBean(MBeanServerConnection server, String objectName) throws JMException, IOException {
+        this(server, objectName, false);
+    }
+
+    public GroovyMBean(MBeanServerConnection server, String objectName, boolean ignoreErrors) throws JMException, IOException {
+        this(server, new ObjectName(objectName), ignoreErrors);
+    }
+
+    public GroovyMBean(MBeanServerConnection server, ObjectName name) throws JMException, IOException {
+        this(server, name, false);
+    }
+
+    public GroovyMBean(MBeanServerConnection server, ObjectName name, boolean ignoreErrors) throws JMException, IOException {
+        this.server = server;
+        this.name = name;
+        this.ignoreErrors = ignoreErrors;
+        this.beanInfo = server.getMBeanInfo(name);
+
+        MBeanOperationInfo[] operationInfos = beanInfo.getOperations();
+        for (int i = 0; i < operationInfos.length; i++) {
+            MBeanOperationInfo info = operationInfos[i];
+            String signature[] = createSignature(info);
+
+            // Include a simple fix here to support overloaded operations on the MBean.
+            // Construct a simple key for an operation by adding the number of parameters it uses
+            String operationKey = createOperationKey(info.getName(), signature.length);
+            operations.put(operationKey, signature);
+        }
+    }
+
+    public MBeanServerConnection server() {
+        return server;
+    }
+
+    public ObjectName name() {
+        return name;
+    }
+
+    public MBeanInfo info() {
+        return beanInfo;
+    }
+
+    public Object getProperty(String property) {
+        try {
+            return server.getAttribute(name, property);
+        }
+        catch (MBeanException e) {
+            throwExceptionWithTarget("Could not access property: " + property + ". Reason: ", e);
+        }
+        catch (Exception e) {
+            if (!ignoreErrors)
+            throwException("Could not access property: " + property + ". Reason: ", e);
+        }
+        return null;
+    }
+
+    public void setProperty(String property, Object value) {
+        try {
+            server.setAttribute(name, new Attribute(property, value));
+        }
+        catch (MBeanException e) {
+            throwExceptionWithTarget("Could not set property: " + property + ". Reason: ", e);
+        }
+        catch (Exception e) {
+            throwException("Could not set property: " + property + ". Reason: ", e);
+        }
+    }
+
+    public Object invokeMethod(String method, Object arguments) {
+        // Moved this outside the try block so we can obtain the number of parameters
+        // specified in the arguments array, which is needed to find the correct method.
+        Object[] argArray = null;
+        if (arguments instanceof Object[]) {
+            argArray = (Object[]) arguments;
+        } else {
+            argArray = new Object[]{arguments};
+        }
+        // Locate the specific method based on the name and number of parameters
+        String operationKey = createOperationKey(method, argArray.length);
+        String[] signature = (String[]) operations.get(operationKey);
+
+        if (signature != null) {
+            try {
+                return server.invoke(name, method, argArray, signature);
+            }
+            catch (MBeanException e) {
+                throwExceptionWithTarget("Could not invoke method: " + method + ". Reason: ", e);
+            }
+            catch (Exception e) {
+                throwException("Could not invoke method: " + method + ". Reason: ", e);
+            }
+            return null;
+        } else {
+            return super.invokeMethod(method, arguments);
+        }
+    }
+
+    protected String[] createSignature(MBeanOperationInfo info) {
+        MBeanParameterInfo[] params = info.getSignature();
+        String[] answer = new String[params.length];
+        for (int i = 0; i < params.length; i++) {
+            answer[i] = params[i].getType();
+        }
+        return answer;
+    }
+
+    /**
+     * Construct a simple key based on the method name and the number of parameters
+     *
+     * @param operation - the mbean operation name
+     * @param params    - the number of parameters the operation supports
+     * @return simple unique identifier for a method
+     */
+    protected String createOperationKey(String operation, int params) {
+        // This could be changed to support some hash of the parameter types, etc.
+        return operation + "_" + params;
+    }
+
+    /**
+     * List of the names of each of the attributes on the MBean
+     *
+     * @return list of attribute names
+     */
+    public Collection listAttributeNames() {
+        List list = new ArrayList();
+        try {
+            MBeanAttributeInfo[] attrs = beanInfo.getAttributes();
+            for (int i = 0; i < attrs.length; i++) {
+                MBeanAttributeInfo attr = attrs[i];
+                list.add(attr.getName());
+            }
+        } catch (Exception e) {
+            throwException("Could not list attribute names. Reason: ", e);
+        }
+        return list;
+    }
+
+    /**
+     * The values of each of the attributes on the MBean
+     *
+     * @return list of values of each attribute
+     */
+    public List listAttributeValues() {
+        List list = new ArrayList();
+        Collection names = listAttributeNames();
+        for (Iterator iterator = names.iterator(); iterator.hasNext();) {
+            String name = (String) iterator.next();
+            try {
+                Object val = this.getProperty(name);
+                if (val != null) {
+                    list.add(name + " : " + val.toString());
+                }
+            } catch (Exception e) {
+                throwException("Could not list attribute values. Reason: ", e);
+            }
+        }
+        return list;
+    }
+
+    /**
+     * List of string representations of all of the attributes on the MBean.
+     *
+     * @return list of descriptions of each attribute on the mbean
+     */
+    public Collection listAttributeDescriptions() {
+        List list = new ArrayList();
+        try {
+            MBeanAttributeInfo[] attrs = beanInfo.getAttributes();
+            for (int i = 0; i < attrs.length; i++) {
+                MBeanAttributeInfo attr = attrs[i];
+                list.add(describeAttribute(attr));
+            }
+        } catch (Exception e) {
+            throwException("Could not list attribute descriptions. Reason: ", e);
+        }
+        return list;
+    }
+
+    /**
+     * Description of the specified attribute name.
+     *
+     * @param attr - the attribute
+     * @return String the description
+     */
+    protected String describeAttribute(MBeanAttributeInfo attr) {
+        StringBuffer buf = new StringBuffer();
+        buf.append("(");
+        if (attr.isReadable()) {
+            buf.append("r");
+        }
+        if (attr.isWritable()) {
+            buf.append("w");
+        }
+        buf.append(") ")
+                .append(attr.getType())
+                .append(" ")
+                .append(attr.getName());
+        return buf.toString();
+    }
+
+    /**
+     * Description of the specified attribute name.
+     *
+     * @param attributeName - stringified name of the attribute
+     * @return the description
+     */
+    public String describeAttribute(String attributeName) {
+        String ret = "Attribute not found";
+        try {
+            MBeanAttributeInfo[] attributes = beanInfo.getAttributes();
+            for (int i = 0; i < attributes.length; i++) {
+                MBeanAttributeInfo attribute = attributes[i];
+                if (attribute.getName().equals(attributeName)) {
+                    return describeAttribute(attribute);
+                }
+            }
+        } catch (Exception e) {
+            throwException("Could not describe attribute '" + attributeName + "'. Reason: ", e);
+        }
+        return ret;
+    }
+
+    /**
+     * Names of all the operations available on the MBean.
+     *
+     * @return all the operations on the MBean
+     */
+    public Collection listOperationNames() {
+        List list = new ArrayList();
+        try {
+            MBeanOperationInfo[] operations = beanInfo.getOperations();
+            for (int i = 0; i < operations.length; i++) {
+                MBeanOperationInfo operation = operations[i];
+                list.add(operation.getName());
+            }
+        } catch (Exception e) {
+            throwException("Could not list operation names. Reason: ", e);
+        }
+        return list;
+    }
+
+    /**
+     * Description of all of the operations available on the MBean.
+     *
+     * @return full description of each operation on the MBean
+     */
+    public Collection listOperationDescriptions() {
+        List list = new ArrayList();
+        try {
+            MBeanOperationInfo[] operations = beanInfo.getOperations();
+            for (int i = 0; i < operations.length; i++) {
+                MBeanOperationInfo operation = operations[i];
+                list.add(describeOperation(operation));
+            }
+        } catch (Exception e) {
+            throwException("Could not list operation descriptions. Reason: ", e);
+        }
+        return list;
+    }
+
+    /**
+     * Get the description of the specified operation.  This returns a Collection since
+     * operations can be overloaded and one operationName can have multiple forms.
+     *
+     * @param operationName the name of the operation to describe
+     * @return Collection of operation description
+     */
+    public List describeOperation(String operationName) {
+        List list = new ArrayList();
+        try {
+            MBeanOperationInfo[] operations = beanInfo.getOperations();
+            for (int i = 0; i < operations.length; i++) {
+                MBeanOperationInfo operation = operations[i];
+                if (operation.getName().equals(operationName)) {
+                    list.add(describeOperation(operation));
+                }
+            }
+        } catch (Exception e) {
+            throwException("Could not describe operations matching name '" + operationName + "'. Reason: ", e);
+        }
+        return list;
+    }
+
+    /**
+     * Description of the operation.
+     *
+     * @param operation the operation to describe
+     * @return pretty-printed description
+     */
+    protected String describeOperation(MBeanOperationInfo operation) {
+        StringBuffer buf = new StringBuffer();
+        buf.append(operation.getReturnType())
+                .append(" ")
+                .append(operation.getName())
+                .append("(");
+
+        MBeanParameterInfo[] params = operation.getSignature();
+        for (int j = 0; j < params.length; j++) {
+            MBeanParameterInfo param = params[j];
+            if (j != 0) {
+                buf.append(", ");
+            }
+            buf.append(param.getType())
+                    .append(" ")
+                    .append(param.getName());
+        }
+        buf.append(")");
+        return buf.toString();
+    }
+
+    /**
+     * Return an end user readable representation of the underlying MBean
+     *
+     * @return the user readable description
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("MBean Name:")
+                .append("\n  ")
+                .append(name.getCanonicalName())
+                .append("\n  ");
+        if (!listAttributeDescriptions().isEmpty()) {
+            buf.append("\nAttributes:");
+            for (Iterator iterator = listAttributeDescriptions().iterator(); iterator.hasNext();) {
+                buf.append("\n  ")
+                        .append((String) iterator.next());
+            }
+        }
+        if (!listOperationDescriptions().isEmpty()) {
+            buf.append("\nOperations:");
+            for (Iterator iterator = listOperationDescriptions().iterator(); iterator.hasNext();) {
+                buf.append("\n  ")
+                        .append((String) iterator.next());
+            }
+        }
+        return buf.toString();
+    }
+
+    private void throwException(String m, Exception e) {
+        if (!ignoreErrors) {
+            throw new GroovyRuntimeException(m + e, e);
+        }
+    }
+
+    private void throwExceptionWithTarget(String m, MBeanException e) {
+        if (!ignoreErrors) {
+            throw new GroovyRuntimeException(m + e, e.getTargetException());
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/util/GroovyScriptEngine.java b/groovy/src/main/groovy/util/GroovyScriptEngine.java
new file mode 100644
index 0000000..c7c9487
--- /dev/null
+++ b/groovy/src/main/groovy/util/GroovyScriptEngine.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.Script;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * Specific script engine able to reload modified scripts as well as dealing properly with dependent scripts.
+ *
+ * @author sam
+ * @author Marc Palmer
+ * @author Guillaume Laforge
+ */
+public class GroovyScriptEngine implements ResourceConnector {
+
+    /**
+     * Simple testing harness for the GSE. Enter script roots as arguments and
+     * then input script names to run them.
+     *
+     * @param urls array of URLs
+     * @throws Exception
+     */
+    public static void main(String[] urls) throws Exception {
+        URL[] roots = new URL[urls.length];
+        for (int i = 0; i < roots.length; i++) {
+            if(urls[i].indexOf("://") != -1) {
+                roots[i] = new URL(urls[i]);
+            } else {
+                roots[i] = new File(urls[i]).toURI().toURL();
+            }
+        }
+        GroovyScriptEngine gse = new GroovyScriptEngine(roots);
+        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
+        String line;
+        while (true) {
+            System.out.print("groovy> ");
+            if ((line = br.readLine()) == null || line.equals("quit"))
+                break;
+            try {
+                System.out.println(gse.run(line, new Binding()));
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private URL[] roots;
+    private Map scriptCache = Collections.synchronizedMap(new HashMap());
+    private ResourceConnector rc;
+    // private ClassLoader parentClassLoader = getClass().getClassLoader();
+
+    //private ScriptCacheEntry currentCacheEntry = null;
+    private static ThreadLocal currentCacheEntryHolder = new ThreadLocal();
+    private GroovyClassLoader groovyLoader = null;
+
+    private static class ScriptCacheEntry {
+        private Class scriptClass;
+        private long lastModified;
+        private Map dependencies = new HashMap();
+    }
+
+    /**
+     * Initialize a new GroovyClassLoader with the parentClassLoader passed as a parameter
+     * A GroovyScriptEngine should only use one GroovyClassLoader but since in version
+     * prior to 1.0-RC-01 you could set a new parentClassLoader
+     * Ultimately groovyLoader should be final and only set in the constructor
+     *
+     * @param parentClassLoader
+     */
+    private void initGroovyLoader(final ClassLoader parentClassLoader) {
+        if (groovyLoader == null || groovyLoader.getParent() != parentClassLoader) {
+            groovyLoader =
+                    (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+                        public Object run() {
+                            return new GroovyClassLoader(parentClassLoader) {
+                                protected Class findClass(String className) throws ClassNotFoundException {
+                                    String filename = className.replace('.', File.separatorChar) + ".groovy";
+                                    URLConnection dependentScriptConn = null;
+                                    try {
+                                        dependentScriptConn = rc.getResourceConnection(filename);
+                                        ScriptCacheEntry currentCacheEntry = (ScriptCacheEntry) currentCacheEntryHolder.get();
+                                        currentCacheEntry.dependencies.put(
+                                                dependentScriptConn.getURL(),
+                                                new Long(dependentScriptConn.getLastModified()));
+                                        return parseClass(dependentScriptConn.getInputStream(), filename);
+                                    } catch (ResourceException e1) {
+                                        throw new ClassNotFoundException("Could not read " + className + ": " + e1);
+                                    } catch (CompilationFailedException e2) {
+                                        throw new ClassNotFoundException("Syntax error in " + className + ": " + e2);
+                                    } catch (IOException e3) {
+                                        throw new ClassNotFoundException("Problem reading " + className + ": " + e3);
+                                    } finally {
+                                        try {
+                                            if (dependentScriptConn != null && dependentScriptConn.getInputStream() != null) {
+                                                dependentScriptConn.getInputStream().close();
+                                            }
+                                        } catch (IOException e) {
+                                            // IGNORE
+                                        }
+                                    }
+                                }
+                            };
+                        }
+                    });
+        }
+    }
+
+    /**
+     * Get a resource connection as a <code>URLConnection</code> to retrieve a script
+     * from the <code>ResourceConnector</code>
+     *
+     * @param resourceName name of the resource to be retrieved
+     * @return a URLConnection to the resource
+     * @throws ResourceException
+     */
+    public URLConnection getResourceConnection(String resourceName) throws ResourceException {
+        // Get the URLConnection
+        URLConnection groovyScriptConn = null;
+
+        ResourceException se = null;
+        for (int i = 0; i < roots.length; i++) {
+            URL scriptURL = null;
+            InputStream in = null;
+            try {
+                scriptURL = new URL(roots[i], resourceName);
+
+                groovyScriptConn = scriptURL.openConnection();
+
+                // Make sure we can open it, if we can't it doesn't exist.
+                // Could be very slow if there are any non-file:// URLs in there
+                in = groovyScriptConn.getInputStream();
+
+                break; // Now this is a bit unusual
+
+            } catch (MalformedURLException e) {
+                String message = "Malformed URL: " + roots[i] + ", " + resourceName;
+                if (se == null) {
+                    se = new ResourceException(message);
+                } else {
+                    se = new ResourceException(message, se);
+                }
+            } catch (IOException e1) {
+                String message = "Cannot open URL: " + scriptURL;
+                if (se == null) {
+                    se = new ResourceException(message);
+                } else {
+                    se = new ResourceException(message, se);
+                }
+            }
+        }
+
+        // If we didn't find anything, report on all the exceptions that occurred.
+        if (groovyScriptConn == null) {
+            throw se;
+        }
+
+        return groovyScriptConn;
+    }
+
+    /**
+     * The groovy script engine will run groovy scripts and reload them and
+     * their dependencies when they are modified. This is useful for embedding
+     * groovy in other containers like games and application servers.
+     *
+     * @param roots This an array of URLs where Groovy scripts will be stored. They should
+     *              be layed out using their package structure like Java classes
+     */
+    public GroovyScriptEngine(URL[] roots) {
+        this.roots = roots;
+        this.rc = this;
+        initGroovyLoader(getClass().getClassLoader());
+    }
+
+    public GroovyScriptEngine(URL[] roots, ClassLoader parentClassLoader) {
+        this(roots);
+        initGroovyLoader(parentClassLoader);
+    }
+
+    public GroovyScriptEngine(String[] urls) throws IOException {
+        roots = new URL[urls.length];
+        for (int i = 0; i < roots.length; i++) {
+            roots[i] = new File(urls[i]).toURI().toURL();
+        }
+        this.rc = this;
+        initGroovyLoader(getClass().getClassLoader());
+    }
+
+    public GroovyScriptEngine(String[] urls, ClassLoader parentClassLoader) throws IOException {
+        this(urls);
+        initGroovyLoader(parentClassLoader);
+    }
+
+    public GroovyScriptEngine(String url) throws IOException {
+        roots = new URL[1];
+        roots[0] = new File(url).toURI().toURL();
+        this.rc = this;
+        initGroovyLoader(getClass().getClassLoader());
+    }
+
+    public GroovyScriptEngine(String url, ClassLoader parentClassLoader) throws IOException {
+        this(url);
+        initGroovyLoader(parentClassLoader);
+    }
+
+    public GroovyScriptEngine(ResourceConnector rc) {
+        this.rc = rc;
+        initGroovyLoader(getClass().getClassLoader());
+    }
+
+    public GroovyScriptEngine(ResourceConnector rc, ClassLoader parentClassLoader) {
+        this(rc);
+        initGroovyLoader(parentClassLoader);
+    }
+
+    /**
+     * Get the <code>ClassLoader</code> that will serve as the parent ClassLoader of the
+     * {@link GroovyClassLoader} in which scripts will be executed. By default, this is the
+     * ClassLoader that loaded the <code>GroovyScriptEngine</code> class.
+     *
+     * @return parent classloader used to load scripts
+     */
+    public ClassLoader getParentClassLoader() {
+        return groovyLoader.getParent();
+    }
+
+    /**
+     * @param parentClassLoader ClassLoader to be used as the parent ClassLoader for scripts executed by the engine
+     * @deprecated
+     */
+    public void setParentClassLoader(ClassLoader parentClassLoader) {
+        if (parentClassLoader == null) {
+            throw new IllegalArgumentException("The parent class loader must not be null.");
+        }
+        initGroovyLoader(parentClassLoader);
+    }
+
+    /**
+     * Get the class of the scriptName in question, so that you can instantiate Groovy objects with caching and reloading.
+     *
+     * @param scriptName
+     * @return the loaded scriptName as a compiled class
+     * @throws ResourceException
+     * @throws ScriptException
+     */
+    public Class loadScriptByName(String scriptName) throws ResourceException, ScriptException {
+        scriptName = scriptName.replace('.', File.separatorChar) + ".groovy";
+        ScriptCacheEntry entry = updateCacheEntry(scriptName);
+        return entry.scriptClass;
+    }
+
+
+    /**
+     * Get the class of the scriptName in question, so that you can instantiate Groovy objects with caching and reloading.
+     *
+     * @param scriptName
+     * @return the loaded scriptName as a compiled class
+     * @throws ResourceException
+     * @throws ScriptException
+     * @deprecated
+     */
+    public Class loadScriptByName(String scriptName, ClassLoader parentClassLoader)
+            throws ResourceException, ScriptException {
+        initGroovyLoader(parentClassLoader);
+        return loadScriptByName(scriptName);
+    }
+
+    /**
+     * Locate the class and reload it or any of its dependencies
+     *
+     * @param scriptName
+     * @return the scriptName cache entry
+     * @throws ResourceException
+     * @throws ScriptException
+     */
+    private ScriptCacheEntry updateCacheEntry(String scriptName)
+            throws ResourceException, ScriptException {
+        ScriptCacheEntry entry;
+
+        scriptName = scriptName.intern();
+        synchronized (scriptName) {
+
+            URLConnection groovyScriptConn = rc.getResourceConnection(scriptName);
+
+            // URL last modified
+            long lastModified = groovyScriptConn.getLastModified();
+            // Check the cache for the scriptName
+            entry = (ScriptCacheEntry) scriptCache.get(scriptName);
+            // If the entry isn't null check all the dependencies
+
+            boolean dependencyOutOfDate = false;
+            if (entry != null) {
+
+                for (Iterator i = entry.dependencies.keySet().iterator(); i.hasNext();) {
+                    URLConnection urlc = null;
+                    URL url = (URL) i.next();
+                    try {
+                        urlc = url.openConnection();
+                        urlc.setDoInput(false);
+                        urlc.setDoOutput(false);
+                        long dependentLastModified = urlc.getLastModified();
+                        if (dependentLastModified > ((Long) entry.dependencies.get(url)).longValue()) {
+                            dependencyOutOfDate = true;
+                            break;
+                        }
+                    } catch (IOException ioe) {
+                        dependencyOutOfDate = true;
+                        break;
+                    }
+                }
+            }
+
+            if (entry == null || entry.lastModified < lastModified || dependencyOutOfDate) {
+                // Make a new entry
+                ScriptCacheEntry currentCacheEntry = new ScriptCacheEntry();
+                currentCacheEntryHolder.set(currentCacheEntry);
+                InputStream in = null;
+
+                try {
+                    in = groovyScriptConn.getInputStream();
+                    currentCacheEntry.scriptClass = groovyLoader.parseClass(in, scriptName);
+                } catch (Exception e) {
+                    throw new ScriptException("Could not parse scriptName: " + scriptName, e);
+                } finally {
+                    currentCacheEntryHolder.set(null);
+                    try {
+                        if (in != null)
+                            in.close();
+                    } catch (IOException e) {
+                        // Do nothing: Just want to make sure it is closed
+                    }
+                }
+
+                currentCacheEntry.lastModified = lastModified;
+                scriptCache.put(scriptName, currentCacheEntry);
+
+                entry = currentCacheEntry;
+                currentCacheEntry = null;
+            }
+        }
+        return entry;
+    }
+
+    /**
+     * Run a script identified by name.
+     *
+     * @param scriptName name of the script to run
+     * @param argument   a single argument passed as a variable named <code>arg</code> in the binding
+     * @return a <code>toString()</code> representation of the result of the execution of the script
+     * @throws ResourceException
+     * @throws ScriptException
+     */
+    public String run(String scriptName, String argument) throws ResourceException, ScriptException {
+        Binding binding = new Binding();
+        binding.setVariable("arg", argument);
+        Object result = run(scriptName, binding);
+        return result == null ? "" : result.toString();
+    }
+
+    /**
+     * Run a script identified by name.
+     *
+     * @param scriptName name of the script to run
+     * @param binding    binding to pass to the script
+     * @return an object
+     * @throws ResourceException
+     * @throws ScriptException
+     */
+    public Object run(String scriptName, Binding binding) throws ResourceException, ScriptException {
+
+        ScriptCacheEntry entry = updateCacheEntry(scriptName);
+        Script scriptObject = InvokerHelper.createScript(entry.scriptClass, binding);
+        return scriptObject.run();
+    }
+}
diff --git a/groovy/src/main/groovy/util/GroovyTestCase.java b/groovy/src/main/groovy/util/GroovyTestCase.java
new file mode 100644
index 0000000..40d645b
--- /dev/null
+++ b/groovy/src/main/groovy/util/GroovyTestCase.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.GroovyShell;
+import junit.framework.TestCase;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.logging.Logger;
+
+/**
+ * A default JUnit TestCase in Groovy. This provides a number of helper methods
+ * plus avoids the JUnit restriction of requiring all test* methods to be void
+ * return type.
+ *
+ * @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Dierk Koenig (the notYetImplemented feature, changes to shouldFail)
+ * @version $Revision$
+ */
+public class GroovyTestCase extends TestCase {
+
+    protected static Logger log = Logger.getLogger(GroovyTestCase.class.getName());
+    private static int counter;
+    private boolean useAgileDoxNaming = false;
+
+    public GroovyTestCase() {
+    }
+
+    /**
+     * Overload the getName() method to make the test cases look more like AgileDox
+     * (thanks to Joe Walnes for this tip!)
+     */
+    public String getName() {
+        if (useAgileDoxNaming) {
+            return super.getName().substring(4).replaceAll("([A-Z])", " $1").toLowerCase();
+        }
+        else {
+            return super.getName();
+        }
+    }
+
+    public String getMethodName() {
+        return super.getName();
+    }
+
+    /**
+     * Asserts that the arrays are equivalent and contain the same values
+     *
+     * @param expected
+     * @param value
+     */
+    protected void assertArrayEquals(Object[] expected, Object[] value) {
+        String message =
+            "expected array: " + InvokerHelper.toString(expected) + " value array: " + InvokerHelper.toString(value);
+        assertNotNull(message + ": expected should not be null", expected);
+        assertNotNull(message + ": value should not be null", value);
+        assertEquals(message, expected.length, value.length);
+        for (int i = 0, size = expected.length; i < size; i++) {
+            assertEquals("value[" + i + "] when " + message, expected[i], value[i]);
+        }
+    }
+
+    /**
+     * Asserts that the array of characters has a given length
+     *
+     * @param length expected length
+     * @param array the array
+     */
+    protected void assertLength(int length, char[] array) {
+        assertEquals(length, array.length);
+    }
+
+    /**
+     * Asserts that the array of ints has a given length
+     *
+     * @param length expected length
+     * @param array the array
+     */
+    protected void assertLength(int length, int[] array) {
+        assertEquals(length, array.length);
+    }
+
+    /**
+     * Asserts that the array of objects has a given length
+     *
+     * @param length expected length
+     * @param array the array
+     */
+    protected void assertLength(int length, Object[] array) {
+        assertEquals(length, array.length);
+    }
+
+    /**
+     * Asserts that the array of characters contains a given char
+     *
+     * @param expected expected character to be found
+     * @param array the array
+     */
+    protected void assertContains(char expected, char[] array) {
+        for (int i = 0; i < array.length; ++i) {
+            if (array[i] == expected) {
+                return;
+            }
+        }
+
+        StringBuffer message = new StringBuffer();
+
+        message.append(expected).append(" not in {");
+
+        for (int i = 0; i < array.length; ++i) {
+            message.append("'").append(array[i]).append("'");
+
+            if (i < (array.length - 1)) {
+                message.append(", ");
+            }
+        }
+
+        message.append(" }");
+
+        fail(message.toString());
+    }
+
+    /**
+     * Asserts that the array of ints contains a given int
+     *
+     * @param expected expected int
+     * @param array the array
+     */
+    protected void assertContains(int expected, int[] array) {
+        for (int i = 0; i < array.length; ++i) {
+            if (array[i] == expected) {
+                return;
+            }
+        }
+
+        StringBuffer message = new StringBuffer();
+
+        message.append(expected).append(" not in {");
+
+        for (int i = 0; i < array.length; ++i) {
+            message.append("'").append(array[i]).append("'");
+
+            if (i < (array.length - 1)) {
+                message.append(", ");
+            }
+        }
+
+        message.append(" }");
+
+        fail(message.toString());
+    }
+
+    /**
+     * Asserts that the value of toString() on the given object matches the
+     * given text string
+     *
+     * @param value the object to be output to the console
+     * @param expected the expected String representation
+     */
+    protected void assertToString(Object value, String expected) {
+        Object console = InvokerHelper.invokeMethod(value, "toString", null);
+        assertEquals("toString() on value: " + value, expected, console);
+    }
+
+    /**
+     * Asserts that the value of inspect() on the given object matches the
+     * given text string
+     *
+     * @param value the object to be output to the console
+     * @param expected the expected String representation
+     */
+    protected void assertInspect(Object value, String expected) {
+        Object console = InvokerHelper.invokeMethod(value, "inspect", null);
+        assertEquals("inspect() on value: " + value, expected, console);
+    }
+
+    /**
+     * Asserts that the script runs without any exceptions
+     *
+     * @param script the script that should pass without any exception thrown
+     */
+    protected void assertScript(final String script) throws Exception {
+        GroovyShell shell = new GroovyShell();
+        shell.evaluate(script, getTestClassName());
+    }
+
+    protected String getTestClassName() {
+        return "TestScript" + getMethodName() + (counter++) + ".groovy";
+    }
+
+    /**
+     * Asserts that the given code closure fails when it is evaluated
+     *
+     * @param code
+     * @return the message of the thrown Throwable
+     */
+    protected String shouldFail(Closure code) {
+        boolean failed = false;
+        String result = null;
+        try {
+            code.call();
+        }
+        catch (Throwable e) {
+                failed = true;
+                result = e.getMessage();
+        }
+        assertTrue("Closure " + code + " should have failed", failed);
+        return result;
+    }
+
+    /**
+     * Asserts that the given code closure fails when it is evaluated
+     * and that a particular exception is thrown.
+     *
+     * @param clazz the class of the expected exception
+     * @param code the closure that should fail
+     * @return the message of the expected Throwable
+     */
+    protected String shouldFail(Class clazz, Closure code) {
+        Throwable th = null;
+        try {
+            code.call();
+        } catch (GroovyRuntimeException gre) {
+            th = unwrap(gre);
+        } catch (Throwable e) {
+            th = e;
+        }
+
+        if (th==null) {
+            fail("Closure " + code + " should have failed with an exception of type " + clazz.getName());
+        } else if (! clazz.isInstance(th)) {
+            fail("Closure " + code + " should have failed with an exception of type " + clazz.getName() + ", instead got Exception " + th);
+        }
+        return th.getMessage();
+    }
+
+    protected String shouldFailWithCause(Class clazz, Closure code) {
+        Throwable th = null;
+        try {
+            code.call();
+        } catch (GroovyRuntimeException gre) {
+            th = gre;
+            while (th.getCause()!=null && th.getCause()!=gre){ // if wrapped, find the root cause
+                th=th.getCause();
+                if (th!=gre && (th instanceof GroovyRuntimeException)) {
+                    gre = (GroovyRuntimeException) th;
+                }
+            }
+        } catch (Throwable e) {
+            th = e;
+        }
+
+        if (th==null) {
+            fail("Closure " + code + " should have failed with an exception of type " + clazz.getName());
+        } else if (! clazz.isInstance(th)) {
+            fail("Closure " + code + " should have failed with an exception of type " + clazz.getName() + ", instead got Exception " + th);
+        }
+        return th.getMessage();
+    }
+
+    /**
+     *  Returns a copy of a string in which all EOLs are \n.
+     */
+    protected String fixEOLs( String value )
+    {
+        return value.replaceAll( "(\\r\\n?)|\n", "\n" );
+    }
+
+    /**
+     * Runs the calling JUnit test again and fails only if it unexpectedly runs.<br/>
+     * This is helpful for tests that don't currently work but should work one day,
+     * when the tested functionality has been implemented.<br/>
+     * The right way to use it is:
+     * <pre>
+     * public void testXXX() {
+     *   if (GroovyTestCase.notYetImplemented(this)) return;
+     *   ... the real (now failing) unit test
+     * }
+     * </pre>
+     * Idea copied from HtmlUnit (many thanks to Marc Guillemot).
+     * Future versions maybe available in the JUnit distro.
+     * The purpose of providing a 'static' version is such that you can use the
+     * feature even if not subclassing GroovyTestCase.
+     * @return <false> when not itself already in the call stack
+     */
+    public static boolean notYetImplemented(TestCase caller) {
+        if (notYetImplementedFlag.get() != null) {
+            return false;
+        }
+        notYetImplementedFlag.set(Boolean.TRUE);
+
+        final Method testMethod = findRunningJUnitTestMethod(caller.getClass());
+        try {
+            log.info("Running " + testMethod.getName() + " as not yet implemented");
+            testMethod.invoke(caller, new Class[] {});
+            fail(testMethod.getName() + " is marked as not yet implemented but passes unexpectedly");
+        }
+        catch (final Exception e) {
+            log.info(testMethod.getName() + " fails which is expected as it is not yet implemented");
+            // method execution failed, it is really "not yet implemented"
+        }
+        finally {
+            notYetImplementedFlag.set(null);
+        }
+        return true;
+    }
+
+    /**
+     * Convenience method for subclasses of GroovyTestCase, identical to
+     * <pre> GroovyTestCase.notYetImplemented(this); </pre>.
+     * @see #notYetImplemented(junit.framework.TestCase)
+     * @return  <false> when not itself already in the call stack
+     */
+    public boolean notYetImplemented() {
+        return notYetImplemented(this);
+    }
+
+    /**
+     * From JUnit. Finds from the call stack the active running JUnit test case
+     * @return the test case method
+     * @throws RuntimeException if no method could be found.
+     */
+    private static Method findRunningJUnitTestMethod(Class caller) {
+        final Class[] args = new Class[] {};
+
+        // search the inial junit test
+        final Throwable t = new Exception();
+        for (int i=t.getStackTrace().length-1; i>=0; --i) {
+            final StackTraceElement element = t.getStackTrace()[i];
+            if (element.getClassName().equals(caller.getName())) {
+                try {
+                    final Method m = caller.getMethod(element.getMethodName(), args);
+                    if (isPublicTestMethod(m)) {
+                        return m;
+                    }
+                }
+                catch (final Exception e) {
+                    // can't access, ignore it
+                }
+            }
+        }
+        throw new RuntimeException("No JUnit test case method found in call stack");
+    }
+
+
+    /**
+     * From Junit. Test if the method is a junit test.
+     * @param method the method
+     * @return <code>true</code> if this is a junit test.
+     */
+    private static boolean isPublicTestMethod(final Method method) {
+        final String name = method.getName();
+        final Class[] parameters = method.getParameterTypes();
+        final Class returnType = method.getReturnType();
+
+        return parameters.length == 0 && name.startsWith("test")
+            && returnType.equals(Void.TYPE)
+            && Modifier.isPublic(method.getModifiers());
+    }
+
+    public static void assertEquals(String message, Object expected, Object actual) {
+        if (expected == null && actual == null)
+			return;
+		if (expected != null && DefaultTypeTransformation.compareEqual(expected, actual))
+			return;
+		failNotEquals(message, expected, actual);
+    }
+
+    public static void assertEquals(Object expected, Object actual) {
+	    assertEquals(null, expected, actual);
+	}
+
+	public static void assertEquals(String expected, String actual) {
+	    assertEquals(null, expected, actual);
+	}
+
+    private static final ThreadLocal notYetImplementedFlag = new ThreadLocal();
+
+    private Throwable unwrap (GroovyRuntimeException gre) {
+        Throwable th = gre;
+        if (th.getCause() != null && th.getCause() != gre) th = th.getCause();
+        if (th != gre && (th instanceof GroovyRuntimeException)) return unwrap((GroovyRuntimeException) th);
+        return th;
+    }
+}
diff --git a/groovy/src/main/groovy/util/GroovyTestSuite.java b/groovy/src/main/groovy/util/GroovyTestSuite.java
new file mode 100644
index 0000000..711a1ac
--- /dev/null
+++ b/groovy/src/main/groovy/util/GroovyTestSuite.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.Script;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import org.codehaus.groovy.runtime.ScriptTestAdapter;
+
+import java.io.File;
+
+
+/**
+ * A TestSuite which will run a Groovy unit test case inside any Java IDE
+ * either as a unit test case or as an application.
+ * <p/>
+ * You can specify the GroovyUnitTest to run by running this class as an appplication
+ * and specifying the script to run on the command line.
+ * <p/>
+ * <code>
+ * java groovy.util.GroovyTestSuite src/test/Foo.groovy
+ * </code>
+ * <p/>
+ * Or to run the test suite as a unit test suite in an IDE you can use
+ * the 'test' system property to define the test script to run.
+ * e.g. pass this into the JVM when the unit test plugin runs...
+ * <p/>
+ * <code>
+ * -Dtest=src/test/Foo.groovy
+ * </code>
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class GroovyTestSuite extends TestSuite {
+
+    protected static String file = null;
+
+    protected final GroovyClassLoader loader = new GroovyClassLoader(GroovyTestSuite.class.getClassLoader());
+
+    public static void main(String[] args) {
+        if (args.length > 0) {
+            file = args[0];
+        }
+        TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        GroovyTestSuite suite = new GroovyTestSuite();
+        try {
+            suite.loadTestSuite();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not create the test suite: " + e, e);
+        }
+        return suite;
+    }
+
+    public void loadTestSuite() throws Exception {
+        String fileName = System.getProperty("test", file);
+        if (fileName == null) {
+            throw new RuntimeException("No filename given in the 'test' system property so cannot run a Groovy unit test");
+        }
+        System.out.println("Compiling: " + fileName);
+        Class type = compile(fileName);
+        String[] args = {};
+        if (!Test.class.isAssignableFrom(type) && Script.class.isAssignableFrom(type)) {
+            // lets treat the script as a Test
+            addTest(new ScriptTestAdapter(type, args));
+        } else {
+            addTestSuite(type);
+        }
+    }
+
+    public Class compile(String fileName) throws Exception {
+        return loader.parseClass(new File(fileName));
+    }
+}
diff --git a/groovy/src/main/groovy/util/IFileNameFinder.java b/groovy/src/main/groovy/util/IFileNameFinder.java
new file mode 100644
index 0000000..74cda3c
--- /dev/null
+++ b/groovy/src/main/groovy/util/IFileNameFinder.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util;
+
+import java.util.List;
+
+public interface IFileNameFinder {
+    List getFileNames(String basedir, String pattern);
+    List getFileNames(String basedir, String pattern, String excludesPattern);
+}
diff --git a/groovy/src/main/groovy/util/IndentPrinter.java b/groovy/src/main/groovy/util/IndentPrinter.java
new file mode 100644
index 0000000..7750a8b
--- /dev/null
+++ b/groovy/src/main/groovy/util/IndentPrinter.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import java.io.PrintWriter;
+
+/**
+ * A helper class for printing indented text
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class IndentPrinter {
+
+    private int indentLevel;
+    private String indent;
+    private PrintWriter out;
+
+    public IndentPrinter() {
+        this(new PrintWriter(System.out), "  ");
+    }
+
+    public IndentPrinter(PrintWriter out) {
+        this(out, "  ");
+    }
+
+    public IndentPrinter(PrintWriter out, String indent) {
+        if (out == null) {
+            /** @todo temporary hack */
+            out = new PrintWriter(System.out);
+            //throw new IllegalArgumentException("Must specify a PrintWriter");
+        }
+        this.out = out;
+        this.indent = indent;
+    }
+
+    public void println(String text) {
+        out.print(text);
+        out.println();
+    }
+
+    public void print(String text) {
+        out.print(text);
+    }
+
+    public void printIndent() {
+        for (int i = 0; i < indentLevel; i++) {
+            out.print(indent);
+        }
+    }
+
+    public void println() {
+        out.println();
+    }
+
+    public void incrementIndent() {
+        ++indentLevel;
+    }
+
+    public void decrementIndent() {
+        --indentLevel;
+    }
+
+    public int getIndentLevel() {
+        return indentLevel;
+    }
+
+    public void setIndentLevel(int indentLevel) {
+        this.indentLevel = indentLevel;
+    }
+
+    public void flush() {
+        out.flush();
+    }
+}
diff --git a/groovy/src/main/groovy/util/MapEntry.java b/groovy/src/main/groovy/util/MapEntry.java
new file mode 100644
index 0000000..b44a060
--- /dev/null
+++ b/groovy/src/main/groovy/util/MapEntry.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import java.util.Map;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+/**
+ * Represents a list of Integer objects key a specified Object up to but not including
+ * a given and to.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MapEntry implements Map.Entry {
+
+    private Object key;
+    private Object value;
+
+    public MapEntry(Object key, Object value) {
+        this.key = key;
+        this.value = value;
+    }
+
+    public boolean equals(Object that) {
+        if (that instanceof MapEntry) {
+            return equals((MapEntry) that);
+        }
+        return false;
+    }
+
+    public boolean equals(MapEntry that) {
+        return DefaultTypeTransformation.compareEqual(this.key, that.key) && DefaultTypeTransformation.compareEqual(this.value, that.value);
+    }
+
+    public int hashCode() {
+        return hash(key) ^ hash(value);
+    }
+
+    public String toString() {
+        return "" + key + ":" + value;
+    }
+
+    public Object getKey() {
+        return key;
+    }
+
+    public void setKey(Object key) {
+        this.key = key;
+    }
+
+    public Object getValue() {
+        return value;
+    }
+
+    public Object setValue(Object value) {
+        this.value = value;
+        return value;
+    }
+
+    /**
+     * Helper method to handle object hashes for possibly null values
+     */
+    protected int hash(Object object) {
+        return (object == null) ? 0xbabe : object.hashCode();
+    }
+
+}
diff --git a/groovy/src/main/groovy/util/Node.java b/groovy/src/main/groovy/util/Node.java
new file mode 100644
index 0000000..69a3870
--- /dev/null
+++ b/groovy/src/main/groovy/util/Node.java
@@ -0,0 +1,363 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.DelegatingMetaClass;
+import groovy.lang.GroovySystem;
+import groovy.lang.MetaClass;
+import groovy.xml.QName;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.io.PrintWriter;
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * Represents an arbitrary tree node which can be used for structured metadata or any arbitrary XML-like tree.
+ * A node can have a name, a value and an optional Map of attributes.
+ * Typically the name is a String and a value is either a String or a List of other Nodes,
+ * though the types are extensible to provide a flexible structure, e.g. you could use a
+ * QName as the name which includes a namespace URI and a local name. Or a JMX ObjectName etc.
+ * So this class can represent metadata like {foo a=1 b="abc"} or nested metadata like {foo a=1 b="123" { bar x=12 text="hello" }}
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Paul King
+ * @version $Revision$
+ */
+public class Node implements Serializable {
+
+    static {
+        // wrap the standard MetaClass with the delegate
+        setMetaClass(GroovySystem.getMetaClassRegistry().getMetaClass(Node.class));
+    }
+
+    private static final long serialVersionUID = 4121134753270542643L;
+    private Node parent;
+    private Object name;
+    private Map attributes;
+    private Object value;
+
+    public Node(Node parent, Object name) {
+        this(parent, name, new NodeList());
+    }
+
+    public Node(Node parent, Object name, Object value) {
+        this(parent, name, new HashMap(), value);
+    }
+
+    public Node(Node parent, Object name, Map attributes) {
+        this(parent, name, attributes, new NodeList());
+    }
+
+    public Node(Node parent, Object name, Map attributes, Object value) {
+        this.parent = parent;
+        this.name = name;
+        this.attributes = attributes;
+        this.value = value;
+
+        if (parent != null) {
+            Object parentValue = parent.value();
+            List parentList;
+            if (parentValue instanceof List) {
+                parentList = (List) parentValue;
+            } else {
+                parentList = new NodeList();
+                parentList.add(parentValue);
+                parent.setValue(parentList);
+            }
+            parentList.add(this);
+        }
+    }
+
+    public Node appendNode(Object name, Map attributes) {
+        return new Node(this, name, attributes);
+    }
+
+    public Node appendNode(Object name, Object value) {
+        return new Node(this, name, value);
+    }
+
+    public Node appendNode(Object name, Map attributes, Object value) {
+        return new Node(this, name, attributes, value);
+    }
+
+    private static void setMetaClass(final MetaClass metaClass) {
+        final MetaClass newMetaClass = new DelegatingMetaClass(metaClass) {
+            /* (non-Javadoc)
+            * @see groovy.lang.DelegatingMetaClass#getAttribute(java.lang.Object, java.lang.String)
+            */
+            public Object getAttribute(final Object object, final String attribute) {
+                Node n = (Node) object;
+                return n.get("@" + attribute);
+            }
+
+            /* (non-Javadoc)
+             * @see groovy.lang.MetaClass#setAttribute(java.lang.Object, java.lang.String, java.lang.Object)
+             */
+            public void setAttribute(final Object object, final String attribute, final Object newValue) {
+                Node n = (Node) object;
+                n.attributes().put(attribute, newValue);
+            }
+
+            /* (non-Javadoc)
+            * @see groovy.lang.MetaClass#getProperty(java.lang.Object, java.lang.String)
+            */
+            public Object getProperty(Object object, String property) {
+                if (object instanceof Node) {
+                    Node n = (Node) object;
+                    return n.get(property);
+                }
+                return super.getProperty(object, property);
+            }
+
+            /* (non-Javadoc)
+             * @see groovy.lang.MetaClass#setProperty(java.lang.Object, java.lang.String, java.lang.Object)
+             */
+            public void setProperty(Object object, String property, Object newValue) {
+                if (property.startsWith("@")) {
+                    String attribute = property.substring(1);
+                    Node n = (Node) object;
+                    n.attributes().put(attribute, newValue);
+                    return;
+                }
+                delegate.setProperty(object, property, newValue);
+            }
+
+        };
+        GroovySystem.getMetaClassRegistry().setMetaClass(Node.class, newMetaClass);
+    }
+
+    public String text() {
+        if (value instanceof String) {
+            return (String) value;
+        } else if (value instanceof Collection) {
+            Collection coll = (Collection) value;
+            String previousText = null;
+            StringBuffer buffer = null;
+            for (Iterator iter = coll.iterator(); iter.hasNext();) {
+                Object child = iter.next();
+                if (child instanceof String) {
+                    String childText = (String) child;
+                    if (previousText == null) {
+                        previousText = childText;
+                    } else {
+                        if (buffer == null) {
+                            buffer = new StringBuffer();
+                            buffer.append(previousText);
+                        }
+                        buffer.append(childText);
+                    }
+                }
+            }
+            if (buffer != null) {
+                return buffer.toString();
+            } else {
+                if (previousText != null) {
+                    return previousText;
+                }
+            }
+        }
+        return "";
+    }
+
+
+    public Iterator iterator() {
+        return children().iterator();
+    }
+
+    public List children() {
+        if (value == null) {
+            return new NodeList();
+        }
+        if (value instanceof List) {
+            return (List) value;
+        }
+        // we're probably just a String
+        List result = new NodeList();
+        result.add(value);
+        return result;
+    }
+
+    public Map attributes() {
+        return attributes;
+    }
+
+    public Object attribute(Object key) {
+        return (attributes != null) ? attributes.get(key) : null;
+    }
+
+    public Object name() {
+        return name;
+    }
+
+    public Object value() {
+        return value;
+    }
+
+    public void setValue(Object value) {
+        this.value = value;
+    }
+
+    public Node parent() {
+        return parent;
+    }
+
+    /**
+     * Provides lookup of elements by non-namespaced name
+     *
+     * @param key the name (or shortcut key) of the node(s) of interest
+     * @return the nodes which match key
+     */
+    public Object get(String key) {
+        if (key != null && key.charAt(0) == '@') {
+            String attributeName = key.substring(1);
+            return attributes().get(attributeName);
+        }
+        if ("..".equals(key)) {
+            return parent();
+        }
+        if ("*".equals(key)) {
+            return children();
+        }
+        if ("**".equals(key)) {
+            return depthFirst();
+        }
+        return getByName(key);
+    }
+
+    /**
+     * Provides lookup of elements by QName.
+     *
+     * @param name the QName of interest
+     * @return the nodes matching name
+     */
+    public NodeList getAt(QName name) {
+        NodeList answer = new NodeList();
+        for (Iterator iter = children().iterator(); iter.hasNext();) {
+            Object child = iter.next();
+            if (child instanceof Node) {
+                Node childNode = (Node) child;
+                Object childNodeName = childNode.name();
+                if (name.matches(childNodeName)) {
+                    answer.add(childNode);
+                }
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Provides lookup of elements by name.
+     *
+     * @param name the name of interest
+     * @return the nodes matching name
+     */
+    private NodeList getByName(String name) {
+        NodeList answer = new NodeList();
+        for (Iterator iter = children().iterator(); iter.hasNext();) {
+            Object child = iter.next();
+            if (child instanceof Node) {
+                Node childNode = (Node) child;
+                Object childNodeName = childNode.name();
+                if (childNodeName instanceof QName) {
+                    QName qn = (QName) childNodeName;
+                    if (qn.matches(name)) {
+                        answer.add(childNode);
+                    }
+                } else if (name.equals(childNodeName)) {
+                    answer.add(childNode);
+                }
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Provide a collection of all the nodes in the tree
+     * using a depth first traversal.
+     *
+     * @return the list of (depth-first) ordered nodes
+     */
+    public List depthFirst() {
+        List answer = new NodeList();
+        answer.add(this);
+        answer.addAll(depthFirstRest());
+        return answer;
+    }
+
+    private List depthFirstRest() {
+        List answer = new NodeList();
+        for (Iterator iter = InvokerHelper.asIterator(value); iter.hasNext();) {
+            Object child = iter.next();
+            if (child instanceof Node) {
+                Node childNode = (Node) child;
+                List children = childNode.depthFirstRest();
+                answer.add(childNode);
+                answer.addAll(children);
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Provide a collection of all the nodes in the tree
+     * using a breadth-first traversal.
+     *
+     * @return the list of (breadth-first) ordered nodes
+     */
+    public List breadthFirst() {
+        List answer = new NodeList();
+        answer.add(this);
+        answer.addAll(breadthFirstRest());
+        return answer;
+    }
+
+    private List breadthFirstRest() {
+        List answer = new NodeList();
+        List nextLevelChildren = getDirectChildren();
+        while (!nextLevelChildren.isEmpty()) {
+            List working = new NodeList(nextLevelChildren);
+            nextLevelChildren = new NodeList();
+            for (Iterator iter = working.iterator(); iter.hasNext();) {
+                Node childNode = (Node) iter.next();
+                answer.add(childNode);
+                List children = childNode.getDirectChildren();
+                nextLevelChildren.addAll(children);
+            }
+        }
+        return answer;
+    }
+
+    private List getDirectChildren() {
+        List answer = new NodeList();
+        for (Iterator iter = InvokerHelper.asIterator(value); iter.hasNext();) {
+            Object child = iter.next();
+            if (child instanceof Node) {
+                Node childNode = (Node) child;
+                answer.add(childNode);
+            }
+        }
+        return answer;
+    }
+
+    public String toString() {
+        return name + "[attributes=" + attributes + "; value=" + value + "]";
+    }
+
+    public void print(PrintWriter out) {
+        new NodePrinter(out).print(this);
+    }
+}
diff --git a/groovy/src/main/groovy/util/NodeBuilder.java b/groovy/src/main/groovy/util/NodeBuilder.java
new file mode 100644
index 0000000..579eb6e
--- /dev/null
+++ b/groovy/src/main/groovy/util/NodeBuilder.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import java.util.ArrayList;
+import java.util.Map;
+
+/**
+ * A helper class for creating nested trees of Node objects for 
+ * handling arbitrary data
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class NodeBuilder extends BuilderSupport {
+
+    public static NodeBuilder newInstance() {
+        return new NodeBuilder();
+    }
+
+    protected void setParent(Object parent, Object child) {
+    }
+
+    protected Object createNode(Object name) {
+        return new Node(getCurrentNode(), name, new ArrayList());
+    }
+
+    protected Object createNode(Object name, Object value) {
+        return new Node(getCurrentNode(), name, value);
+    }
+
+    protected Object createNode(Object name, Map attributes) {
+        return new Node(getCurrentNode(), name, attributes, new ArrayList());
+    }
+
+    protected Object createNode(Object name, Map attributes, Object value) {
+        return new Node(getCurrentNode(), name, attributes, value);
+    }
+
+    protected Node getCurrentNode() {
+        return (Node) getCurrent();
+    }
+}
diff --git a/groovy/src/main/groovy/util/NodeList.java b/groovy/src/main/groovy/util/NodeList.java
new file mode 100644
index 0000000..a3db49b
--- /dev/null
+++ b/groovy/src/main/groovy/util/NodeList.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.DelegatingMetaClass;
+import groovy.lang.GroovySystem;
+import groovy.lang.MetaClass;
+import groovy.xml.QName;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A List implementation which is returned by queries on a {@link Node}
+ * which provides some XPath like helper methods for GPath.
+ */
+public class NodeList extends ArrayList {
+
+    static {
+        // wrap the standard MetaClass with the delegate
+        setMetaClass(GroovySystem.getMetaClassRegistry().getMetaClass(NodeList.class));
+    }
+
+    public NodeList() {
+    }
+
+    public NodeList(Collection collection) {
+        super(collection);
+    }
+
+    public NodeList(int size) {
+        super(size);
+    }
+
+    private static void setMetaClass(final MetaClass metaClass) {
+        final MetaClass newMetaClass = new DelegatingMetaClass(metaClass) {
+            /* (non-Javadoc)
+            * @see groovy.lang.DelegatingMetaClass#getAttribute(java.lang.Object, java.lang.String)
+            */
+            public Object getAttribute(final Object object, final String attribute) {
+                NodeList nl = (NodeList) object;
+                Iterator it = nl.iterator();
+                List result = new ArrayList();
+                while (it.hasNext()) {
+                    Node node = (Node) it.next();
+                    result.add(node.attributes().get(attribute));
+                }
+                return result;
+            }
+
+            public void setAttribute(final Object object, final String attribute, final Object newValue) {
+                NodeList nl = (NodeList) object;
+                Iterator it = nl.iterator();
+                while (it.hasNext()) {
+                    Node node = (Node) it.next();
+                    node.attributes().put(attribute, newValue);
+                }
+            }
+
+            /* (non-Javadoc)
+            * @see groovy.lang.MetaClass#getProperty(java.lang.Object, java.lang.String)
+            */
+            public Object getProperty(Object object, String property) {
+                if (object instanceof NodeList) {
+                    NodeList nl = (NodeList) object;
+                    return nl.getAt(property);
+                }
+                return super.getProperty(object, property);
+            }
+        };
+        GroovySystem.getMetaClassRegistry().setMetaClass(NodeList.class, newMetaClass);
+    }
+
+    /**
+     * Provides lookup of elements by non-namespaced name.
+     *
+     * @param name the name or shortcut key for nodes of interest
+     * @return the nodes of interest which match name
+     */
+    public NodeList getAt(String name) {
+        NodeList answer = new NodeList();
+        for (Iterator iter = iterator(); iter.hasNext();) {
+            Object child = iter.next();
+            if (child instanceof Node) {
+                Node childNode = (Node) child;
+                Object temp = childNode.get(name);
+                if (temp instanceof Collection) {
+                    answer.addAll((Collection) temp);
+                } else {
+                    answer.add(temp);
+                }
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Provides lookup of elements by QName.
+     *
+     * @param name the name or shortcut key for nodes of interest
+     * @return the nodes of interest which match name
+     */
+    public NodeList getAt(QName name) {
+        NodeList answer = new NodeList();
+        for (Iterator iter = iterator(); iter.hasNext();) {
+            Object child = iter.next();
+            if (child instanceof Node) {
+                Node childNode = (Node) child;
+                NodeList temp = childNode.getAt(name);
+                answer.addAll(temp);
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Returns the text value of all of the elements in the collection.
+     *
+     * @return the text value of all the elements in the collection or null
+     */
+    public String text() {
+        String previousText = null;
+        StringBuffer buffer = null;
+        for (Iterator iter = this.iterator(); iter.hasNext();) {
+            Object child = iter.next();
+            String text = null;
+            if (child instanceof String) {
+                text = (String) child;
+            } else if (child instanceof Node) {
+                text = ((Node) child).text();
+            }
+            if (text != null) {
+                if (previousText == null) {
+                    previousText = text;
+                } else {
+                    if (buffer == null) {
+                        buffer = new StringBuffer();
+                        buffer.append(previousText);
+                    }
+                    buffer.append(text);
+                }
+            }
+        }
+        if (buffer != null) {
+            return buffer.toString();
+        }
+        if (previousText != null) {
+            return previousText;
+        }
+        return "";
+    }
+}
diff --git a/groovy/src/main/groovy/util/NodePrinter.java b/groovy/src/main/groovy/util/NodePrinter.java
new file mode 100644
index 0000000..ae179c0
--- /dev/null
+++ b/groovy/src/main/groovy/util/NodePrinter.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A helper class for creating nested trees of data
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Christian Stein
+ * @version $Revision$
+ */
+public class NodePrinter {
+
+    protected final IndentPrinter out;
+
+    public NodePrinter() {
+        this(new IndentPrinter(new PrintWriter(new OutputStreamWriter(System.out))));
+    }
+
+    public NodePrinter(PrintWriter out) {
+        this(new IndentPrinter(out));
+    }
+
+    public NodePrinter(IndentPrinter out) {
+        if (out == null) {
+            throw new NullPointerException("IndentPrinter 'out' must not be null!");
+        }
+        this.out = out;
+    }
+
+    public void print(Node node) {
+        out.printIndent();
+        printName(node);
+        Map attributes = node.attributes();
+        boolean hasAttributes = attributes != null && !attributes.isEmpty();
+        if (hasAttributes) {
+            printAttributes(attributes);
+        }
+        Object value = node.value();
+        if (value instanceof List) {
+            if (!hasAttributes) {
+                out.print("()");
+            }
+            printList((List) value);
+        }
+        else {
+            if (value instanceof String) {
+                out.print("('");
+                out.print((String) value);
+                out.println("')");
+            }
+            else {
+                out.println("()");
+            }
+        }
+        out.flush();
+    }
+
+    protected void printName(Node node) {
+        Object name = node.name();
+        if (name != null) {
+            out.print(name.toString());
+        }
+        else {
+            out.print("null");
+        }
+    }
+
+    protected void printList(List list) {
+        if (list.isEmpty()) {
+            out.println("");
+        }
+        else {
+            out.println(" {");
+            out.incrementIndent();
+            for (Iterator iter = list.iterator(); iter.hasNext();) {
+                Object value = iter.next();
+                if (value instanceof Node) {
+                    print((Node) value);
+                }
+                else {
+                    out.printIndent();
+                    out.print("builder.append(");
+                    out.print(InvokerHelper.toString(value));
+                    out.println(")");
+                }
+            }
+            out.decrementIndent();
+            out.printIndent();
+            out.println("}");
+        }
+    }
+
+
+    protected void printAttributes(Map attributes) {
+        out.print("(");
+        boolean first = true;
+        for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            if (first) {
+                first = false;
+            }
+            else {
+                out.print(", ");
+            }
+            out.print(entry.getKey().toString());
+            out.print(":");
+            if (entry.getValue() instanceof String) {
+                out.print("'" + entry.getValue() + "'");
+            }
+            else {
+                out.print(InvokerHelper.toString(entry.getValue()));
+            }
+        }
+        out.print(")");
+    }
+
+}
diff --git a/groovy/src/main/groovy/util/ObjectGraphBuilder.java b/groovy/src/main/groovy/util/ObjectGraphBuilder.java
new file mode 100644
index 0000000..deb0581
--- /dev/null
+++ b/groovy/src/main/groovy/util/ObjectGraphBuilder.java
@@ -0,0 +1,543 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.util;

+

+import groovy.lang.Closure;

+import groovy.lang.MetaProperty;

+

+import java.util.Collection;

+import java.util.HashMap;

+import java.util.Map;

+

+import org.codehaus.groovy.runtime.InvokerHelper;

+

+/**

+ * A builder for creating object graphs.<br>

+ * Each node defines the class to be created and the property on its parent (if

+ * any) at the same time.

+ *

+ * @author Scott Vlaminck (http://refactr.com)

+ * @author Andres Almiray <aalmiray@users.sourceforge.com>

+ */

+public class ObjectGraphBuilder extends FactoryBuilderSupport {

+    public static final String NODE_CLASS = "_NODE_CLASS_";

+    public static final String NODE_NAME = "_NODE_NAME_";

+    public static final String OBJECT_ID = "_OBJECT_ID_";

+

+    private ChildPropertySetter childPropertySetter;

+    private ClassNameResolver classNameResolver;

+    private IdentifierResolver identifierResolver;

+    private NewInstanceResolver newInstanceResolver;

+    private ObjectFactory objectFactory = new ObjectFactory();

+    private ObjectRefFactory objectRefFactory = new ObjectRefFactory();

+    private ReferenceResolver referenceResolver;

+    private RelationNameResolver relationNameResolver;

+    private Map/* <String,Class> */resolvedClasses = new HashMap/* <String,Class> */();

+    private ClassLoader classLoader;

+

+    public ObjectGraphBuilder() {

+        classNameResolver = new DefaultClassNameResolver();

+        newInstanceResolver = new DefaultNewInstanceResolver();

+        relationNameResolver = new DefaultRelationNameResolver();

+        childPropertySetter = new DefaultChildPropertySetter();

+        identifierResolver = new DefaultIdentifierResolver();

+        referenceResolver = new DefaultReferenceResolver();

+    }

+

+    /**

+     * Returns the current ChildPropertySetter.

+     */

+    public ChildPropertySetter getChildPropertySetter() {

+        return childPropertySetter;

+    }

+

+    /**

+     * Returns the classLoader used to load a node's class.

+     */

+    public ClassLoader getClassLoader() {

+    	return classLoader;

+    }

+    

+    /**

+     * Returns the current ClassNameResolver.

+     */

+    public ClassNameResolver getClassNameResolver() {

+        return classNameResolver;

+    }

+

+    /**

+     * Returns the current NewInstanceResolver.

+     */

+    public NewInstanceResolver getNewInstanceResolver() {

+        return newInstanceResolver;

+    }

+

+    /**

+     * Returns the current RelationNameResolver.

+     */

+    public RelationNameResolver getRelationNameResolver() {

+        return relationNameResolver;

+    }

+

+    /**

+     * Sets the current ChildPropertySetter.<br>

+     * It will assign DefaultChildPropertySetter if null.<br>

+     * It accepts a ChildPropertySetter instance or a Closure.

+     */

+    public void setChildPropertySetter( final Object childPropertySetter ) {

+        if( childPropertySetter instanceof ChildPropertySetter ){

+            this.childPropertySetter = (ChildPropertySetter) childPropertySetter;

+        }else if( childPropertySetter instanceof Closure ){

+            this.childPropertySetter = new ChildPropertySetter(){

+                public void setChild( Object parent, Object child, String parentName,

+                        String propertyName ) {

+                    ((Closure) childPropertySetter).call( new Object[] { parent, child, parentName,

+                            propertyName } );

+                }

+            };

+        }else{

+            this.childPropertySetter = new DefaultChildPropertySetter();

+        }

+    }

+    

+    /**

+     * Sets the classLoader used to load a node's class.

+     */

+    public void setClassLoader( ClassLoader classLoader ){

+    	this.classLoader = classLoader;

+    }

+

+    /**

+     * Sets the current ClassNameResolver.<br>

+     * It will assign DefaultClassNameResolver if null.<br>

+     * It accepts a ClassNameResolver instance, a String or a Closure.

+     */

+    public void setClassNameResolver( final Object classNameResolver ) {

+        if( classNameResolver instanceof ClassNameResolver ){

+            this.classNameResolver = (ClassNameResolver) classNameResolver;

+        }else if( classNameResolver instanceof String ){

+            this.classNameResolver = new ClassNameResolver(){

+                public String resolveClassname( String classname ) {

+                    return classNameResolver + "." + classname.substring( 0, 1 )

+                            .toUpperCase() + classname.substring( 1 );

+                }

+            };

+        }else if( classNameResolver instanceof Closure ){

+            this.classNameResolver = new ClassNameResolver(){

+                public String resolveClassname( String classname ) {

+                    return (String) ((Closure) classNameResolver).call( new Object[] { classname } );

+                }

+            };

+        }else{

+            this.classNameResolver = new DefaultClassNameResolver();

+        }

+    }

+

+    /**

+     * Sets the current IdentifierResolver.<br>

+     * It will assign DefaultIdentifierResolver if null.<br>

+     * It accepts a IdentifierResolver instance or a Closure.

+     */

+    public void setIdentifierResolver( final Object identifierResolver ) {

+        if( identifierResolver instanceof IdentifierResolver ){

+            this.identifierResolver = (IdentifierResolver) identifierResolver;

+        }else if( identifierResolver instanceof Closure ){

+            this.identifierResolver = new IdentifierResolver(){

+                public String getIdentifierFor( String nodeName ) {

+                    return (String) (((Closure) identifierResolver).call( new Object[] { nodeName } ));

+                }

+            };

+        }else{

+            this.identifierResolver = new DefaultIdentifierResolver();

+        }

+    }

+

+    /**

+     * Sets the current NewInstanceResolver.<br>

+     * It will assign DefaultNewInstanceResolver if null.<br>

+     * It accepts a NewInstanceResolver instance or a Closure.

+     */

+    public void setNewInstanceResolver( final Object newInstanceResolver ) {

+        if( newInstanceResolver instanceof NewInstanceResolver ){

+            this.newInstanceResolver = (NewInstanceResolver) newInstanceResolver;

+        }else if( newInstanceResolver instanceof Closure ){

+            this.newInstanceResolver = new NewInstanceResolver(){

+                public Object newInstance( Class klass, Map attributes )

+                        throws InstantiationException, IllegalAccessException {

+                    return ((Closure) newInstanceResolver).call( new Object[] { klass, attributes } );

+                }

+            };

+        }else{

+            this.newInstanceResolver = new DefaultNewInstanceResolver();

+        }

+    }

+

+    /**

+     * Sets the current ReferenceResolver.<br>

+     * It will assign DefaultReferenceResolver if null.<br>

+     * It accepts a ReferenceResolver instance or a Closure.

+     */

+    public void setReferenceResolver( final Object referenceResolver ) {

+        if( referenceResolver instanceof ReferenceResolver ){

+            this.referenceResolver = (ReferenceResolver) referenceResolver;

+        }else if( referenceResolver instanceof Closure ){

+            this.referenceResolver = new ReferenceResolver(){

+                public String getReferenceFor( String nodeName ) {

+                    return (String) (((Closure) referenceResolver).call( new Object[] { nodeName } ));

+                }

+            };

+        }else{

+            this.referenceResolver = new DefaultReferenceResolver();

+        }

+    }

+

+    /**

+     * Sets the current RelationNameResolver.<br>

+     * It will assign DefaultRelationNameResolver if null.

+     */

+    public void setRelationNameResolver( RelationNameResolver relationNameResolver ) {

+        this.relationNameResolver = relationNameResolver != null ? relationNameResolver

+                : new DefaultRelationNameResolver();

+    }

+

+    protected void postInstantiate( Object name, Map attributes, Object node ) {

+        super.postInstantiate( name, attributes, node );

+        Map context = getContext();

+        String objectId = (String) context.get( OBJECT_ID );

+        if( objectId != null && node != null ){

+            setVariable( objectId, node );

+        }

+    }

+

+    protected void preInstantiate( Object name, Map attributes, Object value ) {

+        super.preInstantiate( name, attributes, value );

+        Map context = getContext();

+        context.put( OBJECT_ID,

+                attributes.remove( identifierResolver.getIdentifierFor( (String) name ) ) );

+    }

+

+    protected Factory resolveFactory( Object name, Map attributes, Object value ) {

+        // let custom factories to be resolved first

+        Factory factory = super.resolveFactory( name, attributes, value );

+        if( factory != null ){

+            return factory;

+        }

+        if( attributes.get( referenceResolver.getReferenceFor( (String) name ) ) != null ){

+            return objectRefFactory;

+        }

+        return objectFactory;

+    }

+

+    /**

+     * Strategy for setting a child node on its parent.<br>

+     * Useful for handling Lists/Arrays vs normal properties.

+     */

+    public interface ChildPropertySetter {

+        /**

+         * @param parent the parent's node value

+         * @param child the child's node value

+         * @param parentName the name of the parent node

+         * @param propertyName the resolved relation name of the child

+         */

+        void setChild( Object parent, Object child, String parentName, String propertyName );

+    }

+

+    /**

+     * Strategy for resolving a classname.

+     */

+    public interface ClassNameResolver {

+        /**

+         * @param classname the node name as written on the building code

+         */

+        String resolveClassname( String classname );

+    }

+

+    /**

+     * Default impl that calls parent.propertyName = child<br>

+     * If parent.propertyName is a Collection it will try to add child to the

+     * collection.

+     */

+    public static class DefaultChildPropertySetter implements ChildPropertySetter {

+        public void setChild( Object parent, Object child, String parentName, String propertyName ) {

+            Object property = InvokerHelper.getProperty( parent, propertyName );

+            if( property != null && Collection.class.isAssignableFrom( property.getClass() ) ){

+                ((Collection) property).add( child );

+            }else{

+                InvokerHelper.setProperty( parent, propertyName, child );

+            }

+        }

+    }

+

+    /**

+     * Default impl that capitalizes the classname.

+     */

+    public static class DefaultClassNameResolver implements ClassNameResolver {

+        public String resolveClassname( String classname ) {

+            if( classname.length() == 1 ){

+                return classname.toUpperCase();

+            }

+            return classname.substring( 0, 1 )

+                    .toUpperCase() + classname.substring( 1 );

+        }

+    }

+

+    /**

+     * Default impl, always returns 'id'

+     */

+    public static class DefaultIdentifierResolver implements IdentifierResolver {

+        public String getIdentifierFor( String nodeName ) {

+            return "id";

+        }

+    }

+

+    /**

+     * Default impl that calls Class.newInstance()

+     */

+    public static class DefaultNewInstanceResolver implements NewInstanceResolver {

+        public Object newInstance( Class klass, Map attributes ) throws InstantiationException,

+                IllegalAccessException {

+            return klass.newInstance();

+        }

+    }

+

+    /**

+     * Default impl, always returns 'refId'

+     */

+    public static class DefaultReferenceResolver implements ReferenceResolver {

+        public String getReferenceFor( String nodeName ) {

+            return "refId";

+        }

+    }

+

+    /**

+     * Default impl that returns parentName &amp; childName accordingly.

+     */

+    public static class DefaultRelationNameResolver implements RelationNameResolver {

+        /**

+         * Follow the most conventional plural in English, add 's' to childName.<br>

+         * If the property does not exist then it will return childName

+         * unchanged.

+         */

+        public String resolveChildRelationName( String parentName, Object parent, String childName,

+                Object child ) {

+            MetaProperty metaProperty = InvokerHelper.getMetaClass( parent )

+                    .hasProperty( parent, childName + "s" );

+            if( metaProperty != null ){

+                return childName + "s";

+            }

+            return childName;

+        }

+

+        /**

+         * Follow the most conventional pattern, returns the parentName

+         * unchanged.

+         */

+        public String resolveParentRelationName( String parentName, Object parent,

+                String childName, Object child ) {

+            return parentName;

+        }

+    }

+

+    /**

+     * Strategy for picking the correct synthetic identifier.

+     */

+    public interface IdentifierResolver {

+        /**

+         * Returns the name of the property that will identify the node.<br>

+         *

+         * @param nodeName the name of the node

+         */

+        String getIdentifierFor( String nodeName );

+    }

+

+    /**

+     * Strategy for creating new instances of a class.<br>

+     * Useful for plug-in calls to non-default constructors.

+     */

+    public interface NewInstanceResolver {

+        /**

+         * Create a new instance of Class klass.

+         *

+         * @param klass the resolved class name

+         * @param attributes the attribute Map available for the node

+         */

+        Object newInstance( Class klass, Map attributes ) throws InstantiationException,

+                IllegalAccessException;

+    }

+

+    /**

+     * Strategy for picking the correct synthetic reference identifier.

+     */

+    public interface ReferenceResolver {

+        /**

+         * Returns the name of the property that references another node.<br>

+         *

+         * @param nodeName the name of the node

+         */

+        String getReferenceFor( String nodeName );

+    }

+

+    /**

+     * Strategy for resolving a relationship property name.

+     */

+    public interface RelationNameResolver {

+        /**

+         * Returns the mapping name of child -&gt; parent

+         *

+         * @param parentName the name of the parent node

+         * @param parent the parent node

+         * @param childName the name of the child node

+         * @param child the child node

+         */

+        String resolveChildRelationName( String parentName, Object parent, String childName,

+                Object child );

+

+        /**

+         * Returns the mapping name of parent -&gt; child

+         *

+         * @param parentName the name of the parent node

+         * @param parent the parent node

+         * @param childName the name of the child node

+         * @param child the child node

+         */

+        String resolveParentRelationName( String parentName, Object parent, String childName,

+                Object child);

+    }

+

+    private static class ObjectFactory extends AbstractFactory {

+        public Object newInstance( FactoryBuilderSupport builder, Object name, Object value,

+                Map properties ) throws InstantiationException, IllegalAccessException {

+            ObjectGraphBuilder ogbuilder = (ObjectGraphBuilder) builder;

+            String classname = ogbuilder.classNameResolver.resolveClassname( (String) name );

+            Class klass = (Class) ogbuilder.resolvedClasses.get( classname );

+            if( klass == null ){

+                klass = loadClass( ogbuilder.classLoader, classname );

+                if( klass == null ){

+                	klass = loadClass( ogbuilder.getClass().getClassLoader(), classname );

+                }

+                if( klass == null ){

+            	    try{

+                        klass = Class.forName( classname );

+                    }catch( ClassNotFoundException e ){

+                        // ignore

+                    }

+                }

+                if( klass == null ){

+                	klass = loadClass( Thread.currentThread().getContextClassLoader(), classname );

+                }

+                if( klass == null ){

+                	throw new RuntimeException(new ClassNotFoundException(classname));

+                }

+                ogbuilder.resolvedClasses.put( classname, klass );

+            }

+

+            Map context = ogbuilder.getContext();

+            context.put( ObjectGraphBuilder.NODE_NAME, name );

+            context.put( ObjectGraphBuilder.NODE_CLASS, klass );

+

+            return ogbuilder.newInstanceResolver.newInstance( klass, properties );

+        }

+

+        public void setChild( FactoryBuilderSupport builder, Object parent, Object child ) {

+            ObjectGraphBuilder ogbuilder = (ObjectGraphBuilder) builder;

+            if( parent != null ){

+                Map context = ogbuilder.getContext();

+                Map parentContext = ogbuilder.getParentContext();

+

+                String parentName = null;

+                String childName = (String) context.get( NODE_NAME );

+                Class parentClass = null;

+                Class childClass = (Class) context.get( NODE_CLASS );

+                if( parentContext != null ){

+                    parentName = (String) parentContext.get( NODE_NAME );

+                    parentClass = (Class) parentContext.get( NODE_CLASS );

+                }

+

+                String propertyName = ogbuilder.relationNameResolver.resolveParentRelationName(

+                        parentName, parent, childName, child );

+                MetaProperty metaProperty = InvokerHelper.getMetaClass( child )

+                        .hasProperty( child, propertyName );

+                if( metaProperty != null ){

+                    metaProperty.setProperty( child, parent );

+                }

+            }

+        }

+

+        public void setParent( FactoryBuilderSupport builder, Object parent, Object child ) {

+            ObjectGraphBuilder ogbuilder = (ObjectGraphBuilder) builder;

+            if( parent != null ){

+                Map context = ogbuilder.getContext();

+                Map parentContext = ogbuilder.getParentContext();

+

+                String parentName = null;

+                String childName = (String) context.get( NODE_NAME );

+                Class parentClass = null;

+                Class childClass = (Class) context.get( NODE_CLASS );

+                if( parentContext != null ){

+                    parentName = (String) parentContext.get( NODE_NAME );

+                    parentClass = (Class) parentContext.get( NODE_CLASS );

+                }

+

+                ogbuilder.childPropertySetter.setChild( parent, child, parentName,

+                        ogbuilder.relationNameResolver.resolveChildRelationName( parentName,

+                                parent, childName, child ) );

+            }

+        }

+        

+        private Class loadClass( ClassLoader classLoader, String classname ){

+            if( classLoader == null || classname == null ){

+            	return null;

+            }

+        	try{

+                return classLoader.loadClass( classname );

+            }catch( ClassNotFoundException e ){

+                return null;

+            }

+        }

+    }

+

+    private static class ObjectRefFactory extends ObjectFactory {

+        public boolean isLeaf() {

+            return true;

+        }

+

+        public Object newInstance( FactoryBuilderSupport builder, Object name, Object value,

+                Map properties ) throws InstantiationException, IllegalAccessException {

+            ObjectGraphBuilder ogbuilder = (ObjectGraphBuilder) builder;

+            String refProperty = ogbuilder.referenceResolver.getReferenceFor( (String) name );

+            String refId = (String) properties.remove( refProperty );

+

+            Object object = ogbuilder.getProperty( refId );

+            if( object == null ){

+                throw new IllegalArgumentException( "There is no previous node with "

+                        + ogbuilder.identifierResolver.getIdentifierFor( (String) name ) + "="

+                        + refId );

+            }

+

+            if( !properties.isEmpty() ){

+                throw new IllegalArgumentException(

+                        "You can not modify the properties of a referenced object." );

+            }

+

+            Map context = ogbuilder.getContext();

+            context.put( ObjectGraphBuilder.NODE_NAME, name );

+            context.put( ObjectGraphBuilder.NODE_CLASS, object.getClass() );

+

+            return object;

+        }

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/util/ObservableMap.java b/groovy/src/main/groovy/util/ObservableMap.java
new file mode 100644
index 0000000..6a488e3
--- /dev/null
+++ b/groovy/src/main/groovy/util/ObservableMap.java
@@ -0,0 +1,177 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.util;

+

+import groovy.lang.Closure;

+

+import java.beans.PropertyChangeListener;

+import java.beans.PropertyChangeSupport;

+import java.util.*;

+

+/**

+ * Map decorator that will trigger PropertyChangeEvents when a value changes.<br>

+ * An optional Closure may be specified and will work as a filter, if it returns

+ * true the property will trigger an event (if the value indeed changed),

+ * otherwise it won't. The Closure may receive 1 or 2 parameters, the single one

+ * being the value, the other one both the key and value, for example:

+ * <pre>

+ * // skip all properties whose value is a closure

+ * def map = new ObservableMap( {!(it instanceof Closure)} )

+ * <p/>

+ * // skip all properties whose name matches a regex

+ * def map = new ObservableMap( { name, value -&gt; !(name =~ /[A-Z+]/) } )

+ * </pre>

+ *

+ * @author <a href="mailto:aalmiray@users.sourceforge.net">Andres Almiray</a>

+ */

+public class ObservableMap implements Map {

+    private Map delegate;

+    private PropertyChangeSupport pcs;

+    private Closure test;

+

+    public ObservableMap() {

+        this(new LinkedHashMap(), null);

+    }

+

+    public ObservableMap(Closure test) {

+        this(new LinkedHashMap(), test);

+    }

+

+    public ObservableMap(Map delegate) {

+        this(delegate, null);

+    }

+

+    public ObservableMap(Map delegate, Closure test) {

+        this.delegate = delegate;

+        this.test = test;

+        pcs = new PropertyChangeSupport(this);

+    }

+

+    // Map interface

+

+    public void clear() {

+        delegate.clear();

+    }

+

+    public boolean containsKey(Object key) {

+        return delegate.containsKey(key);

+    }

+

+    public boolean containsValue(Object value) {

+        return delegate.containsValue(value);

+    }

+

+    public Set entrySet() {

+        return delegate.entrySet();

+    }

+

+    public boolean equals(Object o) {

+        return delegate.equals(o);

+    }

+

+    public Object get(Object key) {

+        return delegate.get(key);

+    }

+

+    public int hashCode() {

+        return delegate.hashCode();

+    }

+

+    public boolean isEmpty() {

+        return delegate.isEmpty();

+    }

+

+    public Set keySet() {

+        return delegate.keySet();

+    }

+

+    public Object put(Object key, Object value) {

+        Object oldValue = null;

+        if (test != null) {

+            oldValue = delegate.put(key, value);

+            Object result = null;

+            if (test.getMaximumNumberOfParameters() == 2) {

+                result = test.call(new Object[] {key, value});

+            } else {

+                result = test.call(value);

+            }

+            if (result != null && result instanceof Boolean && ((Boolean) result).booleanValue()) {

+                if (oldValue != value) {

+                    pcs.firePropertyChange(String.valueOf(key), oldValue, value);

+                }

+            }

+        } else {

+            oldValue = delegate.put(key, value);

+            if (oldValue != value) {

+                pcs.firePropertyChange(String.valueOf(key), oldValue, value);

+            }

+        }

+        return oldValue;

+    }

+

+    public void putAll(Map map) {

+        if (map != null) {

+            for (Iterator entries = map.entrySet()

+                    .iterator(); entries.hasNext();) {

+                Map.Entry entry = (Map.Entry) entries.next();

+

+                put(entry.getKey(), entry.getValue());

+            }

+        }

+    }

+

+    public Object remove(Object key) {

+        return delegate.remove(key);

+    }

+

+    public int size() {

+        return delegate.size();

+    }

+

+    public Collection values() {

+        return delegate.values();

+    }

+

+    // observable interface

+

+    public void addPropertyChangeListener(PropertyChangeListener listener) {

+        pcs.addPropertyChangeListener(listener);

+    }

+

+    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {

+        pcs.addPropertyChangeListener(propertyName, listener);

+    }

+

+    public PropertyChangeListener[] getPropertyChangeListeners() {

+        return pcs.getPropertyChangeListeners();

+    }

+

+    public PropertyChangeListener[] getPropertyChangeListeners(String propertyName) {

+        return pcs.getPropertyChangeListeners(propertyName);

+    }

+

+    public void removePropertyChangeListener(PropertyChangeListener listener) {

+        pcs.removePropertyChangeListener(listener);

+    }

+

+    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {

+        pcs.removePropertyChangeListener(propertyName, listener);

+    }

+

+    public boolean hasListeners(String propertyName) {

+        return pcs.hasListeners(propertyName);

+    }

+}

diff --git a/groovy/src/main/groovy/util/OrderBy.java b/groovy/src/main/groovy/util/OrderBy.java
new file mode 100644
index 0000000..fced8bd
--- /dev/null
+++ b/groovy/src/main/groovy/util/OrderBy.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.Closure;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A helper class for sorting objects via a closure to return the field
+ * or operation on which to sort.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class OrderBy implements Comparator {
+
+    private List closures;
+
+    public OrderBy() {
+        this.closures = new ArrayList();
+    }
+
+    public OrderBy(Closure closure) {
+        this();
+        closures.add(closure);
+    }
+
+    public OrderBy(List closures) {
+        this.closures = closures;
+    }
+
+    public void add(Closure closure) {
+        closures.add(closure);
+    }
+
+    public int compare(Object object1, Object object2) {
+        for (Iterator iter = closures.iterator(); iter.hasNext();) {
+            Closure closure = (Closure) iter.next();
+            Object value1 = closure.call(object1);
+            Object value2 = closure.call(object2);
+
+            if (value1 == value2) {
+                continue;
+            }
+            if (value1 == null) {
+                return -1;
+            }
+            if (value1 instanceof Comparable) {
+                Comparable c1 = (Comparable) value1;
+                return c1.compareTo(value2);
+            }
+            if (value1.equals(value2)) {
+                continue;
+            }
+            return value1.hashCode() - value2.hashCode();
+        }
+        return 0;
+    }
+}
diff --git a/groovy/src/main/groovy/util/Proxy.java b/groovy/src/main/groovy/util/Proxy.java
new file mode 100644
index 0000000..67417b0
--- /dev/null
+++ b/groovy/src/main/groovy/util/Proxy.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import java.util.Iterator;
+
+import groovy.lang.GroovyObjectSupport;
+import groovy.lang.MissingMethodException;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * Dynamic groovy proxy for another object.  All method
+ * invocations get forwarded to actual object, unless the proxy overrides it.
+ * See groovy/util/ProxyTest.groovy for usage details.
+ *
+ * @author Troy Heninger
+ * @author Dierk Koenig
+ */
+public class Proxy extends GroovyObjectSupport {
+
+    private Object adaptee = null;
+
+    /**
+     * This method is for convenience.
+     * It allows to get around the need for defining dump ctors is subclasses.
+     * See unit tests for details.
+     */
+    public Proxy wrap(Object adaptee){
+        setAdaptee(adaptee);
+        return this;
+    }
+
+    public Object getAdaptee() {
+        return adaptee;
+    }
+
+    public void setAdaptee(Object adaptee) {
+        this.adaptee = adaptee;
+    }
+
+    public Object invokeMethod(String name, Object args) {
+        try {
+            return super.invokeMethod(name, args);
+        }
+        catch (MissingMethodException e) {
+            return InvokerHelper.getMetaClass(adaptee).invokeMethod(adaptee, name, args);
+        }
+    }
+    
+    public Iterator iterator() {
+        return InvokerHelper.asIterator(adaptee);
+    }
+
+}
diff --git a/groovy/src/main/groovy/util/ProxyGenerator.java b/groovy/src/main/groovy/util/ProxyGenerator.java
new file mode 100644
index 0000000..aa6af87
--- /dev/null
+++ b/groovy/src/main/groovy/util/ProxyGenerator.java
@@ -0,0 +1,426 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.lang.*;
+import org.codehaus.groovy.control.MultipleCompilationErrorsException;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/**
+ * Classes to generate 'Proxy' objects which implement interfaces
+ * and/or extend classes.
+ *
+ * @author Paul King
+ * @author Guillaume Laforge
+ */
+public class ProxyGenerator {
+    public static boolean debug = false;
+
+    public static Object instantiateAggregateFromBaseClass(Class clazz) {
+        return instantiateAggregateFromBaseClass(null, clazz);
+    }
+
+    public static Object instantiateAggregateFromBaseClass(Map map, Class clazz) {
+        return instantiateAggregateFromBaseClass(map, clazz, null);
+    }
+
+    public static Object instantiateAggregateFromBaseClass(Map map, Class clazz, Object[] constructorArgs) {
+        return instantiateAggregate(map, null, clazz, constructorArgs);
+    }
+
+    public static Object instantiateAggregateFromInterface(Class clazz) {
+        return instantiateAggregateFromInterface(null, clazz);
+    }
+
+    public static Object instantiateAggregateFromInterface(Map map, Class clazz) {
+        List interfaces = new ArrayList();
+        interfaces.add(clazz);
+        return instantiateAggregate(map, interfaces);
+    }
+
+    public static Object instantiateAggregate(List interfaces) {
+        return instantiateAggregate(null, interfaces);
+    }
+
+    public static Object instantiateAggregate(Map closureMap, List interfaces) {
+        return instantiateAggregate(closureMap, interfaces, null);
+    }
+
+    public static Object instantiateAggregate(Map closureMap, List interfaces, Class clazz) {
+        return instantiateAggregate(closureMap, interfaces, clazz, null);
+    }
+
+    public static Object instantiateAggregate(Map closureMap, List interfaces, Class clazz, Object[] constructorArgs) {
+        Map map = new HashMap();
+        if (closureMap != null) {
+            map = closureMap;
+        }
+        List interfacesToImplement = new ArrayList();
+        if (interfaces != null) {
+            interfacesToImplement = interfaces;
+        }
+        Class baseClass = GroovyObjectSupport.class;
+        if (clazz != null) {
+            baseClass = clazz;
+        }
+        boolean hasArgs = constructorArgs != null && constructorArgs.length > 0;
+        String name = shortName(baseClass.getName()) + "_groovyProxy";
+        StringBuffer buffer = new StringBuffer();
+
+        // add class header and fields
+        buffer.append("class ").append(name);
+        if (clazz != null) {
+            buffer.append(" extends ").append(baseClass.getName());
+        }
+        for (int i = 0; i < interfacesToImplement.size(); i++) {
+            Class thisInterface = (Class) interfacesToImplement.get(i);
+            if (i == 0) {
+                buffer.append(" implements ");
+            } else {
+                buffer.append(", ");
+            }
+            buffer.append(thisInterface.getName());
+        }
+        buffer.append(" {\n").append("    private closureMap\n    ");
+
+        // add constructor
+        buffer.append(name).append("(map");
+        if (hasArgs) {
+            buffer.append(", args");
+        }
+        buffer.append(") {\n");
+        buffer.append("        super(");
+        if (hasArgs) {
+            buffer.append("*args");
+        }
+        buffer.append(")\n");
+        buffer.append("        this.closureMap = map\n");
+        buffer.append("    }\n");
+
+        // add overwriting methods
+        List selectedMethods = new ArrayList();
+        List publicAndProtectedMethods = DefaultGroovyMethods.toList(baseClass.getMethods());
+        publicAndProtectedMethods.addAll(getInheritedMethods(baseClass));
+        for (int i = 0; i < publicAndProtectedMethods.size(); i++) {
+            Method method = (Method) publicAndProtectedMethods.get(i);
+            if (map.containsKey(method.getName())) {
+                selectedMethods.add(method.getName());
+                addOverridingMapCall(buffer, method);
+            }
+        }
+
+        // add interface methods
+        List interfaceMethods = new ArrayList();
+        for (int i = 0; i < interfacesToImplement.size(); i++) {
+            Class thisInterface = (Class) interfacesToImplement.get(i);
+            interfaceMethods.addAll(DefaultGroovyMethods.toList(thisInterface.getMethods()));
+            interfaceMethods.addAll(getInheritedMethods(thisInterface));
+        }
+        for (int i = 0; i < interfaceMethods.size(); i++) {
+            Method method = (Method) interfaceMethods.get(i);
+            if (!containsEquivalentMethod(publicAndProtectedMethods, method)) {
+                selectedMethods.add(method.getName());
+                addMapOrDummyCall(map, buffer, method);
+            }
+        }
+
+        // add leftover methods from the map
+        for (Iterator iterator = map.keySet().iterator(); iterator.hasNext();) {
+            String methodName = (String) iterator.next();
+            if (selectedMethods.contains(methodName)) continue;
+            addNewMapCall(buffer, methodName);
+        }
+
+        // end class
+
+        buffer.append("}\n").append("new ").append(name);
+        buffer.append("(map");
+        if (hasArgs) {
+            buffer.append(", constructorArgs");
+        }
+        buffer.append(")");
+
+        Binding binding = new Binding();
+        binding.setVariable("map", map);
+        binding.setVariable("constructorArgs", constructorArgs);
+        ClassLoader cl = baseClass.getClassLoader();
+        if (clazz == null && interfacesToImplement.size() > 0) {
+            Class c = (Class) interfacesToImplement.get(0);
+            cl = c.getClassLoader();
+        }
+        GroovyShell shell = new GroovyShell(cl, binding);
+        if (debug)
+            System.out.println("proxy source:\n------------------\n" + buffer.toString() + "\n------------------");
+        try {
+            return shell.evaluate(buffer.toString());
+        } catch (MultipleCompilationErrorsException err) {
+            throw new GroovyRuntimeException("Error creating proxy: " + err.getMessage());
+        }
+    }
+
+    public static Object instantiateDelegate(Object delegate) {
+        return instantiateDelegate(null, delegate);
+    }
+
+    public static Object instantiateDelegate(List interfaces, Object delegate) {
+        return instantiateDelegate(null, interfaces, delegate);
+    }
+
+    public static Object instantiateDelegate(Map closureMap, List interfaces, Object delegate) {
+        return instantiateDelegateWithBaseClass(closureMap, interfaces, delegate, null);
+    }
+
+    public static Object instantiateDelegateWithBaseClass(Map closureMap, List interfaces, Object delegate) {
+        return instantiateDelegateWithBaseClass(closureMap, interfaces, delegate, delegate.getClass());
+    }
+
+    public static Object instantiateDelegateWithBaseClass(Map closureMap, List interfaces, Object delegate, Class baseClass) {
+        Map map = new HashMap();
+        if (closureMap != null) {
+            map = closureMap;
+        }
+        List selectedMethods = new ArrayList();
+        List interfacesToImplement = new ArrayList();
+        if (interfaces != null) {
+            interfacesToImplement = interfaces;
+        }
+        String name = shortName(delegate.getClass().getName()) + "_delegateProxy";
+        StringBuffer buffer = new StringBuffer();
+
+        // add class header and fields
+        buffer.append("import org.codehaus.groovy.runtime.InvokerHelper\nclass ").append(name);
+        if (baseClass != null) {
+            buffer.append(" extends ").append(baseClass.getName());
+        }
+        
+        for (int i = 0; i < interfacesToImplement.size(); i++) {
+            Class thisInterface = (Class) interfacesToImplement.get(i);
+            if (i == 0) {
+                buffer.append(" implements ");
+            } else {
+                buffer.append(", ");
+            }
+            buffer.append(thisInterface.getName());
+        }
+        buffer.append(" {\n").append("    private delegate\n").append("    private closureMap\n    ");
+
+        // add constructor
+        buffer.append(name).append("(map, delegate) {\n");
+        buffer.append("        this.closureMap = map\n");
+        buffer.append("        this.delegate = delegate\n");
+        buffer.append("    }\n");
+
+        List objectMethods = DefaultGroovyMethods.toList(Object.class.getMethods());
+        objectMethods.addAll(getInheritedMethods(Object.class));
+
+        List groovyObjectMethods = DefaultGroovyMethods.toList(GroovyObject.class.getMethods());
+        groovyObjectMethods.addAll(getInheritedMethods(GroovyObject.class));
+
+        // add interface methods
+        List interfaceMethods = new ArrayList();
+        for (int i = 0; i < interfacesToImplement.size(); i++) {
+            Class thisInterface = (Class) interfacesToImplement.get(i);
+            interfaceMethods.addAll(DefaultGroovyMethods.toList(thisInterface.getMethods()));
+            interfaceMethods.addAll(getInheritedMethods(thisInterface));
+        }
+        for (int i = 0; i < interfaceMethods.size(); i++) {
+            Method method = (Method) interfaceMethods.get(i);
+            if (!containsEquivalentMethod(objectMethods, method) &&
+                    !containsEquivalentMethod(groovyObjectMethods, method)) {
+                selectedMethods.add(method.getName());
+                addWrappedCall(buffer, method, map);
+            }
+        }
+        List additionalMethods = new ArrayList();
+        additionalMethods.addAll(DefaultGroovyMethods.toList(delegate.getClass().getMethods()));
+        additionalMethods.addAll(getInheritedMethods(delegate.getClass()));
+        for (int i = 0; i < additionalMethods.size(); i++) {
+            Method method = (Method) additionalMethods.get(i);
+            if (!containsEquivalentMethod(interfaceMethods, method) &&
+                    !containsEquivalentMethod(objectMethods, method) &&
+                    !containsEquivalentMethod(groovyObjectMethods, method)) {
+                selectedMethods.add(method.getName());
+                addWrappedCall(buffer, method, map);
+            }
+        }
+
+        // add leftover methods from the map
+        for (Iterator iterator = map.keySet().iterator(); iterator.hasNext();) {
+            String methodName = (String) iterator.next();
+            if (selectedMethods.contains(methodName)) continue;
+            addNewMapCall(buffer, methodName);
+        }
+
+        // end class
+
+        buffer.append("}\n").append("new ").append(name);
+        buffer.append("(map, delegate)");
+
+        Binding binding = new Binding();
+        binding.setVariable("map", map);
+        binding.setVariable("delegate", delegate);
+        ClassLoader cl = delegate.getClass().getClassLoader();
+        GroovyShell shell = new GroovyShell(cl, binding);
+        if (debug)
+            System.out.println("proxy source:\n------------------\n" + buffer.toString() + "\n------------------");
+        try {
+            return shell.evaluate(buffer.toString());
+        } catch (MultipleCompilationErrorsException err) {
+            throw new GroovyRuntimeException("Error creating proxy: " + err.getMessage());
+        }
+    }
+
+    private static void addWrappedCall(StringBuffer buffer, Method method, Map map) {
+        if (map.containsKey(method.getName())) {
+            addOverridingMapCall(buffer, method);
+        } else {
+            Class[] parameterTypes = addMethodPrefix(buffer, method);
+            addWrappedMethodBody(buffer, method, parameterTypes);
+            addMethodSuffix(buffer);
+        }
+    }
+
+    private static boolean containsEquivalentMethod(List publicAndProtectedMethods, Method candidate) {
+        for (int i = 0; i < publicAndProtectedMethods.size(); i++) {
+            Method method = (Method) publicAndProtectedMethods.get(i);
+            if (candidate.getName().equals(method.getName()) &&
+                    candidate.getReturnType().equals(method.getReturnType()) &&
+                    hasMatchingParameterTypes(candidate, method)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static boolean hasMatchingParameterTypes(Method method, Method candidate) {
+        Class[] candidateParamTypes = candidate.getParameterTypes();
+        Class[] methodParamTypes = method.getParameterTypes();
+        if (candidateParamTypes.length != methodParamTypes.length) return false;
+        for (int i = 0; i < methodParamTypes.length; i++) {
+            if (!candidateParamTypes[i].equals(methodParamTypes[i])) return false;
+        }
+        return true;
+    }
+
+    private static List getInheritedMethods(Class baseClass) {
+        List protectedMethodList = new ArrayList();
+        Class currentClass = baseClass;
+        while (currentClass != null) {
+            Method[] protectedMethods = currentClass.getDeclaredMethods();
+            for (int i = 0; i < protectedMethods.length; i++) {
+                Method method = protectedMethods[i];
+                if (Modifier.isProtected(method.getModifiers()))
+                    protectedMethodList.add(method);
+            }
+            currentClass = currentClass.getSuperclass();
+        }
+        return protectedMethodList;
+    }
+
+    private static void addNewMapCall(StringBuffer buffer, String methodName) {
+        buffer.append("    def ").append(methodName).append("(Object[] args) {\n")
+                .append("        this.@closureMap['").append(methodName).append("'] (*args)\n    }\n");
+    }
+
+    private static void addOverridingMapCall(StringBuffer buffer, Method method) {
+        Class[] parameterTypes = addMethodPrefix(buffer, method);
+        addMethodBody(buffer, method, parameterTypes);
+        addMethodSuffix(buffer);
+    }
+
+    private static void addMapOrDummyCall(Map map, StringBuffer buffer, Method method) {
+        Class[] parameterTypes = addMethodPrefix(buffer, method);
+        if (map.containsKey(method.getName())) {
+            addMethodBody(buffer, method, parameterTypes);
+        }
+        addMethodSuffix(buffer);
+    }
+
+    private static Class[] addMethodPrefix(StringBuffer buffer, Method method) {
+        buffer.append("    ").append(getSimpleName(method.getReturnType()))
+                .append(" ").append(method.getName()).append("(");
+        Class[] parameterTypes = method.getParameterTypes();
+        for (int parameterTypeIndex = 0; parameterTypeIndex < parameterTypes.length; parameterTypeIndex++) {
+            Class parameter = parameterTypes[parameterTypeIndex];
+            if (parameterTypeIndex != 0) {
+                buffer.append(", ");
+            }
+            buffer.append(getSimpleName(parameter)).append(" ")
+                    .append("p").append(parameterTypeIndex);
+        }
+        buffer.append(") { ");
+        return parameterTypes;
+    }
+
+    private static void addMethodBody(StringBuffer buffer, Method method, Class[] parameterTypes) {
+        buffer.append("this.@closureMap['").append(method.getName()).append("'] (");
+        for (int j = 0; j < parameterTypes.length; j++) {
+            if (j != 0) {
+                buffer.append(", ");
+            }
+            buffer.append("p").append(j);
+        }
+        buffer.append(")");
+    }
+
+    private static void addWrappedMethodBody(StringBuffer buffer, Method method, Class[] parameterTypes) {
+        buffer.append("\n        Object[] args = [");
+        for (int j = 0; j < parameterTypes.length; j++) {
+            if (j != 0) {
+                buffer.append(", ");
+            }
+            buffer.append("p").append(j);
+        }
+        buffer.append("]\n        ");
+        buffer.append("InvokerHelper.invokeMethod(delegate, '").append(method.getName()).append("', args)\n");
+    }
+
+    private static void addMethodSuffix(StringBuffer buffer) {
+        buffer.append("    }\n");
+    }
+
+    /**
+     * TODO once we switch to Java 1.5 bt default, use Class#getSimpleName() directly
+     *
+     * @param c the class of which we want the readable simple name
+     * @return the readable simple name
+     */
+    private static String getSimpleName(Class c) {
+        if (c.isArray()) {
+            int dimension = 0;
+            Class componentClass = c;
+            while (componentClass.isArray()) {
+                componentClass = componentClass.getComponentType();
+                dimension++;
+            }
+            return componentClass.getName().replaceAll("\\$", "\\.") +
+                    DefaultGroovyMethods.multiply("[]", new Integer(dimension));
+        } else {
+            return c.getName().replaceAll("\\$", "\\.");
+        }
+    }
+
+    public static String shortName(String name) {
+        int index = name.lastIndexOf('.');
+        if (index == -1) return name;
+        return name.substring(index + 1, name.length());
+    }
+
+}
diff --git a/groovy/src/main/groovy/util/ResourceConnector.java b/groovy/src/main/groovy/util/ResourceConnector.java
new file mode 100644
index 0000000..af08b54
--- /dev/null
+++ b/groovy/src/main/groovy/util/ResourceConnector.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import java.net.URLConnection;
+
+/**
+ * Base interface for customizing where resources can be found for the <code>GroovyScriptEngine</code>.
+ *
+ * @author sam
+ */
+public interface ResourceConnector {
+
+    /**
+     * Retrieve a URLConnection to a script referenced by name.
+     *
+     * @param name
+     * @throws ResourceException
+     */
+    URLConnection getResourceConnection(String name) throws ResourceException;
+}
diff --git a/groovy/src/main/groovy/util/ResourceException.java b/groovy/src/main/groovy/util/ResourceException.java
new file mode 100644
index 0000000..d69f943
--- /dev/null
+++ b/groovy/src/main/groovy/util/ResourceException.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+/**
+ * @author sam
+ *
+ * To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+public class ResourceException extends Exception {
+
+	/**
+	 * 
+	 */
+	public ResourceException() {
+		super();
+		// TODO Auto-generated constructor stub
+	}
+
+	/**
+	 * @param message
+	 */
+	public ResourceException(String message) {
+		super(message);
+	}
+
+	/**
+	 * @param message
+	 * @param cause
+	 */
+	public ResourceException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	/**
+	 * @param cause
+	 */
+	public ResourceException(Throwable cause) {
+		super(cause);
+	}
+
+}
diff --git a/groovy/src/main/groovy/util/ScriptException.java b/groovy/src/main/groovy/util/ScriptException.java
new file mode 100644
index 0000000..353c95d
--- /dev/null
+++ b/groovy/src/main/groovy/util/ScriptException.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util;
+
+/**
+ * @author sam
+ *
+ * To change the template for this generated type comment go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+public class ScriptException extends Exception {
+
+	/**
+	 * 
+	 */
+	public ScriptException() {
+		super();
+	}
+
+	/**
+	 * @param message
+	 */
+	public ScriptException(String message) {
+		super(message);
+	}
+
+	/**
+	 * @param message
+	 * @param cause
+	 */
+	public ScriptException(String message, Throwable cause) {
+		super(message, cause);
+	}
+
+	/**
+	 * @param cause
+	 */
+	public ScriptException(Throwable cause) {
+		super(cause);
+	}
+
+}
diff --git a/groovy/src/main/groovy/util/XmlNodePrinter.java b/groovy/src/main/groovy/util/XmlNodePrinter.java
new file mode 100644
index 0000000..eab4e8c
--- /dev/null
+++ b/groovy/src/main/groovy/util/XmlNodePrinter.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util;
+
+import groovy.xml.QName;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Prints a node with all children in XML format.
+ * 
+ * @see groovy.util.NodePrinter
+ * @author Christian Stein
+ */
+public class XmlNodePrinter {
+
+    protected final IndentPrinter out;
+    private final String quote;
+
+    public XmlNodePrinter() {
+        this(new PrintWriter(new OutputStreamWriter(System.out)));
+    }
+
+    public XmlNodePrinter(PrintWriter out) {
+        this(out, "  ");
+    }
+
+    public XmlNodePrinter(PrintWriter out, String indent) {
+        this(out, indent, "\"");
+    }
+
+    public XmlNodePrinter(PrintWriter out, String indent, String quote) {
+        this(new IndentPrinter(out, indent), quote);
+    }
+
+    public XmlNodePrinter(IndentPrinter out, String quote) {
+        if (out == null) {
+            throw new IllegalArgumentException("Argument 'IndentPrinter out' must not be null!");
+        }
+        this.out = out;
+        this.quote = quote;
+    }
+
+    public String getNameOfNode(Node node) {
+        if (node == null) {
+            throw new IllegalArgumentException("Node must not be null!");
+        }
+        Object name = node.name();
+        if (name instanceof QName) {
+            QName qname = (QName) name;
+            return qname.getQualifiedName();
+        }
+        return name.toString();
+    }
+
+    public boolean isEmptyElement(Node node) {
+        if (node == null) {
+            throw new IllegalArgumentException("Node must not be null!");
+        }
+        if (!node.children().isEmpty()) {
+            return false;
+        }
+        return node.text().length() == 0;
+    }
+
+    public void print(Node node) {
+        /*
+         * Handle empty elements like '<br/>', '<img/> or '<hr noshade="noshade"/>.
+         */
+        if (isEmptyElement(node)) {
+            printLineBegin();
+            out.print("<");
+            out.print(getNameOfNode(node));
+            printNameAttributes(node.attributes());
+            out.print("/>");
+            printLineEnd(); // "node named '" + node.name() + "'"
+            out.flush();
+            return;
+        }
+
+        /*
+         * Handle GSP tag element!
+         */
+        if (printSpecialNode(node)) {
+            out.flush();
+            return;
+        }
+
+        /*
+         * Handle normal element like <html> ... </html>.
+         */
+        Object value = node.value();
+        if (value instanceof List) {
+            printName(node, true);
+            printList((List) value);
+            printName(node, false);
+            out.flush();
+            return;
+        }
+
+        // treat as simple type - probably a String
+        printName(node, true);
+        printSimpleItemWithIndent(value);
+        printName(node, false);
+        out.flush();
+    }
+
+    protected void printLineBegin() {
+        out.printIndent();
+    }
+
+    protected void printLineEnd() {
+        printLineEnd(null);
+    }
+
+    protected void printLineEnd(String comment) {
+        if (comment != null) {
+            out.print(" <!-- ");
+            out.print(comment);
+            out.print(" -->");
+        }
+        out.print("\n");
+    }
+
+    protected void printList(List list) {
+        out.incrementIndent();
+        for (Iterator iter = list.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            /*
+             * If the current value is a node, recurse into that node.
+             */
+            if (value instanceof Node) {
+                print((Node) value);
+                continue;
+            }
+            printSimpleItem(value);
+
+        }
+        out.decrementIndent();
+    }
+
+    private void printSimpleItemWithIndent(Object value) {
+        out.incrementIndent();
+        printSimpleItem(value);
+        out.decrementIndent();
+    }
+
+    private void printSimpleItem(Object value) {
+        printLineBegin();
+        out.print(InvokerHelper.toString(value));
+        printLineEnd();
+    }
+
+    protected void printName(Node node, boolean begin) {
+        if (node == null) {
+            throw new NullPointerException("Node must not be null.");
+        }
+        Object name = node.name();
+        if (name == null) {
+            throw new NullPointerException("Name must not be null.");
+        }
+        printLineBegin();
+        out.print("<");
+        if (!begin) {
+            out.print("/");
+        }
+        out.print(getNameOfNode(node));
+        if (begin) {
+            printNameAttributes(node.attributes());
+        }
+        out.print(">");
+        printLineEnd();
+    }
+
+    protected void printNameAttributes(Map attributes) {
+        if (attributes == null || attributes.isEmpty()) {
+            return;
+        }
+        for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            out.print(" ");
+            out.print(entry.getKey().toString());
+            out.print("=");
+            Object value = entry.getValue();
+            out.print(quote);
+            if (value instanceof String) {
+                out.print((String) value);
+            } else {
+                out.print(InvokerHelper.toString(value));
+            }
+            out.print(quote);
+        }
+    }
+
+    protected boolean printSpecialNode(Node node) {
+        return false;
+    }
+
+}
diff --git a/groovy/src/main/groovy/util/XmlParser.java b/groovy/src/main/groovy/util/XmlParser.java
new file mode 100644
index 0000000..956dc3d
--- /dev/null
+++ b/groovy/src/main/groovy/util/XmlParser.java
@@ -0,0 +1,380 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util;
+
+import groovy.xml.QName;
+import groovy.xml.FactorySupport;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.*;
+
+/**
+ * A helper class for parsing XML into a tree of Node instances for 
+ * a simple way of processing XML. This parser does not preserve the
+ * XML InfoSet - if thats what you need try using W3C DOM, dom4j, JDOM, XOM etc.
+ * This parser ignores comments and processing instructions and converts the
+ * XML into a Node for each element in the XML with attributes
+ * and child Nodes and Strings. This simple model is sufficient for
+ * most simple use cases of processing XML.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Paul King
+ * @version $Revision$
+ */
+public class XmlParser implements ContentHandler {
+
+    private StringBuffer bodyText = new StringBuffer();
+    private List stack = new ArrayList();
+    private Locator locator;
+    private XMLReader reader;
+    private Node parent;
+
+    private boolean trimWhitespace = true;
+    private boolean namespaceAware;
+
+    public XmlParser() throws ParserConfigurationException, SAXException {
+        this(false, true);
+    }
+
+    public XmlParser(boolean validating, boolean namespaceAware) throws ParserConfigurationException, SAXException {
+        SAXParserFactory factory = FactorySupport.createSaxParserFactory();
+        factory.setNamespaceAware(namespaceAware);
+        this.namespaceAware = namespaceAware;
+        factory.setValidating(validating);
+        reader = factory.newSAXParser().getXMLReader();
+    }
+
+    public XmlParser(XMLReader reader) {
+        this.reader = reader;
+    }
+
+    public XmlParser(SAXParser parser) throws SAXException {
+        reader = parser.getXMLReader();
+    }
+
+    /**
+     * Returns the current trim whitespace setting.
+     *
+     * @return true if whitespace will be trimmed
+     */
+    public boolean isTrimWhitespace() {
+        return trimWhitespace;
+    }
+
+    /**
+     * Sets the trim whitespace setting value.
+     *
+     * @param trimWhitespace the desired setting value
+     */
+    public void setTrimWhitespace(boolean trimWhitespace) {
+        this.trimWhitespace = trimWhitespace;
+    }
+
+    /**
+     * Parses the content of the given file as XML turning it into a tree
+     * of Nodes.
+     *
+     * @param file the File containing the XML to be parsed
+     * @return the root node of the parsed tree of Nodes
+     * @throws SAXException Any SAX exception, possibly
+     *                      wrapping another exception.
+     * @throws IOException  An IO exception from the parser,
+     *                      possibly from a byte stream or character stream
+     *                      supplied by the application.
+     */
+    public Node parse(File file) throws IOException, SAXException {
+        InputSource input = new InputSource(new FileInputStream(file));
+        input.setSystemId("file://" + file.getAbsolutePath());
+        getXMLReader().parse(input);
+        return parent;
+
+    }
+
+    /**
+     * Parse the content of the specified input source into a tree of Nodes.
+     *
+     * @param input the InputSource for the XML to parse
+     * @return the root node of the parsed tree of Nodes
+     * @throws SAXException Any SAX exception, possibly
+     *                      wrapping another exception.
+     * @throws IOException  An IO exception from the parser,
+     *                      possibly from a byte stream or character stream
+     *                      supplied by the application.
+     */
+    public Node parse(InputSource input) throws IOException, SAXException {
+        getXMLReader().parse(input);
+        return parent;
+    }
+
+    /**
+     * Parse the content of the specified input stream into a tree of Nodes.
+     *
+     * Note that using this method will not provide the parser with any URI
+     * for which to find DTDs etc
+     *
+     * @param input an InputStream containing the XML to be parsed
+     * @return the root node of the parsed tree of Nodes
+     * @throws SAXException Any SAX exception, possibly
+     *                      wrapping another exception.
+     * @throws IOException  An IO exception from the parser,
+     *                      possibly from a byte stream or character stream
+     *                      supplied by the application.
+     */
+    public Node parse(InputStream input) throws IOException, SAXException {
+        InputSource is = new InputSource(input);
+        getXMLReader().parse(is);
+        return parent;
+    }
+
+    /**
+     * Parse the content of the specified reader into a tree of Nodes.
+     *
+     * Note that using this method will not provide the parser with any URI
+     * for which to find DTDs etc
+     *
+     * @param in a Reader to read the XML to be parsed
+     * @return the root node of the parsed tree of Nodes
+     * @throws SAXException Any SAX exception, possibly
+     *                      wrapping another exception.
+     * @throws IOException  An IO exception from the parser,
+     *                      possibly from a byte stream or character stream
+     *                      supplied by the application.
+     */
+    public Node parse(Reader in) throws IOException, SAXException {
+        InputSource is = new InputSource(in);
+        getXMLReader().parse(is);
+        return parent;
+    }
+
+    /**
+     * Parse the content of the specified URI into a tree of Nodes.
+     *
+     * @param uri a String containing a uri pointing to the XML to be parsed
+     * @return the root node of the parsed tree of Nodes
+     * @throws SAXException Any SAX exception, possibly
+     *                      wrapping another exception.
+     * @throws IOException  An IO exception from the parser,
+     *                      possibly from a byte stream or character stream
+     *                      supplied by the application.
+     */
+    public Node parse(String uri) throws IOException, SAXException {
+        InputSource is = new InputSource(uri);
+        getXMLReader().parse(is);
+        return parent;
+    }
+
+    /**
+     * A helper method to parse the given text as XML.
+     * 
+     * @param text the XML text to parse
+     * @return the root node of the parsed tree of Nodes
+     * @throws SAXException Any SAX exception, possibly
+     *                      wrapping another exception.
+     * @throws IOException  An IO exception from the parser,
+     *                      possibly from a byte stream or character stream
+     *                      supplied by the application.
+     */
+    public Node parseText(String text) throws IOException, SAXException {
+        return parse(new StringReader(text));
+    }
+    // Delegated XMLReader methods
+    //------------------------------------------------------------------------
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#getDTDHandler()
+     */
+    public DTDHandler getDTDHandler() {
+        return this.reader.getDTDHandler();
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#getEntityResolver()
+     */
+    public EntityResolver getEntityResolver() {
+        return this.reader.getEntityResolver();
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#getErrorHandler()
+     */
+    public ErrorHandler getErrorHandler() {
+        return this.reader.getErrorHandler();
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#getFeature(java.lang.String)
+     */
+    public boolean getFeature(final String uri) throws SAXNotRecognizedException, SAXNotSupportedException {
+        return this.reader.getFeature(uri);
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#getProperty(java.lang.String)
+     */
+    public Object getProperty(final String uri) throws SAXNotRecognizedException, SAXNotSupportedException {
+        return this.reader.getProperty(uri);
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#setDTDHandler(org.xml.sax.DTDHandler)
+     */
+    public void setDTDHandler(final DTDHandler dtdHandler) {
+        this.reader.setDTDHandler(dtdHandler);
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#setEntityResolver(org.xml.sax.EntityResolver)
+     */
+    public void setEntityResolver(final EntityResolver entityResolver) {
+        this.reader.setEntityResolver(entityResolver);
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#setErrorHandler(org.xml.sax.ErrorHandler)
+     */
+    public void setErrorHandler(final ErrorHandler errorHandler) {
+        this.reader.setErrorHandler(errorHandler);
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#setFeature(java.lang.String, boolean)
+     */
+    public void setFeature(final String uri, final boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
+        this.reader.setFeature(uri, value);
+    }
+
+    /* (non-Javadoc)
+     * @see org.xml.sax.XMLReader#setProperty(java.lang.String, java.lang.Object)
+     */
+    public void setProperty(final String uri, final Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
+         this.reader.setProperty(uri, value);
+    }
+
+    // ContentHandler interface
+    //-------------------------------------------------------------------------                    
+    public void startDocument() throws SAXException {
+        parent = null;
+    }
+
+    public void endDocument() throws SAXException {
+        stack.clear();
+    }
+
+    public void startElement(String namespaceURI, String localName, String qName, Attributes list)
+        throws SAXException {
+        addTextToNode();
+
+        Object name = getElementName(namespaceURI, localName, qName);
+
+        int size = list.getLength();
+        Map attributes = new HashMap(size);
+        for (int i = 0; i < size; i++) {
+            Object attributeName = getElementName(list.getURI(i), list.getLocalName(i), list.getQName(i));
+            String value = list.getValue(i);
+            attributes.put(attributeName, value);
+        }
+        parent = new Node(parent, name, attributes, new NodeList());
+        stack.add(parent);
+    }
+
+    public void endElement(String namespaceURI, String localName, String qName) throws SAXException {
+        addTextToNode();
+
+        if (!stack.isEmpty()) {
+            stack.remove(stack.size() - 1);
+            if (!stack.isEmpty()) {
+                parent = (Node) stack.get(stack.size() - 1);
+            }
+        }
+    }
+
+    public void characters(char buffer[], int start, int length) throws SAXException {
+        bodyText.append(buffer, start, length);
+    }
+
+    public void startPrefixMapping(String prefix, String namespaceURI) throws SAXException {
+    }
+
+    public void endPrefixMapping(String prefix) throws SAXException {
+    }
+
+    public void ignorableWhitespace(char buffer[], int start, int len) throws SAXException {
+    }
+
+    public void processingInstruction(String target, String data) throws SAXException {
+    }
+
+    public Locator getDocumentLocator() {
+        return locator;
+    }
+
+    public void setDocumentLocator(Locator locator) {
+        this.locator = locator;
+    }
+
+    public void skippedEntity(String name) throws SAXException {
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------           
+    protected XMLReader getXMLReader() {
+        reader.setContentHandler(this);
+        return reader;
+    }
+
+    protected void addTextToNode() {
+        String text = bodyText.toString();
+        if (trimWhitespace) {
+            text = text.trim();
+        }
+        if (text.length() > 0) {
+            parent.children().add(text);
+        }
+        bodyText = new StringBuffer();
+    }
+
+    protected Object getElementName(String namespaceURI, String localName, String qName) throws SAXException {
+        String name = localName;
+        String prefix = "";
+        if ((name == null) || (name.length() < 1)) {
+            name = qName;
+        }
+        if (namespaceURI == null || namespaceURI.length() <= 0) {
+            return name;
+        }
+        if (qName != null && qName.length() > 0 && namespaceAware) {
+            int index = qName.lastIndexOf(":");
+            if (index > 0) {
+                prefix = qName.substring(0, index);
+            }
+        }
+        return new QName(namespaceURI, name, prefix);
+    }
+}
diff --git a/groovy/src/main/groovy/util/XmlSlurper.java b/groovy/src/main/groovy/util/XmlSlurper.java
new file mode 100644
index 0000000..73b3dc1
--- /dev/null
+++ b/groovy/src/main/groovy/util/XmlSlurper.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util;
+
+import groovy.util.slurpersupport.GPathResult;
+import groovy.util.slurpersupport.Node;
+import groovy.util.slurpersupport.NodeChild;
+import groovy.xml.FactorySupport;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Stack;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class XmlSlurper extends DefaultHandler {
+  private final XMLReader reader;
+  private Node currentNode = null;
+  private final Stack stack = new Stack();
+  private final StringBuffer charBuffer = new StringBuffer();
+  private final Map namespaceTagHints = new Hashtable();
+  private boolean keepWhitespace = false;
+
+  public XmlSlurper() throws ParserConfigurationException, SAXException {
+    this(false, true);
+  }
+  
+  public XmlSlurper(final boolean validating, final boolean namespaceAware) throws ParserConfigurationException, SAXException {
+    SAXParserFactory factory = FactorySupport.createSaxParserFactory();
+    factory.setNamespaceAware(namespaceAware);
+    factory.setValidating(validating);
+    this.reader = factory.newSAXParser().getXMLReader();
+  }
+  
+  public XmlSlurper(final XMLReader reader) {
+    this.reader = reader;
+  }
+  
+  public XmlSlurper(final SAXParser parser) throws SAXException {
+    this(parser.getXMLReader());
+  }
+  
+  /**
+   * @param keepWhitespace
+   * 
+   * If true then whitespace before elements is kept.
+   * The deafult is to discard the whitespace.
+   */
+  public void setKeepWhitespace(boolean keepWhitespace) {
+      this.keepWhitespace = keepWhitespace;
+  }
+  
+  /**
+   * @return The GPathResult instance created by consuming a stream of SAX events
+   * Note if one of the parse methods has been called then this returns null
+   * Note if this is called more than once all calls after the first will return null
+   *
+   */
+  public GPathResult getDocument() {
+    try {
+      return new NodeChild(this.currentNode, null, this.namespaceTagHints);
+    } finally {
+      this.currentNode = null;
+    }
+  }
+  
+  /**
+   * Parse the content of the specified input source into a GPathResult object
+   * 
+   * @param input
+   * @return An object which supports GPath expressions
+   * @throws IOException
+   * @throws SAXException
+   */
+  public GPathResult parse(final InputSource input) throws IOException, SAXException {
+    this.reader.setContentHandler(this);
+    this.reader.parse(input);
+    
+    return getDocument();
+    
+  }
+  
+  /**
+   * Parses the content of the given file as XML turning it into a GPathResult object
+   * 
+   * @param file
+   * @return An object which supports GPath expressions
+   * @throws IOException
+   * @throws SAXException
+   */
+  public GPathResult parse(final File file) throws IOException, SAXException {
+  final InputSource input = new InputSource(new FileInputStream(file));
+    
+    input.setSystemId("file://" + file.getAbsolutePath());
+    
+    return parse(input);
+    
+  }
+  
+  /**
+   * Parse the content of the specified input stream into an GPathResult Object.
+   * Note that using this method will not provide the parser with any URI
+   * for which to find DTDs etc
+   * 
+   * @param input
+   * @return An object which supports GPath expressions
+   * @throws IOException
+   * @throws SAXException
+   */
+  public GPathResult parse(final InputStream input) throws IOException, SAXException {
+    return parse(new InputSource(input));
+  }
+  
+  /**
+   * Parse the content of the specified reader into a GPathResult Object.
+   * Note that using this method will not provide the parser with any URI
+   * for which to find DTDs etc
+   * 
+   * @param in
+   * @return An object which supports GPath expressions
+   * @throws IOException
+   * @throws SAXException
+   */
+  public GPathResult parse(final Reader in) throws IOException, SAXException {
+    return parse(new InputSource(in));
+  }
+  
+  /**
+   * Parse the content of the specified URI into a GPathResult Object
+   * 
+   * @param uri
+   * @return An object which supports GPath expressions
+   * @throws IOException
+   * @throws SAXException
+   */
+  public GPathResult parse(final String uri) throws IOException, SAXException {
+    return parse(new InputSource(uri));
+  }
+  
+  /**
+   * A helper method to parse the given text as XML
+   * 
+   * @param text
+   * @return An object which supports GPath expressions
+   */
+  public GPathResult parseText(final String text) throws IOException, SAXException {
+    return parse(new StringReader(text));
+  }
+  
+  // Delegated XMLReader methods
+  //------------------------------------------------------------------------
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.XMLReader#getDTDHandler()
+   */
+  public DTDHandler getDTDHandler() {
+      return this.reader.getDTDHandler();
+  }
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.XMLReader#getEntityResolver()
+   */
+  public EntityResolver getEntityResolver() {
+      return this.reader.getEntityResolver();
+  }
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.XMLReader#getErrorHandler()
+   */
+  public ErrorHandler getErrorHandler() {
+      return this.reader.getErrorHandler();
+  }
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.XMLReader#getFeature(java.lang.String)
+   */
+  public boolean getFeature(final String uri) throws SAXNotRecognizedException, SAXNotSupportedException {
+      return this.reader.getFeature(uri);
+  }
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.XMLReader#getProperty(java.lang.String)
+   */
+  public Object getProperty(final String uri) throws SAXNotRecognizedException, SAXNotSupportedException {
+      return this.reader.getProperty(uri);
+  }
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.XMLReader#setDTDHandler(org.xml.sax.DTDHandler)
+   */
+  public void setDTDHandler(final DTDHandler dtdHandler) {
+      this.reader.setDTDHandler(dtdHandler);
+  }
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.XMLReader#setEntityResolver(org.xml.sax.EntityResolver)
+   */
+  public void setEntityResolver(final EntityResolver entityResolver) {
+      this.reader.setEntityResolver(entityResolver);
+  }
+
+  /**
+   * Resolves entities against using the suppied URL as the base for relative URLs
+   * 
+   * @param base
+   * The URL used to resolve relative URLs
+   */
+  public void setEntityBaseUrl(final URL base) {
+      this.reader.setEntityResolver(new EntityResolver() {
+          public InputSource resolveEntity(final String publicId, final String systemId) throws IOException {
+              return new InputSource(new URL(base, systemId).openStream());
+          }
+      });
+  }
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.XMLReader#setErrorHandler(org.xml.sax.ErrorHandler)
+   */
+  public void setErrorHandler(final ErrorHandler errorHandler) {
+      this.reader.setErrorHandler(errorHandler);
+  }
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.XMLReader#setFeature(java.lang.String, boolean)
+   */
+  public void setFeature(final String uri, final boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
+      this.reader.setFeature(uri, value);
+  }
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.XMLReader#setProperty(java.lang.String, java.lang.Object)
+   */
+  public void setProperty(final String uri, final Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
+       this.reader.setProperty(uri, value);
+  }
+  
+  
+  // ContentHandler interface
+  //-------------------------------------------------------------------------                    
+  
+  /* (non-Javadoc)
+   * @see org.xml.sax.ContentHandler#startDocument()
+   */
+  public void startDocument() throws SAXException {
+    this.currentNode = null;
+    this.charBuffer.setLength(0);
+  }
+  
+  /* (non-Javadoc)
+   * @see org.xml.sax.helpers.DefaultHandler#startPrefixMapping(java.lang.String, java.lang.String)
+   */
+  public void startPrefixMapping(final String tag, final String uri) throws SAXException {
+    this.namespaceTagHints.put(tag, uri);
+  }
+
+  /* (non-Javadoc)
+   * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
+   */
+  public void startElement(final String namespaceURI, final String localName, final String qName, final Attributes atts) throws SAXException {
+    addCdata();
+    
+    final Map attributes = new HashMap();
+    final Map attributeNamespaces = new HashMap();
+    
+    for (int i = atts.getLength() - 1; i != -1; i--) {
+      if (atts.getURI(i).length() == 0) {
+        attributes.put(atts.getQName(i), atts.getValue(i));
+      } else {
+        attributes.put(atts.getLocalName(i), atts.getValue(i));
+        attributeNamespaces.put(atts.getLocalName(i), atts.getURI(i));
+      }
+      
+    }
+    
+    final Node newElement;
+    
+    if (namespaceURI.length() == 0){
+      newElement = new Node(this.currentNode, qName, attributes, attributeNamespaces, namespaceURI);
+    } else {
+      newElement = new Node(this.currentNode, localName, attributes, attributeNamespaces, namespaceURI);
+    }
+    
+    if (this.currentNode != null) {
+      this.currentNode.addChild(newElement);
+    }
+    
+    this.stack.push(this.currentNode);
+    this.currentNode = newElement;
+  }
+  
+  /* (non-Javadoc)
+   * @see org.xml.sax.ContentHandler#characters(char[], int, int)
+   */
+  public void characters(final char[] ch, final int start, final int length) throws SAXException {
+    this.charBuffer.append(ch, start, length);
+  }
+  
+  /* (non-Javadoc)
+   * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
+   */
+  public void endElement(final String namespaceURI, final String localName, final String qName) throws SAXException {
+    addCdata();
+    
+    final Object oldCurrentNode = this.stack.pop();
+    
+    if (oldCurrentNode != null) {
+      this.currentNode = (Node)oldCurrentNode;
+    }
+  }
+  
+  /* (non-Javadoc)
+   * @see org.xml.sax.ContentHandler#endDocument()
+   */
+  public void endDocument() throws SAXException {
+  }
+  
+  // Implementation methods
+  //-------------------------------------------------------------------------           
+  
+  /**
+   * 
+   */
+  private void addCdata() {
+    if (this.charBuffer.length() != 0) {
+      //
+      // This element is preceeded by CDATA if keepWhitespace is false (the default setting) and 
+      // it's not whitespace add it to the body
+      // Note that, according to the XML spec, we should preserve the CDATA if it's all whitespace
+      // but for the sort of work I'm doing ignoring the whitespace is preferable
+      //
+      final String cdata = this.charBuffer.toString();
+      
+      this.charBuffer.setLength(0);
+      if (this.keepWhitespace || cdata.trim().length() != 0) {
+        this.currentNode.addChild(cdata);
+      }
+    }   
+  }
+}
diff --git a/groovy/src/main/groovy/util/package.html b/groovy/src/main/groovy/util/package.html
new file mode 100644
index 0000000..05b9ba9
--- /dev/null
+++ b/groovy/src/main/groovy/util/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.util.*</title>
+  </head>
+  <body>
+    <p>Various Groovy utilities for working with nodes, builders, logging, JUnit test cases, text expressions, Ant tasks or JMX MBeans.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/util/slurpersupport/Attribute.java b/groovy/src/main/groovy/util/slurpersupport/Attribute.java
new file mode 100644
index 0000000..fe59db9
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/Attribute.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util.slurpersupport;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyRuntimeException;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+public class Attribute extends GPathResult {
+    private final String value;
+
+    public Attribute(final String name, final String value, final GPathResult parent, final String namespacePrefix, final Map namespaceTagHints) {
+      super(parent, name, namespacePrefix, namespaceTagHints);
+      this.value = value;
+    }
+
+    public String name() {
+        // this name contains @name we need to return name
+        return this.name.substring(1);
+    }
+
+    public int size() {
+        return 1;
+    }
+
+    public String text() {
+         return this.value;
+    }
+
+    public GPathResult parents() {
+        // TODO Auto-generated method stub
+        throw new GroovyRuntimeException("parents() not implemented yet");
+    }
+
+    public Iterator childNodes() {
+        throw new GroovyRuntimeException("can't call childNodes() in the attribute " + this.name);
+    }
+
+    public Iterator iterator() {
+        return nodeIterator();
+    }
+
+    public GPathResult find(final Closure closure) {
+        if (DefaultTypeTransformation.castToBoolean(closure.call(new Object[]{this}))) {
+            return this;
+          } else {
+            return new NoChildren(this, "", this.namespaceTagHints);
+          }
+    }
+
+    public GPathResult findAll(final Closure closure) {
+        return find(closure);
+    }
+
+    public Iterator nodeIterator() {
+        return new Iterator() {
+            private boolean hasNext = true;
+            
+            public boolean hasNext() {
+              return this.hasNext;
+            }
+            
+            public Object next() {
+              try {
+                return (this.hasNext) ? Attribute.this : null;
+              } finally {
+                this.hasNext = false;
+              }
+            }
+            
+            public void remove() {
+              throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public Writer writeTo(final Writer out) throws IOException {
+        out.write(this.value);
+        return out;
+    }
+
+    public void build(final GroovyObject builder) {
+        builder.getProperty("mkp");
+        builder.invokeMethod("yield", new Object[]{this.value});
+    }
+
+    protected void replaceNode(final Closure newValue) {
+    }
+
+    protected void replaceBody(final Object newValue) {
+    }
+
+    protected void appendNode(final Object newValue) {
+    }
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/Attributes.java b/groovy/src/main/groovy/util/slurpersupport/Attributes.java
new file mode 100644
index 0000000..84e80c2
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/Attributes.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util.slurpersupport;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyRuntimeException;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * @author John Wilson
+ */
+
+class Attributes extends NodeChildren {
+    final String attributeName;
+
+    public Attributes(final GPathResult parent, final String name, final String namespacePrefix, final Map namespaceTagHints) {
+        super(parent, name, namespacePrefix, namespaceTagHints);
+        this.attributeName = this.name.substring(1);
+    }
+
+    public Attributes(final GPathResult parent, final String name, final Map namespaceTagHints) {
+        this(parent, name, "*", namespaceTagHints);
+    }
+
+    public String name() {
+        // this name contains @name we need to return name
+        return this.name.substring(1);
+    }
+
+    public Iterator childNodes() {
+        throw new GroovyRuntimeException("Can't get the child nodes on a a GPath expression selecting attributes: ...." + this.parent.name() + "." + name() + ".childNodes()");
+    }
+
+    public Iterator iterator() {
+        return new NodeIterator(nodeIterator()) {
+            protected Object getNextNode(final Iterator iter) {
+                while (iter.hasNext()) {
+                    final Object next = iter.next();
+                    if (next instanceof Attribute) {
+                        return next;
+                    } else {
+                        final String value = (String) ((Node) next).attributes().get(Attributes.this.attributeName);
+                        if (value != null) {
+                            return new Attribute(Attributes.this.attributeName,
+                                    value,
+                                    new NodeChild((Node) next, Attributes.this.parent.parent, "", Attributes.this.namespaceTagHints),
+                                    "",
+                                    Attributes.this.namespaceTagHints);
+                        }
+                    }
+                }
+                return null;
+            }
+        };
+    }
+
+    public Iterator nodeIterator() {
+        return this.parent.nodeIterator();
+    }
+
+    public GPathResult parents() {
+        return super.parents();
+    }
+
+    public String text() {
+        final StringBuffer buf = new StringBuffer();
+        final Iterator iter = iterator();
+        while (iter.hasNext()) {
+            buf.append(iter.next());
+        }
+        return buf.toString();
+    }
+
+    public List list() {
+        final Iterator iter = iterator();
+        final List result = new ArrayList();
+        while (iter.hasNext()) {
+            result.add(iter.next());
+        }
+        return result;
+    }
+
+    public GPathResult findAll(final Closure closure) {
+        return new FilteredAttributes(this, closure, this.namespaceTagHints);
+    }
+
+    public Writer writeTo(final Writer out) throws IOException {
+        out.write(text());
+        return out;
+    }
+
+    public void build(final GroovyObject builder) {
+        builder.getProperty("mkp");
+        builder.invokeMethod("yield", new Object[]{text()});
+    }
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/FilteredAttributes.java b/groovy/src/main/groovy/util/slurpersupport/FilteredAttributes.java
new file mode 100644
index 0000000..c94f0b1
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/FilteredAttributes.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util.slurpersupport;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import groovy.lang.Closure;
+
+/**
+ * Lazy evaluated representation of nodes filtered by attributes.
+ *
+ * @author John Wilson
+ */
+public class FilteredAttributes extends Attributes
+{
+    private final Closure closure;
+
+    public FilteredAttributes(final GPathResult parent, final Closure closure, final Map namespaceTagHints) {
+        super(parent, parent.name, namespaceTagHints);
+        this.closure = closure;
+    }
+
+    public Iterator nodeIterator() {
+        return new NodeIterator(this.parent.iterator())
+        {
+            protected Object getNextNode(final Iterator iter) {
+                while (iter.hasNext()) {
+                    final Object node = iter.next();
+                    if (DefaultTypeTransformation.castToBoolean(FilteredAttributes.this.closure.call(new Object[]{node}))) {
+                        return node;
+                    }
+                }
+                return null;
+            }
+        };
+    }
+
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/FilteredNodeChildren.java b/groovy/src/main/groovy/util/slurpersupport/FilteredNodeChildren.java
new file mode 100644
index 0000000..33bdc67
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/FilteredNodeChildren.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util.slurpersupport;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import groovy.lang.Closure;
+
+/**
+ * @author John Wilson
+ */
+
+public class FilteredNodeChildren extends NodeChildren {
+    private final Closure closure;
+
+    public FilteredNodeChildren(final GPathResult parent, final Closure closure, final Map namespaceTagHints) {
+        super(parent, parent.name, namespaceTagHints);
+        this.closure = closure;
+    }
+
+    public Iterator iterator() {
+        return new Iterator() {
+            final Iterator iter = FilteredNodeChildren.this.parent.iterator();
+            Object next = null;
+
+            public boolean hasNext() {
+                while (this.iter.hasNext()) {
+                    final Object childNode = this.iter.next();
+                    if (closureYieldsTrueForNode(childNode)) {
+                        this.next = childNode;
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            public Object next() {
+                return this.next;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public Iterator nodeIterator() {
+        return new NodeIterator(this.parent.nodeIterator()) {
+            protected Object getNextNode(final Iterator iter) {
+                while (iter.hasNext()) {
+                    final Object node = iter.next();
+                    if (closureYieldsTrueForNode(new NodeChild((Node) node, FilteredNodeChildren.this.parent, FilteredNodeChildren.this.namespaceTagHints))) {
+                        return node;
+                    }
+                }
+                return null;
+            }
+        };
+    }
+
+    private boolean closureYieldsTrueForNode(Object childNode) {
+        return DefaultTypeTransformation.castToBoolean(FilteredNodeChildren.this.closure.call(new Object[]{childNode}));
+    }
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/GPathResult.java b/groovy/src/main/groovy/util/slurpersupport/GPathResult.java
new file mode 100644
index 0000000..196a57f
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/GPathResult.java
@@ -0,0 +1,473 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util.slurpersupport;
+
+import groovy.lang.Buildable;
+import groovy.lang.Closure;
+import groovy.lang.DelegatingMetaClass;
+import groovy.lang.GString;
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyObjectSupport;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.IntRange;
+import groovy.lang.MetaClass;
+import groovy.lang.Writable;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+
+/**
+ * @author John Wilson
+ */
+
+public abstract class GPathResult extends GroovyObjectSupport implements Writable, Buildable {
+    protected final GPathResult parent;
+    protected final String name;
+    protected final String namespacePrefix;
+    protected final Map namespaceMap = new HashMap();
+    protected final Map namespaceTagHints;
+
+    /**
+     * @param parent
+     * @param name
+     * @param namespacePrefix
+     * @param namespaceTagHints
+     */
+    public GPathResult(final GPathResult parent, final String name, final String namespacePrefix, final Map namespaceTagHints) {
+        if (parent == null) {
+            // we are the top of the tree
+            this.parent = this;
+            this.namespaceMap.put("xml", "http://www.w3.org/XML/1998/namespace");  // The XML namespace is always defined
+        } else {
+            this.parent = parent;
+            this.namespaceMap.putAll(parent.namespaceMap);
+        }
+        this.name = name;
+        this.namespacePrefix = namespacePrefix;
+        this.namespaceTagHints = namespaceTagHints;
+
+        setMetaClass(getMetaClass()); // wrap the standard MetaClass with the delegate
+    }
+
+    /* (non-Javadoc)
+     * @see groovy.lang.GroovyObjectSupport#setMetaClass(groovy.lang.MetaClass)
+     */
+    public void setMetaClass(final MetaClass metaClass) {
+        final MetaClass newMetaClass = new DelegatingMetaClass(metaClass) {
+            /* (non-Javadoc)
+            * @see groovy.lang.DelegatingMetaClass#getAttribute(java.lang.Object, java.lang.String)
+            */
+            public Object getAttribute(final Object object, final String attribute) {
+                return GPathResult.this.getProperty("@" + attribute);
+            }
+            
+            public void setAttribute(final Object object, final String attribute, final Object newValue) {
+                GPathResult.this.setProperty("@" + attribute, newValue);
+            }
+        };
+        super.setMetaClass(newMetaClass);
+    }
+
+    public Object getProperty(final String property) {
+        if ("..".equals(property)) {
+            return parent();
+        } else if ("*".equals(property)) {
+            return children();
+        } else if ("**".equals(property)) {
+            return depthFirst();
+        } else if (property.startsWith("@")) {
+            if (property.indexOf(":") != -1) {
+                final int i = property.indexOf(":");
+                return new Attributes(this, "@" + property.substring(i + 1), property.substring(1, i), this.namespaceTagHints);
+            } else {
+                return new Attributes(this, property, this.namespaceTagHints);
+            }
+        } else {
+            if (property.indexOf(":") != -1) {
+                final int i = property.indexOf(":");
+                return new NodeChildren(this, property.substring(i + 1), property.substring(0, i), this.namespaceTagHints);
+            } else {
+                return new NodeChildren(this, property, this.namespaceTagHints);
+            }
+        }
+    }
+
+    public void setProperty(final String property, final Object newValue) {
+        if (property.startsWith("@")) {
+            if (newValue instanceof String || newValue instanceof GString) {
+            final Iterator iter = iterator();
+            
+                while (iter.hasNext()) {
+                final NodeChild child = (NodeChild)iter.next();
+                
+                    child.attributes().put(property.substring(1), newValue);
+                }
+            }
+        } else {
+        final GPathResult result = new NodeChildren(this, property, this.namespaceTagHints);
+        
+            if (newValue instanceof Map) {
+            final Iterator iter = ((Map)newValue).entrySet().iterator();
+            
+                while (iter.hasNext()) {
+                final Map.Entry entry = (Map.Entry)iter.next();
+                
+                    result.setProperty("@" + entry.getKey(), entry.getValue());
+                }
+            } else {           
+              if (newValue instanceof Closure) {
+                  result.replaceNode((Closure)newValue);
+              } else {
+                  result.replaceBody(newValue);
+              }
+            }
+        }
+    }
+    
+    public Object leftShift(final Object newValue) {
+        appendNode(newValue);
+        return this;
+    }
+    
+    public Object plus(final Object newValue) {
+        this.replaceNode(new Closure(this) {
+            public void doCall(Object[] args) {
+            final GroovyObject delegate = (GroovyObject)getDelegate();
+             
+                delegate.getProperty("mkp");
+                delegate.invokeMethod("yield", args);
+                
+                delegate.getProperty("mkp");
+                delegate.invokeMethod("yield", new Object[]{newValue});
+            }
+        });
+        
+        return this;
+    }
+    
+    protected abstract void replaceNode(Closure newValue);
+    
+    protected abstract void replaceBody(Object newValue);
+    
+    protected abstract void appendNode(Object newValue);
+
+    public String name() {
+        return this.name;
+    }
+
+    public GPathResult parent() {
+        return this.parent;
+    }
+
+    public GPathResult children() {
+        return new NodeChildren(this, this.namespaceTagHints);
+    }
+    
+    public String lookupNamespace(final String prefix) {
+        return (String)this.namespaceTagHints.get(prefix);
+    }
+
+    public String toString() {
+        return text();
+    }
+
+    public Integer toInteger() {
+        return DefaultGroovyMethods.toInteger(text());
+    }
+
+    public Long toLong() {
+        return DefaultGroovyMethods.toLong(text());
+    }
+
+    public Float toFloat() {
+        return DefaultGroovyMethods.toFloat(text());
+    }
+
+    public Double toDouble() {
+        return DefaultGroovyMethods.toDouble(text());
+    }
+
+    public BigDecimal toBigDecimal() {
+        return DefaultGroovyMethods.toBigDecimal(text());
+    }
+
+    public BigInteger toBigInteger() {
+        return DefaultGroovyMethods.toBigInteger(text());
+    }
+
+    public URL toURL() throws MalformedURLException {
+        return DefaultGroovyMethods.toURL(text());
+    }
+
+    public URI toURI() throws URISyntaxException {
+        return DefaultGroovyMethods.toURI(text());
+    }
+
+    public Boolean toBoolean() {
+        return DefaultGroovyMethods.toBoolean(text());
+    }
+
+    public GPathResult declareNamespace(final Map newNamespaceMapping) {
+        this.namespaceMap.putAll(newNamespaceMapping);
+        return this;
+    }
+
+    /* (non-Javadoc)
+    * @see java.lang.Object#equals(java.lang.Object)
+    */
+    public boolean equals(Object obj) {
+        return text().equals(obj.toString());
+    }
+
+    public Object getAt(final int index) {
+        if (index < 0) throw new ArrayIndexOutOfBoundsException(index);
+        
+        final Iterator iter = iterator();
+        int count = 0;
+    
+        while (iter.hasNext()) {
+            if (count++ == index) {
+                return iter.next();
+            } else {
+                iter.next();
+            }
+        }
+        
+        return new NoChildren(this, this.name, this.namespaceTagHints);
+    }
+
+    public Object getAt(final IntRange range) {
+    final int from = range.getFromInt();
+    final int to = range.getToInt();
+    
+        if (range.isReverse()) {
+            throw new GroovyRuntimeException("Reverse ranges not supported, range supplied is ["+ to + ".." + from + "]");
+        } else if (from < 0 || to < 0) {
+            throw new GroovyRuntimeException("Negative range indexes not supported, range supplied is ["+ from + ".." + to + "]");
+        } else {
+            return new Iterator() {
+            final Iterator iter = iterator();
+            Object next;
+            int count = 0;
+            
+               public boolean hasNext() {
+                   if (count <= to) {
+                       while (iter.hasNext()) {
+                           if (count++ >= from) {
+                               this.next = iter.next();
+                               return true;
+                           } else {
+                               iter.next();
+                           }
+                       }
+                   }
+
+                   return false;
+                }
+
+                public Object next() {
+                    return next;
+                }
+
+                public void remove() {
+                    throw new UnsupportedOperationException();
+                }
+                
+            };
+        }
+     }
+
+    public void putAt(final int index, final Object newValue) {
+    final GPathResult result = (GPathResult)getAt(index);
+    
+        if (newValue instanceof Closure) {
+            result.replaceNode((Closure)newValue);
+        } else {
+            result.replaceBody(newValue);
+        }
+    }
+    
+    public Iterator depthFirst() {
+        return new Iterator() {
+            private final List list = new LinkedList();
+            private final Stack stack = new Stack();
+            private Iterator iter = iterator();
+            private GPathResult next = getNextByDepth();
+
+            public boolean hasNext() {
+                return this.next != null;
+            }
+
+            public Object next() {
+                try {
+                    return this.next;
+                } finally {
+                    this.next = getNextByDepth();
+                }
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+            private GPathResult getNextByDepth() {
+                while (this.iter.hasNext()) {
+                    final GPathResult node = (GPathResult) this.iter.next();
+                    this.list.add(node);
+                    this.stack.push(this.iter);
+                    this.iter = node.children().iterator();
+                }
+
+                if (this.list.isEmpty()) {
+                    return null;
+                } else {
+                    GPathResult result = (GPathResult) this.list.get(0);
+                    this.list.remove(0);
+                    this.iter = (Iterator) this.stack.pop();
+                    return result;
+                }
+            }
+        };
+    }
+
+    /**
+     * An iterator useful for traversing XML documents/fragments in breadth-first order.
+     *
+     * @return Iterator the iterator of GPathResult objects
+     */
+    public Iterator breadthFirst() {
+        return new Iterator() {
+            private final List list = new LinkedList();
+            private Iterator iter = iterator();
+            private GPathResult next = getNextByBreadth();
+
+            public boolean hasNext() {
+                return this.next != null;
+            }
+
+            public Object next() {
+                try {
+                    return this.next;
+                } finally {
+                    this.next = getNextByBreadth();
+                }
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+            private GPathResult getNextByBreadth() {
+                List children = new ArrayList();
+                while (this.iter.hasNext() || !children.isEmpty()) {
+                    if (this.iter.hasNext()) {
+                        final GPathResult node = (GPathResult) this.iter.next();
+                        this.list.add(node);
+                        this.list.add(this.iter);
+                        children.add(node.children());
+                    } else {
+                        List nextLevel = new ArrayList();
+                        for (int i = 0; i < children.size(); i++) {
+                            GPathResult next = (GPathResult) children.get(i);
+                            Iterator iterator = next.iterator();
+                            while (iterator.hasNext()) {
+                                nextLevel.add(iterator.next());
+                            }
+                        }
+                        this.iter = nextLevel.iterator();
+                        children = new ArrayList();
+                    }
+                }
+                if (this.list.isEmpty()) {
+                    return null;
+                } else {
+                    GPathResult result = (GPathResult) this.list.get(0);
+                    this.list.remove(0);
+                    this.iter = (Iterator) this.list.get(0);
+                    this.list.remove(0);
+                    return result;
+                }
+            }
+        };
+    }
+
+    public List list() {
+        final Iterator iter = nodeIterator();
+        final List result = new LinkedList();
+        while (iter.hasNext()) {
+            result.add(new NodeChild((Node) iter.next(), this.parent, this.namespacePrefix, this.namespaceTagHints));
+        }
+        return result;
+    }
+
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+    
+    public Closure getBody() {
+        return new Closure(this.parent(),this) {
+            public void doCall(Object[] args) {
+                final GroovyObject delegate = (GroovyObject)getDelegate();
+                final GPathResult thisObject = (GPathResult)getThisObject();
+
+                Node node = (Node)thisObject.getAt(0);
+                List children = node.children();
+
+                for(int i=0;  i<children.size(); i++){
+                    Object child = children.get(i);
+                    delegate.getProperty("mkp");
+                    if(child instanceof Node){
+                        delegate.invokeMethod("yield", new Object[]{new NodeChild((Node)child, thisObject,"*",null)});
+                    }   
+                    else{
+                        delegate.invokeMethod("yield", new Object[]{child});
+                    }   
+                }                
+            }
+        };
+    }
+
+    public abstract int size();
+
+    public abstract String text();
+
+    public abstract GPathResult parents();
+
+    public abstract Iterator childNodes();
+
+    public abstract Iterator iterator();
+
+    public abstract GPathResult find(Closure closure);
+
+    public abstract GPathResult findAll(Closure closure);
+
+    public abstract Iterator nodeIterator();
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/NoChildren.java b/groovy/src/main/groovy/util/slurpersupport/NoChildren.java
new file mode 100644
index 0000000..8e0ec29
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/NoChildren.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util.slurpersupport;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyRuntimeException;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class NoChildren extends GPathResult {
+  /**
+   * @param parent
+   * @param name
+   */
+  public NoChildren(final GPathResult parent, final String name, final Map namespaceTagHints) {
+    super(parent, name, "*", namespaceTagHints);
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.GPathResult#size()
+   */
+  public int size() {
+    return 0;
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.GPathResult#text()
+   */
+  public String text() {
+    return "";
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.GPathResult#parents()
+   */
+  public GPathResult parents() {
+    // TODO Auto-generated method stub
+    throw new GroovyRuntimeException("parents() not implemented yet");
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.GPathResult#childNodes()
+   */
+  public Iterator childNodes() {
+    return iterator();
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.GPathResult#iterator()
+   */
+  public Iterator iterator() {
+    return new Iterator() {
+      public boolean hasNext() {
+        return false;
+      }
+      
+      public Object next() {
+        return null;
+      }
+      
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+    };
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.GPathResult#find(groovy.lang.Closure)
+   */
+  public GPathResult find(final Closure closure) {
+    return this;
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.GPathResult#findAll(groovy.lang.Closure)
+   */
+  public GPathResult findAll(final Closure closure) {
+    return this;
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.GPathResult#nodeIterator()
+   */
+  public Iterator nodeIterator() {
+    return iterator();
+  }
+
+  /* (non-Javadoc)
+   * @see groovy.lang.Writable#writeTo(java.io.Writer)
+   */
+  public Writer writeTo(final Writer out) throws IOException {
+    return out;
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.markup.Buildable#build(groovy.lang.GroovyObject)
+   */
+  public void build(final GroovyObject builder) {
+  }
+
+  protected void replaceNode(final Closure newValue) {
+    // No elements match GPath expression - do nothing
+  }
+
+  protected void replaceBody(final Object newValue) {
+    // No elements match GPath expression - do nothing   
+  }
+
+  protected void appendNode(final Object newValue) {
+    // TODO consider creating an element for this
+  }
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/Node.java b/groovy/src/main/groovy/util/slurpersupport/Node.java
new file mode 100644
index 0000000..b6c3925
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/Node.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util.slurpersupport;
+
+import groovy.lang.Buildable;
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
+import groovy.lang.Writable;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class Node implements Writable {
+  private final String name;
+  private final Map attributes;
+  private final Map attributeNamespaces;
+  private final String namespaceURI;
+  private final List children = new LinkedList();
+  private final Stack replacementNodeStack = new Stack();
+  
+  public Node(final Node parent, final String name, final Map attributes, final Map attributeNamespaces, final String namespaceURI) {
+    this.name = name;
+    this.attributes = attributes;
+    this.attributeNamespaces = attributeNamespaces;
+    this.namespaceURI = namespaceURI;
+  }
+  
+  public String name() {
+    return this.name;
+  }
+  
+  public String namespaceURI() {
+    return this.namespaceURI;
+  }
+  
+  public Map attributes() {
+    return this.attributes;
+  }
+
+  public List children() {
+    return this.children;
+  }
+
+  public void addChild(final Object child) {
+    this.children.add(child);
+  }
+  
+  public void replaceNode(final Closure replacementClosure, final GPathResult result) {
+      this.replacementNodeStack.push(new ReplacementNode() {
+                                          public void build(final GroovyObject builder, final Map namespaceMap, final Map namespaceTagHints) {
+                                              final Closure c = (Closure)replacementClosure.clone();
+                                              
+                                                  Node.this.replacementNodeStack.pop(); // disable the replacement whilst the closure is being executed 
+                                                  c.setDelegate(builder);
+                                                  c.call(new Object[]{result});
+                                                  Node.this.replacementNodeStack.push(this);
+                                              }
+                                          });
+  }
+  
+
+  protected void replaceBody(final Object newValue) {
+      this.children.clear();
+      this.children.add(newValue);
+  }
+
+  protected void appendNode(final Object newValue, final GPathResult result) {
+      if (newValue instanceof Closure) {
+          this.children.add(new ReplacementNode() {
+                              public void build(final GroovyObject builder, final Map namespaceMap, final Map namespaceTagHints) {
+                                  final Closure c = (Closure)((Closure)newValue).clone();
+                                  
+                                      c.setDelegate(builder);
+                                      c.call(new Object[]{result});
+                                  }
+                              });
+      } else {
+          this.children.add(newValue);
+      }
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.Node#text()
+   */
+  public String text() {
+  final StringBuffer buff = new StringBuffer();
+  final Iterator iter = this.children.iterator();
+  
+    while (iter.hasNext()) {
+    final Object child = iter.next();
+    
+        if (child instanceof Node) {
+            buff.append(((Node)child).text());
+        } else {
+            buff.append(child);
+        }
+    }
+  
+    return buff.toString();
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.Node#childNodes()
+   */
+  
+  public Iterator childNodes() {
+    return new Iterator() {
+      private final Iterator iter = Node.this.children.iterator();
+      private Object nextElementNodes = getNextElementNodes();
+      
+      public boolean hasNext() {
+        return this.nextElementNodes != null;
+      }
+      
+      public Object next() {
+        try {
+          return this.nextElementNodes;
+        } finally {
+          this.nextElementNodes = getNextElementNodes();
+        }
+      }
+      
+      public void remove() {
+        throw new UnsupportedOperationException();
+      }
+
+      private Object getNextElementNodes() {
+        while (iter.hasNext()) {
+        final Object node = iter.next();
+        
+          if (node instanceof Node) {
+            return node;
+          }
+        }
+        
+        return null;
+      }
+    };
+  }
+
+  /* (non-Javadoc)
+   * @see org.codehaus.groovy.sandbox.util.slurpersupport.Node#writeTo(java.io.Writer)
+   */
+  public Writer writeTo(final Writer out) throws IOException {
+      if (this.replacementNodeStack.empty()) {
+      final Iterator iter = this.children.iterator();
+      
+        while (iter.hasNext()) {
+        final Object child = iter.next();
+        
+          if (child instanceof Writable) {
+            ((Writable)child).writeTo(out);
+          } else {
+            out.write(child.toString());
+          }
+        }
+        
+        return out;
+        
+      } else {
+         return ((Writable)this.replacementNodeStack.peek()).writeTo(out); 
+      }
+  }
+  
+  public void build(final GroovyObject builder, final Map namespaceMap, final Map namespaceTagHints) {
+      if (this.replacementNodeStack.empty()) {
+      final Closure rest = new Closure(null) {
+                              public Object doCall(final Object o) {
+                                buildChildren(builder, namespaceMap, namespaceTagHints);
+                                
+                                return null;
+                              }
+                            };
+        
+        if (this.namespaceURI.length() == 0 && this.attributeNamespaces.isEmpty()) {
+          builder.invokeMethod(this.name, new Object[]{this.attributes, rest});
+        } else {
+          final List newTags = new LinkedList();
+          builder.getProperty("mkp");
+          final List namespaces = (List)builder.invokeMethod("getNamespaces", new Object[]{});
+          
+          final Map current = (Map)namespaces.get(0);
+          final Map pending = (Map)namespaces.get(1);
+          
+          if (this.attributeNamespaces.isEmpty()) {     
+            builder.getProperty(getTagFor(this.namespaceURI, current, pending, namespaceMap, namespaceTagHints, newTags, builder));
+            builder.invokeMethod(this.name, new Object[]{this.attributes, rest});
+          } else {
+          final Map attributesWithNamespaces = new HashMap(this.attributes);
+          final Iterator attrs = this.attributes.keySet().iterator();
+            
+            while (attrs.hasNext()) {
+            final Object key = attrs.next();
+            final Object attributeNamespaceURI = this.attributeNamespaces.get(key);
+              
+              if (attributeNamespaceURI != null) {
+                attributesWithNamespaces.put(getTagFor(attributeNamespaceURI, current, pending, namespaceMap, namespaceTagHints, newTags, builder) +
+                                             "$" + key, attributesWithNamespaces.remove(key));
+              }
+            }
+            
+            builder.getProperty(getTagFor(this.namespaceURI, current, pending, namespaceMap,namespaceTagHints,  newTags, builder));
+            builder.invokeMethod(this.name, new Object[]{attributesWithNamespaces, rest});
+          }
+          
+          // remove the new tags we had to define for this element
+          if (!newTags.isEmpty()) {
+          final Iterator iter = newTags.iterator();
+          
+            do {
+              pending.remove(iter.next());
+            } while (iter.hasNext());
+          }  
+        }   
+      } else {
+          ((ReplacementNode)this.replacementNodeStack.peek()).build(builder, namespaceMap, namespaceTagHints);
+      }
+  }
+  
+  private static String getTagFor(final Object namespaceURI, final Map current,
+                                  final Map pending, final Map local, final Map tagHints,
+                                  final List newTags, final GroovyObject builder) {
+  String tag = findNamespaceTag(pending, namespaceURI); // look in the namespaces whose declaration has already been emitted
+    
+    if (tag == null) {
+      tag = findNamespaceTag(current, namespaceURI);  // look in the namespaces who will be declared at the next element
+      
+      if (tag == null) {
+        // we have to declare the namespace - choose a tag
+        tag = findNamespaceTag(local, namespaceURI);  // If the namespace has been decared in the GPath expression use that tag
+        
+        if (tag == null || tag.length() == 0) {
+          tag = findNamespaceTag(tagHints, namespaceURI);  // If the namespace has been used in the parse documant use that tag         
+        }
+        
+        if (tag == null || tag.length() == 0) { // otherwise make up a new tag and check it has not been used before
+        int suffix = 0;
+        
+          do {
+            final String posibleTag = "tag" + suffix++;
+            
+            if (!pending.containsKey(posibleTag) && !current.containsKey(posibleTag) && !local.containsKey(posibleTag)) {
+              tag = posibleTag;
+            }
+          } while (tag == null);
+        }
+        
+        final Map newNamespace = new HashMap();
+        newNamespace.put(tag, namespaceURI);
+        builder.getProperty("mkp");
+        builder.invokeMethod("declareNamespace", new Object[]{newNamespace});
+        newTags.add(tag);
+      }
+    }
+    
+    return tag;
+  }
+  
+  private static String findNamespaceTag(final Map tagMap, final Object namespaceURI) {
+    if (tagMap.containsValue(namespaceURI)) {
+    final Iterator entries = tagMap.entrySet().iterator();
+      
+      while (entries.hasNext()) {
+        final Map.Entry entry = (Map.Entry)entries.next();
+        
+        if (namespaceURI.equals(entry.getValue())) {
+          return (String)entry.getKey();
+        }
+      }
+    }
+    
+    return null;
+  }
+  
+  private void buildChildren(final GroovyObject builder, final Map namespaceMap, final Map namespaceTagHints) {
+  final Iterator iter = this.children.iterator();
+  
+    while (iter.hasNext()) {
+    final Object child = iter.next();
+    
+      if (child instanceof Node) {
+        ((Node)child).build(builder, namespaceMap, namespaceTagHints);
+      } else if (child instanceof Buildable) {
+        ((Buildable)child).build(builder);
+      } else {
+        builder.getProperty("mkp");
+        builder.invokeMethod("yield", new Object[]{child});
+      }
+    }
+  }
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/NodeChild.java b/groovy/src/main/groovy/util/slurpersupport/NodeChild.java
new file mode 100644
index 0000000..11ccc3e
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/NodeChild.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util.slurpersupport;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyRuntimeException;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+/**
+ * @author John Wilson
+ */
+
+public class NodeChild extends GPathResult {
+    private final Node node;
+
+    public NodeChild(final Node node, final GPathResult parent, final String namespacePrefix, final Map namespaceTagHints) {
+        super(parent, node.name(), namespacePrefix, namespaceTagHints);
+        this.node = node;
+    }
+
+    public NodeChild(final Node node, final GPathResult parent, final Map namespaceTagHints) {
+        this(node, parent, "*", namespaceTagHints);
+    }
+
+    public int size() {
+        return 1;
+    }
+
+    public String text() {
+        return this.node.text();
+    }
+
+    public GPathResult parents() {
+        // TODO Auto-generated method stub
+        throw new GroovyRuntimeException("parents() not implemented yet");
+    }
+
+    public Iterator iterator() {
+        return new Iterator() {
+            private boolean hasNext = true;
+
+            public boolean hasNext() {
+                return this.hasNext;
+            }
+
+            public Object next() {
+                try {
+                    return (this.hasNext) ? NodeChild.this : null;
+                } finally {
+                    this.hasNext = false;
+                }
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public Iterator nodeIterator() {
+        return new Iterator() {
+            private boolean hasNext = true;
+
+            public boolean hasNext() {
+                return this.hasNext;
+            }
+
+            public Object next() {
+                try {
+                    return (this.hasNext) ? NodeChild.this.node : null;
+                } finally {
+                    this.hasNext = false;
+                }
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public Object getAt(final int index) {
+        if (index == 0) {
+            return node;
+        } else {
+            throw new ArrayIndexOutOfBoundsException(index);
+        }
+    }
+
+    public Map attributes() {
+        return this.node.attributes();
+    }
+
+    public Iterator childNodes() {
+        return this.node.childNodes();
+    }
+
+    public GPathResult find(final Closure closure) {
+        if (DefaultTypeTransformation.castToBoolean(closure.call(new Object[]{this.node}))) {
+            return this;
+        } else {
+            return new NoChildren(this, "", this.namespaceTagHints);
+        }
+    }
+
+    public GPathResult findAll(final Closure closure) {
+        return find(closure);
+    }
+
+    public void build(final GroovyObject builder) {
+        this.node.build(builder, this.namespaceMap, this.namespaceTagHints);
+    }
+
+    public Writer writeTo(final Writer out) throws IOException {
+        return this.node.writeTo(out);
+    }
+
+    protected void replaceNode(final Closure newValue) {
+        this.node.replaceNode(newValue, this);
+    }
+
+    protected void replaceBody(final Object newValue) {
+        this.node.replaceBody(newValue);
+    }
+
+    protected void appendNode(final Object newValue) {
+        this.node.appendNode(newValue, this);
+    }
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/NodeChildren.java b/groovy/src/main/groovy/util/slurpersupport/NodeChildren.java
new file mode 100644
index 0000000..05da65b
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/NodeChildren.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util.slurpersupport;
+
+import groovy.lang.Buildable;
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyRuntimeException;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+/**
+ * @author John Wilson
+ */
+
+class NodeChildren extends GPathResult {
+    private int size = -1;
+
+    /**
+     * @param parent
+     * @param name
+     * @param namespacePrefix
+     * @param namespaceTagHints
+     */
+    public NodeChildren(final GPathResult parent, final String name, final String namespacePrefix, final Map namespaceTagHints) {
+        super(parent, name, namespacePrefix, namespaceTagHints);
+    }
+
+    /**
+     * @param parent
+     * @param name
+     * @param namespaceTagHints
+     */
+    public NodeChildren(final GPathResult parent, final String name, final Map namespaceTagHints) {
+        this(parent, name, "*", namespaceTagHints);
+    }
+
+    /**
+     * @param parent
+     * @param namespaceTagHints
+     */
+    public NodeChildren(final GPathResult parent, final Map namespaceTagHints) {
+        this(parent, "*", namespaceTagHints);
+    }
+
+    public Iterator childNodes() {
+        return new Iterator() {
+            private final Iterator iter = parent.childNodes();
+            private Iterator childIter = nextChildIter();
+
+            /* (non-Javadoc)
+            * @see java.util.Iterator#hasNext()
+            */
+            public boolean hasNext() {
+                return childIter != null;
+            }
+
+            /* (non-Javadoc)
+            * @see java.util.Iterator#next()
+            */
+            public Object next() {
+                while (childIter != null) {
+                    try {
+                        if (childIter.hasNext()) {
+                            return childIter.next();
+                        }
+                    } finally {
+                        if (!childIter.hasNext()) {
+                            childIter = nextChildIter();
+                        }
+                    }
+                }
+                return null;
+            }
+
+            /* (non-Javadoc)
+            * @see java.util.Iterator#remove()
+            */
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+            private Iterator nextChildIter() {
+                while (iter.hasNext()) {
+                    final Node node = (Node) iter.next();
+                    if (name.equals(node.name()) || name.equals("*")) {
+                        final Iterator result = node.childNodes();
+                        if (result.hasNext()) {
+                            if ("*".equals(namespacePrefix) ||
+                                    ("".equals(namespacePrefix) && "".equals(node.namespaceURI())) ||
+                                    node.namespaceURI().equals(namespaceMap.get(namespacePrefix))) {
+                                return result;
+                            }
+                        }
+                    }
+                }
+                return null;
+            }
+        };
+    }
+
+    public Iterator iterator() {
+        return new Iterator() {
+            final Iterator iter = nodeIterator();
+
+            public boolean hasNext() {
+                return iter.hasNext();
+            }
+
+            public Object next() {
+                return new NodeChild((Node) iter.next(), parent, namespaceTagHints);
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    public Iterator nodeIterator() {
+        if ("*".equals(name)) {
+            return parent.childNodes();
+        } else {
+            return new NodeIterator(parent.childNodes()) {
+                /* (non-Javadoc)
+                * @see org.codehaus.groovy.sandbox.util.slurpersupport.NodeIterator#getNextNode(java.util.Iterator)
+                */
+                protected Object getNextNode(Iterator iter) {
+                    while (iter.hasNext()) {
+                        final Node node = (Node) iter.next();
+                        if (name.equals(node.name())) {
+                            if ("*".equals(namespacePrefix) ||
+                                    ("".equals(namespacePrefix) && "".equals(node.namespaceURI())) ||
+                                    node.namespaceURI().equals(namespaceMap.get(namespacePrefix))) {
+                                return node;
+                            }
+                        }
+                    }
+                    return null;
+                }
+            };
+        }
+    }
+
+    public GPathResult parents() {
+        // TODO Auto-generated method stub
+        throw new GroovyRuntimeException("parents() not implemented yet");
+    }
+
+    public synchronized int size() {
+        if (this.size == -1) {
+            final Iterator iter = iterator();
+            this.size = 0;
+            while (iter.hasNext()) {
+                iter.next();
+                this.size++;
+            }
+        }
+        return this.size;
+    }
+
+    public String text() {
+        final StringBuffer buf = new StringBuffer();
+        final Iterator iter = nodeIterator();
+        while (iter.hasNext()) {
+            buf.append(((Node) iter.next()).text());
+        }
+        return buf.toString();
+    }
+
+    public GPathResult find(final Closure closure) {
+        final Iterator iter = iterator();
+        while (iter.hasNext()) {
+            final Object node = iter.next();
+            if (DefaultTypeTransformation.castToBoolean(closure.call(new Object[]{node}))) {
+                return (GPathResult) node;
+            }
+        }
+        return new NoChildren(this, this.name, namespaceTagHints);
+    }
+
+    public GPathResult findAll(final Closure closure) {
+        return new FilteredNodeChildren(this, closure, namespaceTagHints);
+    }
+
+    public void build(final GroovyObject builder) {
+        final Iterator iter = nodeIterator();
+        while (iter.hasNext()) {
+            final Object next = iter.next();
+            if (next instanceof Buildable) {
+                ((Buildable) next).build(builder);
+            } else {
+                ((Node) next).build(builder, namespaceMap, namespaceTagHints);
+            }
+        }
+    }
+
+    /* (non-Javadoc)
+    * @see groovy.lang.Writable#writeTo(java.io.Writer)
+    */
+    public Writer writeTo(final Writer out) throws IOException {
+        final Iterator iter = nodeIterator();
+        while (iter.hasNext()) {
+            ((Node) iter.next()).writeTo(out);
+        }
+        return out;
+    }
+
+    protected void replaceNode(final Closure newValue) {
+        final Iterator iter = iterator();
+        while (iter.hasNext()) {
+            final NodeChild result = (NodeChild) iter.next();
+            result.replaceNode(newValue);
+        }
+    }
+
+    protected void replaceBody(final Object newValue) {
+        final Iterator iter = iterator();
+        while (iter.hasNext()) {
+            final NodeChild result = (NodeChild) iter.next();
+            result.replaceBody(newValue);
+        }
+    }
+
+    protected void appendNode(final Object newValue) {
+        final Iterator iter = iterator();
+        while (iter.hasNext()) {
+            final NodeChild result = (NodeChild) iter.next();
+            result.appendNode(newValue);
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/NodeIterator.java b/groovy/src/main/groovy/util/slurpersupport/NodeIterator.java
new file mode 100644
index 0000000..862125d
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/NodeIterator.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.util.slurpersupport;
+
+import java.util.Iterator;
+
+/**
+ * @author John Wilson
+ *
+ */
+public abstract class NodeIterator implements Iterator {
+    private static final Object DELAYED_INIT = new Object();
+    private final Iterator iter;
+    private Object nextNode;
+
+    public NodeIterator(final Iterator iter) {
+        this.iter = iter;
+        this.nextNode = DELAYED_INIT;
+    }
+
+    private void initNextNode(){
+        if (nextNode==DELAYED_INIT) nextNode = getNextNode(iter);
+    }
+
+    public boolean hasNext() {
+        initNextNode();
+        return this.nextNode != null;
+    }
+
+    public Object next() {
+        initNextNode();
+        try {
+            return this.nextNode;
+        } finally {
+            this.nextNode = getNextNode(this.iter);
+        }
+    }
+
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+    protected abstract Object getNextNode(final Iterator iter);
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/ReplacementNode.java b/groovy/src/main/groovy/util/slurpersupport/ReplacementNode.java
new file mode 100644
index 0000000..65a27d4
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/ReplacementNode.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util.slurpersupport;
+
+import groovy.lang.Buildable;
+import groovy.lang.GroovyObject;
+import groovy.lang.Writable;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Map;
+
+public abstract class ReplacementNode implements Buildable, Writable {
+    public abstract void build(GroovyObject builder, Map namespaceMap, Map namespaceTagHints);
+    
+    public void build(final GroovyObject builder) {
+        build(builder, null, null);
+    }
+    
+    public Writer writeTo(final Writer out) throws IOException {
+        return out;
+    }
+}
diff --git a/groovy/src/main/groovy/util/slurpersupport/package.html b/groovy/src/main/groovy/util/slurpersupport/package.html
new file mode 100644
index 0000000..ca52df9
--- /dev/null
+++ b/groovy/src/main/groovy/util/slurpersupport/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.util.slurpersupport.*</title>
+  </head>
+  <body>
+    <p>Helper classes for XmlSlurper.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/xml/DOMBuilder.java b/groovy/src/main/groovy/xml/DOMBuilder.java
new file mode 100644
index 0000000..8ebc76c
--- /dev/null
+++ b/groovy/src/main/groovy/xml/DOMBuilder.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.xml;
+
+import groovy.util.BuilderSupport;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * A helper class for creating a W3C DOM tree
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DOMBuilder extends BuilderSupport {
+
+    Document document;
+    DocumentBuilder documentBuilder;
+
+    public static DOMBuilder newInstance() throws ParserConfigurationException {
+        return newInstance(false, true);
+    }
+
+    public static DOMBuilder newInstance(boolean validating, boolean namespaceAware) throws ParserConfigurationException {
+        DocumentBuilderFactory factory = FactorySupport.createDocumentBuilderFactory();
+        factory.setNamespaceAware(namespaceAware);
+        factory.setValidating(validating);
+        return new DOMBuilder(factory.newDocumentBuilder());
+    }
+
+    public static Document parse(Reader reader) throws SAXException, IOException, ParserConfigurationException {
+        return parse(reader, false, true);
+    }
+
+    public static Document parse(Reader reader, boolean validating, boolean namespaceAware)
+            throws SAXException, IOException, ParserConfigurationException {
+        DocumentBuilderFactory factory = FactorySupport.createDocumentBuilderFactory();
+        factory.setNamespaceAware(namespaceAware);
+        factory.setValidating(validating);
+        DocumentBuilder documentBuilder = factory.newDocumentBuilder();
+        return documentBuilder.parse(new InputSource(reader));
+    }
+
+    public DOMBuilder(Document document) {
+        this.document = document;
+    }
+
+    public DOMBuilder(DocumentBuilder documentBuilder) {
+        this.documentBuilder = documentBuilder;
+    }
+
+    protected void setParent(Object parent, Object child) {
+        Node current = (Node) parent;
+        Node node = (Node) child;
+
+        current.appendChild(node);
+    }
+
+    protected Object createNode(Object name) {
+        if (document == null) {
+            document = createDocument();
+        }
+        if (name instanceof QName) {
+            QName qname = (QName) name;
+            return document.createElementNS(qname.getNamespaceURI(), qname.getQualifiedName());
+        } else {
+            return document.createElement(name.toString());
+        }
+    }
+
+    protected Document createDocument() {
+        if (documentBuilder == null) {
+            throw new IllegalArgumentException("No Document or DOMImplementation available so cannot create Document");
+        } else {
+            return documentBuilder.newDocument();
+        }
+    }
+
+    protected Object createNode(Object name, Object value) {
+        Element element = (Element) createNode(name);
+        element.appendChild(document.createTextNode(value.toString()));
+        return element;
+    }
+
+    protected Object createNode(Object name, Map attributes, Object value) {
+        Element element = (Element) createNode(name, attributes);
+        element.appendChild(document.createTextNode(value.toString()));
+        return element;
+    }
+
+    protected Object createNode(Object name, Map attributes) {
+        Element element = (Element) createNode(name);
+        for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            String attrName = entry.getKey().toString();
+            Object value = entry.getValue();
+            if ("xmlns".equals(attrName)) {
+                if (value instanceof Map) {
+                    appendNamespaceAttributes(element, (Map) value);
+                } else {
+                    throw new IllegalArgumentException("The value of the xmlns attribute must be a Map of QNames to String URIs");
+                }
+            } else {
+                String valueText = (value != null) ? value.toString() : "";
+                element.setAttribute(attrName, valueText);
+            }
+        }
+        return element;
+    }
+
+    protected void appendNamespaceAttributes(Element element, Map attributes) {
+        for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            Object key = entry.getKey();
+            Object value = entry.getValue();
+            if (value == null) {
+                throw new IllegalArgumentException("The value of key: " + key + " cannot be null");
+            }
+            if (key instanceof String) {
+                String prefix = (String) key;
+
+                //System.out.println("Creating namespace for prefix: " + prefix + " with value: " + value);
+
+                //element.setAttributeNS("http://www.w3.org/XML/1998/namespace", "xmlns:" + prefix, value.toString());
+                element.setAttributeNS("", prefix, value.toString());
+            } else if (key instanceof QName) {
+                QName qname = (QName) key;
+                element.setAttributeNS(qname.getNamespaceURI(), qname.getQualifiedName(), value.toString());
+            } else {
+                throw new IllegalArgumentException("The key: " + key + " should be an instanceof of " + QName.class);
+            }
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/xml/Entity.groovy b/groovy/src/main/groovy/xml/Entity.groovy
new file mode 100644
index 0000000..7ee5077
--- /dev/null
+++ b/groovy/src/main/groovy/xml/Entity.groovy
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.xml
+
+class Entity implements Buildable {
+    /*
+     * XML-compatible ISO Latin 1 Character Entity Set for XHTML
+     */
+    public static final Entity nbsp = new Entity("nbsp")        // no-break space = non-breaking space, U+00A0 ISOnum 
+    public static final Entity iexcl = new Entity("iexcl")      // inverted exclamation mark, U+00A1 ISOnum 
+    public static final Entity cent = new Entity("cent")        // cent sign, U+00A2 ISOnum 
+    public static final Entity pound = new Entity("pound")      // pound sign, U+00A3 ISOnum 
+    public static final Entity curren = new Entity("curren")    // currency sign, U+00A4 ISOnum 
+    public static final Entity yen = new Entity("yen")          // yen sign = yuan sign, U+00A5 ISOnum 
+    public static final Entity brvbar = new Entity("brvbar")    // broken bar = broken vertical bar, U+00A6 ISOnum 
+    public static final Entity sect = new Entity("sect")        // section sign, U+00A7 ISOnum 
+    public static final Entity uml = new Entity("uml")          // diaeresis = spacing diaeresis, U+00A8 ISOdia
+    public static final Entity copy = new Entity("copy")        // copyright sign, U+00A9 ISOnum 
+    public static final Entity ordf = new Entity("ordf")        // feminine ordinal indicator, U+00AA ISOnum 
+    public static final Entity laquo = new Entity("laquo")      // left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum 
+    public static final Entity not = new Entity("not")          // not sign, U+00AC ISOnum 
+    public static final Entity shy = new Entity("shy")          // soft hyphen = discretionary hyphen, U+00AD ISOnum 
+    public static final Entity reg = new Entity("reg")          // registered sign = registered trade mark sign, U+00AE ISOnum 
+    public static final Entity macr = new Entity("macr")        // macron = spacing macron = overline = APL overbar, U+00AF ISOdia 
+    public static final Entity deg = new Entity("deg")          // degree sign, U+00B0 ISOnum 
+    public static final Entity plusmn = new Entity("plusmn")    // plus-minus sign = plus-or-minus sign, U+00B1 ISOnum 
+    public static final Entity sup2 = new Entity("sup2")        // superscript two = superscript digit two = squared, U+00B2 ISOnum 
+    public static final Entity sup3 = new Entity("sup3")        // superscript three = superscript digit three = cubed, U+00B3 ISOnum 
+    public static final Entity acute = new Entity("acute")      // acute accent = spacing acute, U+00B4 ISOdia 
+    public static final Entity micro = new Entity("micro")      // micro sign, U+00B5 ISOnum 
+    public static final Entity para = new Entity("para")        // pilcrow sign = paragraph sign, U+00B6 ISOnum 
+    public static final Entity middot = new Entity("middot")    // middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum 
+    public static final Entity cedil = new Entity("cedil")      // cedilla = spacing cedilla, U+00B8 ISOdia 
+    public static final Entity sup1 = new Entity("sup1")        // superscript one = superscript digit one, U+00B9 ISOnum 
+    public static final Entity ordm = new Entity("ordm")        // masculine ordinal indicator, U+00BA ISOnum 
+    public static final Entity raquo = new Entity("raquo")      // right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum 
+    public static final Entity frac14 = new Entity("frac14")    // vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum 
+    public static final Entity frac12 = new Entity("frac12")    // vulgar fraction one half = fraction one half, U+00BD ISOnum 
+    public static final Entity frac34 = new Entity("frac34")    // vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum 
+    public static final Entity iquest = new Entity("iquest")    // inverted question mark = turned question mark, U+00BF ISOnum 
+    public static final Entity Agrave = new Entity("Agrave")    // latin capital A with grave = latin capital A grave, U+00C0 ISOlat1 
+    public static final Entity Aacute = new Entity("Aacute")    // latin capital A with acute, U+00C1 ISOlat1 
+    public static final Entity Acirc = new Entity("Acirc")      // latin capital A with circumflex, U+00C2 ISOlat1 
+    public static final Entity Atilde = new Entity("Atilde")    // latin capital A with tilde, U+00C3 ISOlat1 
+    public static final Entity Auml = new Entity("Auml")        // latin capital A with diaeresis, U+00C4 ISOlat1 
+    public static final Entity Aring = new Entity("Aring")      // latin capital A with ring above = latin capital A ring, U+00C5 ISOlat1 
+    public static final Entity AElig = new Entity("AElig")      // latin capital AE = latin capital ligature AE, U+00C6 ISOlat1 
+    public static final Entity Ccedil = new Entity("Ccedil")    // latin capital C with cedilla, U+00C7 ISOlat1 
+    public static final Entity Egrave = new Entity("Egrave")    // latin capital E with grave, U+00C8 ISOlat1 
+    public static final Entity Eacute = new Entity("Eacute")    // latin capital E with acute, U+00C9 ISOlat1 
+    public static final Entity Ecirc = new Entity("Ecirc")      // latin capital E with circumflex, U+00CA ISOlat1 
+    public static final Entity Euml = new Entity("Euml")        // latin capital E with diaeresis, U+00CB ISOlat1 
+    public static final Entity Igrave = new Entity("Igrave")    // latin capital I with grave, U+00CC ISOlat1 
+    public static final Entity Iacute = new Entity("Iacute")    // latin capital I with acute, U+00CD ISOlat1 
+    public static final Entity Icirc = new Entity("Icirc")      // latin capital I with circumflex, U+00CE ISOlat1 
+    public static final Entity Iuml = new Entity("Iuml")        // latin capital I with diaeresis, U+00CF ISOlat1 
+    public static final Entity ETH = new Entity("ETH")          // latin capital ETH, U+00D0 ISOlat1 
+    public static final Entity Ntilde = new Entity("Ntilde")    // latin capital N with tilde, U+00D1 ISOlat1 
+    public static final Entity Ograve = new Entity("Ograve")    // latin capital O with grave, U+00D2 ISOlat1 
+    public static final Entity Oacute = new Entity("Oacute")    // latin capital O with acute, U+00D3 ISOlat1 
+    public static final Entity Ocirc = new Entity("Ocirc")      // latin capital O with circumflex, U+00D4 ISOlat1 
+    public static final Entity Otilde = new Entity("Otilde")    // latin capital O with tilde, U+00D5 ISOlat1 
+    public static final Entity Ouml = new Entity("Ouml")        // latin capital O with diaeresis, U+00D6 ISOlat1 
+    public static final Entity times = new Entity("times")      // multiplication sign, U+00D7 ISOnum 
+    public static final Entity Oslash = new Entity("Oslash")    // latin capital O with stroke = latin capital O slash, U+00D8 ISOlat1 
+    public static final Entity Ugrave = new Entity("Ugrave")    // latin capital U with grave, U+00D9 ISOlat1 
+    public static final Entity Uacute = new Entity("Uacute")    // latin capital U with acute, U+00DA ISOlat1 
+    public static final Entity Ucirc = new Entity("Ucirc")      // latin capital U with circumflex, U+00DB ISOlat1 
+    public static final Entity Uuml = new Entity("Uuml")        // latin capital U with diaeresis, U+00DC ISOlat1 
+    public static final Entity Yacute = new Entity("Yacute")    // latin capital Y with acute, U+00DD ISOlat1 
+    public static final Entity THORN = new Entity("THORN")      // latin capital THORN, U+00DE ISOlat1 
+    public static final Entity szlig = new Entity("szlig")      // latin small sharp s = ess-zed, U+00DF ISOlat1 
+    public static final Entity agrave = new Entity("agrave")    // latin small a with grave = latin small a grave, U+00E0 ISOlat1 
+    public static final Entity aacute = new Entity("aacute")    // latin small a with acute, U+00E1 ISOlat1 
+    public static final Entity acirc = new Entity("acirc")      // latin small a with circumflex, U+00E2 ISOlat1 
+    public static final Entity atilde = new Entity("atilde")    // latin small a with tilde, U+00E3 ISOlat1 
+    public static final Entity auml = new Entity("auml")        // latin small a with diaeresis, U+00E4 ISOlat1 
+    public static final Entity aring = new Entity("aring")      // latin small a with ring above = latin small a ring, U+00E5 ISOlat1 
+    public static final Entity aelig = new Entity("aelig")      // latin small ae = latin small ligature ae, U+00E6 ISOlat1 
+    public static final Entity ccedil = new Entity("ccedil")    // latin small c with cedilla, U+00E7 ISOlat1 
+    public static final Entity egrave = new Entity("egrave")    // latin small e with grave, U+00E8 ISOlat1 
+    public static final Entity eacute = new Entity("eacute")    // latin small e with acute, U+00E9 ISOlat1 
+    public static final Entity ecirc = new Entity("ecirc")      // latin small e with circumflex, U+00EA ISOlat1 
+    public static final Entity euml = new Entity("euml")        // latin small e with diaeresis, U+00EB ISOlat1 
+    public static final Entity igrave = new Entity("igrave")    // latin small i with grave, U+00EC ISOlat1 
+    public static final Entity iacute = new Entity("iacute")    // latin small i with acute, U+00ED ISOlat1 
+    public static final Entity icirc = new Entity("icirc")      // latin small i with circumflex, U+00EE ISOlat1 
+    public static final Entity iuml = new Entity("iuml")        // latin small i with diaeresis, U+00EF ISOlat1 
+    public static final Entity eth = new Entity("eth")          // latin small eth, U+00F0 ISOlat1 
+    public static final Entity ntilde = new Entity("ntilde")    // latin small n with tilde, U+00F1 ISOlat1 
+    public static final Entity ograve = new Entity("ograve")    // latin small o with grave, U+00F2 ISOlat1 
+    public static final Entity oacute = new Entity("oacute")    // latin small o with acute, U+00F3 ISOlat1 
+    public static final Entity ocirc = new Entity("ocirc")      // latin small o with circumflex, U+00F4 ISOlat1 
+    public static final Entity otilde = new Entity("otilde")    // latin small o with tilde, U+00F5 ISOlat1 
+    public static final Entity ouml = new Entity("ouml")        // latin small o with diaeresis, U+00F6 ISOlat1 
+    public static final Entity divide = new Entity("divide")    // division sign, U+00F7 ISOnum 
+    public static final Entity oslash = new Entity("oslash")    // latin small o with stroke, = latin small o slash, U+00F8 ISOlat1 
+    public static final Entity ugrave = new Entity("ugrave")    // latin small u with grave, U+00F9 ISOlat1 
+    public static final Entity uacute = new Entity("uacute")    // latin small u with acute, U+00FA ISOlat1 
+    public static final Entity ucirc = new Entity("ucirc")      // latin small u with circumflex, U+00FB ISOlat1 
+    public static final Entity uuml = new Entity("uuml")        // latin small u with diaeresis, U+00FC ISOlat1 
+    public static final Entity yacute = new Entity("yacute")    // latin small y with acute, U+00FD ISOlat1 
+    public static final Entity thorn = new Entity("thorn")      // latin small thorn with, U+00FE ISOlat1 
+    public static final Entity yuml = new Entity("yuml")        // latin small y with diaeresis, U+00FF ISOlat1
+    
+    /*
+     * XML-compatible ISO Special Character Entity Set for XHTML 
+     */
+    
+    // C0 Controls and Basic Latin 
+    public static final Entity lt = new Entity("lt")            //less-than sign, U+003C ISOnum
+    public static final Entity gt = new Entity("gt")            //greater-than sign, U+003E ISOnum
+    public static final Entity amp = new Entity("amp")          //ampersand, U+0026 ISOnum
+    public static final Entity apos = new Entity("apos")        //The Apostrophe (Apostrophe Quote, APL Quote), U+0027 ISOnum
+    public static final Entity quot = new Entity("quot")        //quotation mark (Quote Double), U+0022 ISOnum
+    
+    // Latin Extended-A
+    public static final Entity OElig = new Entity("OElig")  //latin capital ligature OE, U+0152 ISOlat2
+    public static final Entity oelig = new Entity("oelig")  //latin small ligature oe, U+0153 ISOlat2
+    
+    // ligature is a misnomer, this is a separate character in some languages 
+    public static final Entity Scaron = new Entity("Scaron")    //latin capital letter S with caron, U+0160 ISOlat2
+    public static final Entity scaron = new Entity("scaron")    //latin small letter s with caron, U+0161 ISOlat2
+    public static final Entity Yuml = new Entity("Yuml")    //latin capital letter Y with diaeresis, U+0178 ISOlat2
+    
+    // Spacing Modifier Letters
+    public static final Entity circ = new Entity("circ")    //modifier letter circumflex accent, U+02C6 ISOpub
+    public static final Entity tilde = new Entity("tilde")  //small tilde, U+02DC ISOdia
+    
+    // General Punctuation
+    public static final Entity ensp = new Entity("ensp")    //en space, U+2002 ISOpub
+    public static final Entity emsp = new Entity("emsp")    //em space, U+2003 ISOpub
+    public static final Entity thinsp = new Entity("thinsp")    //thin space, U+2009 ISOpub
+    public static final Entity zwnj = new Entity("zwnj")    //zero width non-joiner, U+200C NEW RFC 2070
+    public static final Entity zwj = new Entity("zwj")  //zero width joiner, U+200D NEW RFC 2070
+    public static final Entity lrm = new Entity("lrm")  //left-to-right mark, U+200E NEW RFC 2070
+    public static final Entity rlm = new Entity("rlm")  //right-to-left mark, U+200F NEW RFC 2070
+    public static final Entity ndash = new Entity("ndash")  //en dash, U+2013 ISOpub
+    public static final Entity mdash = new Entity("mdash")  //em dash, U+2014 ISOpub
+    public static final Entity lsquo = new Entity("lsquo")  //left single quotation mark, U+2018 ISOnum
+    public static final Entity rsquo = new Entity("rsquo")  //right single quotation mark, U+2019 ISOnum
+    public static final Entity sbquo = new Entity("sbquo")  //single low-9 quotation mark, U+201A NEW
+    public static final Entity ldquo = new Entity("ldquo")  //left double quotation mark, U+201C ISOnum
+    public static final Entity rdquo = new Entity("rdquo")  //right double quotation mark, U+201D ISOnum
+    public static final Entity bdquo = new Entity("bdquo")  //double low-9 quotation mark, U+201E NEW
+    public static final Entity dagger = new Entity("dagger")    //dagger, U+2020 ISOpub
+    public static final Entity Dagger = new Entity("Dagger")    //double dagger, U+2021 ISOpub
+    public static final Entity permil = new Entity("permil")    //per mille sign, U+2030 ISOtech
+    
+    // lsaquo is proposed but not yet ISO standardized
+    public static final Entity lsaquo = new Entity("lsaquo")    //single left-pointing angle quotation mark, U+2039 ISO proposed
+    
+    // rsaquo is proposed but not yet ISO standardized
+    public static final Entity rsaquo = new Entity("rsaquo")    //single right-pointing angle quotation mark, U+203A ISO proposed
+    public static final Entity euro = new Entity("euro")    //euro sign, U+20AC NEW
+
+    
+    private final entity;
+    
+    Entity(String name) {
+        this.entity = "&$name;"
+    }
+    
+    Entity(int name) {
+        this.entity = "&#$name;"
+    }
+    
+    void build(GroovyObject builder) {
+        builder.unescaped << entity
+    }
+}
diff --git a/groovy/src/main/groovy/xml/FactorySupport.java b/groovy/src/main/groovy/xml/FactorySupport.java
new file mode 100644
index 0000000..47d8d57
--- /dev/null
+++ b/groovy/src/main/groovy/xml/FactorySupport.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.xml;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.SAXParserFactory;
+import java.security.PrivilegedExceptionAction;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+
+/**
+ * Support class for creating XML Factories
+ */
+public class FactorySupport {
+    static Object createFactory(PrivilegedExceptionAction action) throws ParserConfigurationException {
+        Object factory;
+        try {
+            factory = AccessController.doPrivileged(action);
+        } catch (PrivilegedActionException pae) {
+            Exception e = pae.getException();
+            if (e instanceof ParserConfigurationException) {
+                throw(ParserConfigurationException) e;
+            } else {
+                throw new RuntimeException(e);
+            }
+        }
+        return factory;
+    }
+
+    public static DocumentBuilderFactory createDocumentBuilderFactory() throws ParserConfigurationException {
+        return (DocumentBuilderFactory) createFactory(new PrivilegedExceptionAction() {
+            public Object run() throws ParserConfigurationException {
+                return DocumentBuilderFactory.newInstance();
+            }
+        });
+    }
+
+    public static SAXParserFactory createSaxParserFactory() throws ParserConfigurationException {
+        return (SAXParserFactory) createFactory(new PrivilegedExceptionAction() {
+                public Object run() throws ParserConfigurationException {
+                    return SAXParserFactory.newInstance();
+                }
+            });
+    }
+}
diff --git a/groovy/src/main/groovy/xml/MarkupBuilder.java b/groovy/src/main/groovy/xml/MarkupBuilder.java
new file mode 100644
index 0000000..e9b43bc
--- /dev/null
+++ b/groovy/src/main/groovy/xml/MarkupBuilder.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml;
+
+import groovy.util.BuilderSupport;
+import groovy.util.IndentPrinter;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A helper class for creating XML or HTML markup
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Stefan Matthias Aust
+ * @author <a href="mailto:scottstirling@rcn.com">Scott Stirling</a>
+ * @version $Revision$
+ */
+public class MarkupBuilder extends BuilderSupport {
+    private IndentPrinter out;
+    private boolean nospace;
+    private int state;
+    private boolean nodeIsEmpty = true;
+    private boolean useDoubleQuotes = false;
+
+    public MarkupBuilder() {
+        this(new IndentPrinter());
+    }
+
+    public MarkupBuilder(PrintWriter writer) {
+        this(new IndentPrinter(writer));
+    }
+
+    public MarkupBuilder(Writer writer) {
+        this(new IndentPrinter(new PrintWriter(writer)));
+    }
+
+    public MarkupBuilder(IndentPrinter out) {
+        this.out = out;
+    }
+
+    /**
+     * Returns <code>true</code> if attribute values are output with
+     * double quotes; <code>false</code> if single quotes are used.
+     * By default, single quotes are used.
+     * @return true if double quotes are used for attributes
+     */
+    public boolean getDoubleQuotes() {
+        return this.useDoubleQuotes;
+    }
+
+    /**
+     * Sets whether the builder outputs attribute values in double
+     * quotes or single quotes.
+     * @param useDoubleQuotes If this parameter is <code>true</code>,
+     * double quotes are used; otherwise, single quotes are.
+     */
+    public void setDoubleQuotes(boolean useDoubleQuotes) {
+        this.useDoubleQuotes = useDoubleQuotes;
+    }
+
+    protected IndentPrinter getPrinter() {
+        return this.out;
+    }
+
+    protected void setParent(Object parent, Object child) { }
+
+    public Object getMkp() {
+        return this;
+    }
+
+    public void yield(String value) {
+        if (state == 1) {
+            state = 2;
+            this.nodeIsEmpty = false;
+            out.print(">");
+        }
+        if (state == 2 || state == 3) {
+            out.print(escapeElementContent(value));
+        }
+    }
+
+    public void yieldUnescaped(String value) {
+        if (state == 1) {
+            state = 2;
+            this.nodeIsEmpty = false;
+            out.print(">");
+        }
+        if (state == 2 || state == 3) {
+            out.print(value);
+        }
+    }
+
+    protected Object createNode(Object name) {
+        this.nodeIsEmpty = true;
+        toState(1, name);
+        return name;
+    }
+
+    protected Object createNode(Object name, Object value) {
+        toState(2, name);
+        this.nodeIsEmpty = false;
+        out.print(">");
+        out.print(escapeElementContent(value.toString()));
+        return name;
+    }
+
+    protected Object createNode(Object name, Map attributes, Object value) {
+        toState(1, name);
+        for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            Object attributeValue = entry.getValue();
+            if (attributeValue != null) {
+                out.print(" ");
+                // Output the attribute name,
+                print(entry.getKey().toString());
+                // Output the attribute value within quotes. Use whichever
+                // type of quotes are currently configured.
+                out.print(this.useDoubleQuotes ? "=\"" : "='");
+                print(escapeAttributeValue(attributeValue.toString()));
+                out.print(this.useDoubleQuotes ? "\"" : "'");
+            }
+        }
+
+        if (value != null) {
+            yield(value.toString());
+        } else {
+            nodeIsEmpty = true;
+        }
+
+        return name;
+    }
+
+    protected Object createNode(Object name, Map attributes) {
+        return createNode(name, attributes, null);
+    }
+    
+    protected void nodeCompleted(Object parent, Object node) {
+        toState(3, node);
+        out.flush();
+    }
+
+    protected void print(Object node) {
+        out.print(node == null ? "null" : node.toString());
+    }
+
+    protected Object getName(String methodName) {
+        return super.getName(methodName);
+    }
+
+    /**
+     * Returns a String with special XML characters escaped as entities so that
+     * output XML is valid. Escapes the following characters as corresponding 
+     * entities:
+     * <ul>
+     *   <li>\' as &amp;apos;</li>
+     *   <li>&amp; as &amp;amp;</li>
+     *   <li>&lt; as &amp;lt;</li>
+     *   <li>&gt; as &amp;gt;</li>
+     * </ul>
+     * 
+     * @param value to be searched and replaced for XML special characters.
+     * @return value with XML characters escaped
+     * @deprecated
+     * @see #escapeXmlValue(String, boolean)
+     */
+    protected String transformValue(String value) {
+        // & has to be checked and replaced before others
+        if (value.matches(".*&.*")) {
+            value = value.replaceAll("&", "&amp;");
+        }
+        if (value.matches(".*\\'.*")) {
+            value = value.replaceAll("\\'", "&apos;");
+        }
+        if (value.matches(".*<.*")) {
+            value = value.replaceAll("<", "&lt;");
+        }
+        if (value.matches(".*>.*")) {
+            value = value.replaceAll(">", "&gt;");
+        }
+        return value;
+    }
+
+    /**
+     * Escapes a string so that it can be used directly as an XML
+     * attribute value.
+     * @param value The string to escape.
+     * @return A new string in which all characters that require escaping
+     * have been replaced with the corresponding XML entities.
+     * @see #escapeXmlValue(String, boolean)
+     */
+    private String escapeAttributeValue(String value) {
+        return escapeXmlValue(value, true);
+    }
+
+    /**
+     * Escapes a string so that it can be used directly in XML element
+     * content.
+     * @param value The string to escape.
+     * @return A new string in which all characters that require escaping
+     * have been replaced with the corresponding XML entities.
+     * @see #escapeXmlValue(String, boolean)
+     */
+    private String escapeElementContent(String value) {
+        return escapeXmlValue(value, false);
+    }
+
+    /**
+     * Escapes a string so that it can be used in XML text successfully.
+     * It replaces the following characters with the corresponding XML
+     * entities:
+     * <ul>
+     *   <li>&amp; as &amp;amp;</li>
+     *   <li>&lt; as &amp;lt;</li>
+     *   <li>&gt; as &amp;gt;</li>
+     * </ul>
+     * If the string is to be added as an attribute value, these
+     * characters are also escaped:
+     * <ul>
+     *   <li>' as &amp;apos;</li>
+     * </ul>
+     * @param value The string to escape.
+     * @param isAttrValue <code>true</code> if the string is to be used
+     * as an attribute value, otherwise <code>false</code>.
+     * @return A new string in which all characters that require escaping
+     * have been replaced with the corresponding XML entities.
+     */
+    private String escapeXmlValue(String value, boolean isAttrValue) {
+        StringBuffer buffer = new StringBuffer(value);
+        for (int i = 0, n = buffer.length(); i < n; i++) {
+            switch (buffer.charAt(i)) {
+            case '&':
+                buffer.replace(i, i + 1, "&amp;");
+
+                // We're replacing a single character by a string of
+                // length 5, so we need to update the index variable
+                // and the total length.
+                i += 4;
+                n += 4;
+                break;
+
+            case '<':
+                buffer.replace(i, i + 1, "&lt;");
+
+                // We're replacing a single character by a string of
+                // length 4, so we need to update the index variable
+                // and the total length.
+                i += 3;
+                n += 3;
+                break;
+
+            case '>':
+                buffer.replace(i, i + 1, "&gt;");
+
+                // We're replacing a single character by a string of
+                // length 4, so we need to update the index variable
+                // and the total length.
+                i += 3;
+                n += 3;
+                break;
+
+            case '"':
+                // The double quote is only escaped if the value is for
+                // an attribute and the builder is configured to output
+                // attribute values inside double quotes.
+                if (isAttrValue && this.useDoubleQuotes) {
+                    buffer.replace(i, i + 1, "&quot;");
+
+                    // We're replacing a single character by a string of
+                    // length 6, so we need to update the index variable
+                    // and the total length.
+                    i += 5;
+                    n += 5;
+                }
+                break;
+
+            case '\'':
+                // The apostrophe is only escaped if the value is for an
+                // attribute, as opposed to element content, and if the
+                // builder is configured to surround attribute values with
+                // single quotes.
+                if (isAttrValue && !this.useDoubleQuotes){
+                    buffer.replace(i, i + 1, "&apos;");
+
+                    // We're replacing a single character by a string of
+                    // length 6, so we need to update the index variable
+                    // and the total length.
+                    i += 5;
+                    n += 5;
+                }
+                break;
+
+            default:
+                break;
+            }
+        }
+
+        return buffer.toString();
+    }
+
+    private void toState(int next, Object name) {
+        switch (state) {
+            case 0:
+                switch (next) {
+                    case 1:
+                    case 2:
+                        out.print("<");
+                        print(name);
+                        break;
+                    case 3:
+                        throw new Error();
+                }
+                break;
+            case 1:
+                switch (next) {
+                    case 1:
+                    case 2:
+                        out.print(">");
+                        if (nospace) {
+                            nospace = false;
+                        } else {
+                            out.println();
+                            out.incrementIndent();
+                            out.printIndent();
+                        }
+                        out.print("<");
+                        print(name);
+                        break;
+                    case 3:
+                        if (nodeIsEmpty) {
+                            out.print(" />");
+                        }
+                        break;
+                }
+                break;
+            case 2:
+                switch (next) {
+                    case 1:
+                    case 2:
+                        out.print("<");
+                        print(name);
+                        break;
+                    case 3:
+                        out.print("</");
+                        print(name);
+                        out.print(">");
+                        break;
+                }
+                break;
+            case 3:
+                switch (next) {
+                    case 1:
+                    case 2:
+                        if (nospace) {
+                            nospace = false;
+                        } else {
+                            out.println();
+                            out.printIndent();
+                        }
+                        out.print("<");
+                        print(name);
+                        break;
+                    case 3:
+                        if (nospace) {
+                            nospace = false;
+                        } else {
+                            out.println();
+                            out.decrementIndent();
+                            out.printIndent();
+                        }
+                        out.print("</");
+                        print(name);
+                        out.print(">");
+                        break;
+                }
+                break;
+        }
+        state = next;
+    }
+}
diff --git a/groovy/src/main/groovy/xml/Namespace.java b/groovy/src/main/groovy/xml/Namespace.java
new file mode 100644
index 0000000..2b0178d
--- /dev/null
+++ b/groovy/src/main/groovy/xml/Namespace.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml;
+
+/**
+ * A simple helper class which acts as a factory of {@link QName} instances.
+ * 
+ * @version $Revision$
+ */
+public class Namespace {
+
+    private String uri;
+    private String prefix;
+
+    public Namespace() {
+    }
+
+    public Namespace(String uri) {
+        this.uri = uri;
+    }
+
+    public Namespace(String uri, String prefix) {
+        this.uri = uri;
+        this.prefix = prefix;
+    }
+
+    /**
+     * Returns the QName for the given localName.
+     * 
+     * @param localName
+     *            the local name within this
+     */
+    public QName get(String localName) {
+        if (uri != null && uri.length() > 0) {
+            if (prefix != null) {
+                return new QName(uri, localName, prefix);
+            }
+            else {
+                return new QName(uri, localName);
+            }
+        }
+        else {
+            return new QName(localName);
+        }
+    }
+
+    /**
+     * Returns the prefix mapped to this namespace
+     * 
+     * @return the prefix assigned to this namespace or null if no namespace is
+     *         mapped.
+     */
+    public String getPrefix() {
+        return prefix;
+    }
+
+    /**
+     * Returns the URI of this namespace
+     * 
+     * @return the URI of this namespace
+     */
+    public String getUri() {
+        return uri;
+    }
+
+}
diff --git a/groovy/src/main/groovy/xml/NamespaceBuilder.java b/groovy/src/main/groovy/xml/NamespaceBuilder.java
new file mode 100644
index 0000000..c8d6d58
--- /dev/null
+++ b/groovy/src/main/groovy/xml/NamespaceBuilder.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml;
+
+import groovy.util.BuilderSupport;
+
+/**
+ * A helper class for creating namespaces for GroovyMarkup
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class NamespaceBuilder {
+
+    private BuilderSupport builder;
+
+    public static NamespaceBuilderSupport newInstance(BuilderSupport builder, String uri) {
+        return new NamespaceBuilder(builder).namespace(uri);
+    }
+    
+    public static NamespaceBuilderSupport newInstance(BuilderSupport builder, String uri, String prefix) {
+        return new NamespaceBuilder(builder).namespace(uri, prefix);
+    }
+
+    public NamespaceBuilder(BuilderSupport builder) {
+        this.builder = builder;
+    }
+
+    public NamespaceBuilderSupport namespace(String uri) {
+        return namespace(uri, "");
+    }
+
+    public NamespaceBuilderSupport namespace(String uri, String prefix) {
+        return new NamespaceBuilderSupport(builder, uri, prefix);
+    }
+}
diff --git a/groovy/src/main/groovy/xml/NamespaceBuilderSupport.java b/groovy/src/main/groovy/xml/NamespaceBuilderSupport.java
new file mode 100644
index 0000000..4c91def
--- /dev/null
+++ b/groovy/src/main/groovy/xml/NamespaceBuilderSupport.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml;
+
+import java.util.Map;
+
+import groovy.util.BuilderSupport;
+
+
+/**
+ * A helper class for creating namespaced GroovyMarkup
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class NamespaceBuilderSupport extends BuilderSupport {
+
+    private final Object builder;
+    private final String uri;
+    private final String prefix;
+
+    public NamespaceBuilderSupport(BuilderSupport builder, String uri) {
+        this(builder, uri, "");
+    }
+
+    public NamespaceBuilderSupport(BuilderSupport builder, String uri, String prefix) {
+        super(builder);
+        this.builder = builder;
+        this.uri = uri;
+        this.prefix = prefix;
+    }
+
+    protected void setParent(Object parent, Object child) {
+    }
+
+    protected Object getName(String methodName) {
+        return new QName(uri, methodName, prefix);
+    }
+
+    protected Object createNode(Object name) {
+        return name;
+    }
+
+    protected Object createNode(Object name, Object value) {
+        return name;
+    }
+
+    protected Object createNode(Object name, Map attributes) {
+        return name;
+    }
+
+    protected Object createNode(Object name, Map attributes, Object value) {
+        return name;
+    }
+}
diff --git a/groovy/src/main/groovy/xml/QName.java b/groovy/src/main/groovy/xml/QName.java
new file mode 100644
index 0000000..e07986f
--- /dev/null
+++ b/groovy/src/main/groovy/xml/QName.java
@@ -0,0 +1,301 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
+
+/**
+ * <code>QName</code> class represents the value of a qualified name
+ * as specified in <a href=" http://www.w3.org/TR/xmlschema-2/#QName ">XML
+ * Schema Part2: Datatypes specification</a>.
+ * <p>
+ * The value of a QName contains a <b>namespaceURI</b>, a <b>localPart</b> and a <b>prefix</b>.
+ * The localPart provides the local part of the qualified name. The
+ * namespaceURI is a URI reference identifying the namespace.
+ *
+ * @version 1.1
+ */
+public class QName implements Serializable {
+
+    /** comment/shared empty string */
+    private static final String EMPTY_STRING = "".intern();
+
+    /** Field namespaceURI */
+    private String namespaceURI;
+
+    /** Field localPart */
+    private String localPart;
+
+    /** Field prefix */
+    private String prefix;
+
+    /**
+     * Constructor for the QName.
+     *
+     * @param localPart Local part of the QName
+     */
+    public QName(String localPart) {
+        this(EMPTY_STRING, localPart, EMPTY_STRING);
+    }
+
+    /**
+     * Constructor for the QName.
+     *
+     * @param namespaceURI Namespace URI for the QName
+     * @param localPart Local part of the QName.
+     */
+    public QName(String namespaceURI, String localPart) {
+        this(namespaceURI, localPart, EMPTY_STRING);
+    }
+
+    /**
+     * Constructor for the QName.
+     *
+     * @param namespaceURI Namespace URI for the QName
+     * @param localPart Local part of the QName.
+     * @param prefix Prefix of the QName.
+     */
+    public QName(String namespaceURI, String localPart, String prefix) {
+        this.namespaceURI = (namespaceURI == null)
+                ? EMPTY_STRING
+                : namespaceURI.intern();
+        if (localPart == null) {
+            throw new IllegalArgumentException("invalid QName local part");
+        } else {
+            this.localPart = localPart.intern();
+        }
+
+        if (prefix == null) {
+            throw new IllegalArgumentException("invalid QName prefix");
+        } else {
+            this.prefix = prefix.intern();
+        }
+    }
+
+    /**
+     * Gets the Namespace URI for this QName
+     *
+     * @return Namespace URI
+     */
+    public String getNamespaceURI() {
+        return namespaceURI;
+    }
+
+    /**
+     * Gets the Local part for this QName
+     *
+     * @return Local part
+     */
+    public String getLocalPart() {
+        return localPart;
+    }
+
+    /**
+     * Gets the Prefix for this QName
+     *
+     * @return Prefix
+     */
+    public String getPrefix() {
+        return prefix;
+    }
+
+    /**
+     * Returns the fully qualified name of this QName
+     *
+     * @return  a string representation of the QName
+     */
+    public String getQualifiedName() {
+
+        return ((prefix.equals(EMPTY_STRING))
+                ? localPart
+                : prefix + ':' + localPart);
+    }
+
+    /**
+     * Returns a string representation of this QName
+     *
+     * @return  a string representation of the QName
+     */
+    public String toString() {
+
+        return ((namespaceURI.equals(EMPTY_STRING))
+                ? localPart
+                : '{' + namespaceURI + '}' + localPart);
+    }
+
+    /**
+     * Tests this QName for equality with another object.
+     * <p>
+     * If the given object is not a QName or String equivalent or is null then this method
+     * returns <tt>false</tt>.
+     * <p>
+     * For two QNames to be considered equal requires that both
+     * localPart and namespaceURI must be equal. This method uses
+     * <code>String.equals</code> to check equality of localPart
+     * and namespaceURI. Any class that extends QName is required
+     * to satisfy this equality contract.
+     *
+     * If the supplied object is a String, then it is split in two on the last colon
+     * and the first half is compared against the prefix || namespaceURI
+     * and the second half is compared against the localPart
+     *
+     * i.e. assert new QName("namespace","localPart").equals("namespace:localPart")
+     *
+     * Intended Usage: for gpath accessors, e.g. root.'urn:mynamespace:node'
+     *
+     * Warning: this equivalence is not commutative,
+     * i.e. qname.equals(string) may be true/false  but string.equals(qname) is always false
+     *
+     * <p>
+     * This method satisfies the general contract of the <code>Object.equals</code> method.
+     *
+     * @param o the reference object with which to compare
+     *
+     * @return <code>true</code> if the given object is identical to this
+     *      QName: <code>false</code> otherwise.
+     */
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null) return false;
+        if (o instanceof QName) {
+            final QName qName = (QName) o;
+            if (!namespaceURI.equals(qName.namespaceURI)) return false;
+            return localPart.equals(qName.localPart);
+
+        } else if (o instanceof String) {
+            final String string = (String)o;
+            if (string.length() == 0) return false;
+            int lastColonIndex = string.lastIndexOf(":");
+            if (lastColonIndex < 0 || lastColonIndex == string.length() - 1) return false;
+            final String stringPrefix = string.substring(0,lastColonIndex);
+            final String stringLocalPart = string.substring(lastColonIndex + 1);
+            if (stringPrefix.equals(prefix) || stringPrefix.equals(namespaceURI)) {
+                return localPart.equals(stringLocalPart);
+            }
+            return false;
+        }
+        return false;
+    }
+
+    /**
+     * Tests if this QName matches another object.
+     * <p>
+     * If the given object is not a QName or String equivalent or is null then this method
+     * returns <tt>false</tt>.
+     * <p>
+     * For two QNames to be considered matching requires that both
+     * localPart and namespaceURI must be equal or one of them is a wildcard.
+     *
+     * If the supplied object is a String, then it is split in two on the last colon
+     * and the first half is matched against the prefix || namespaceURI
+     * and the second half is matched against the localPart
+     *
+     * @param o the reference object with which to compare
+     *
+     * @return <code>true</code> if the given object matches
+     * this QName: <code>false</code> otherwise.
+     */
+    public boolean matches(Object o) {
+        if (this == o) return true;
+        if (o == null) return false;
+        if (o instanceof QName) {
+            final QName qName = (QName) o;
+            if (!namespaceURI.equals(qName.namespaceURI) && !namespaceURI.equals("*") && !qName.namespaceURI.equals("*")) return false;
+            return localPart.equals(qName.localPart) || localPart.equals("*") || qName.localPart.equals("*");
+        } else if (o instanceof String) {
+            final String string = (String)o;
+            if (string.length() == 0) return false;
+            // try matching against 'prefix:localname'
+            int lastColonIndex = string.lastIndexOf(":");
+            if (lastColonIndex < 0 || lastColonIndex == string.length() - 1) return false;
+            final String stringPrefix = string.substring(0,lastColonIndex);
+            final String stringLocalPart = string.substring(lastColonIndex + 1);
+            if (stringPrefix.equals(prefix) || stringPrefix.equals(namespaceURI) || stringPrefix.equals("*")) {
+                return localPart.equals(stringLocalPart) || stringLocalPart.equals("*");
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns a QName holding the value of the specified String.
+     * <p>
+     * The string must be in the form returned by the QName.toString()
+     * method, i.e. "{namespaceURI}localPart", with the "{namespaceURI}"
+     * part being optional.
+     * <p>
+     * This method doesn't do a full validation of the resulting QName.
+     * In particular, it doesn't check that the resulting namespace URI
+     * is a legal URI (per RFC 2396 and RFC 2732), nor that the resulting
+     * local part is a legal NCName per the XML Namespaces specification.
+     *
+     * @param s the string to be parsed
+     * @throws java.lang.IllegalArgumentException If the specified String cannot be parsed as a QName
+     * @return QName corresponding to the given String
+     */
+    public static QName valueOf(String s) {
+
+        if ((s == null) || s.equals("")) {
+            throw new IllegalArgumentException("invalid QName literal");
+        }
+
+        if (s.charAt(0) == '{') {
+            int i = s.indexOf('}');
+
+            if (i == -1) {
+                throw new IllegalArgumentException("invalid QName literal");
+            }
+
+            if (i == s.length() - 1) {
+                throw new IllegalArgumentException("invalid QName literal");
+            } else {
+                return new QName(s.substring(1, i), s.substring(i + 1));
+            }
+        } else {
+            return new QName(s);
+        }
+    }
+
+    /**
+     * Returns a hash code value for this QName object. The hash code
+     * is based on both the localPart and namespaceURI parts of the
+     * QName. This method satisfies the  general contract of the
+     * <code>Object.hashCode</code> method.
+     *
+     * @return a hash code value for this Qname object
+     */
+    public int hashCode() {
+        int result;
+        result = namespaceURI.hashCode();
+        result = 29 * result + localPart.hashCode();
+        return result;
+    }
+
+    /**
+     * Ensure that deserialization properly interns the results.
+     * @param in the ObjectInputStream to be read
+     */
+    private void readObject(ObjectInputStream in) throws
+            IOException, ClassNotFoundException {
+        in.defaultReadObject();
+
+        namespaceURI = namespaceURI.intern();
+        localPart = localPart.intern();
+        prefix = prefix.intern();
+    }
+} 
\ No newline at end of file
diff --git a/groovy/src/main/groovy/xml/SAXBuilder.java b/groovy/src/main/groovy/xml/SAXBuilder.java
new file mode 100644
index 0000000..273d681
--- /dev/null
+++ b/groovy/src/main/groovy/xml/SAXBuilder.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml;
+
+import groovy.util.BuilderSupport;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * A helper class for creating a W3C D
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class SAXBuilder extends BuilderSupport {
+
+    private ContentHandler handler;
+    private Attributes emptyAttributes = new AttributesImpl();
+
+    public SAXBuilder(ContentHandler handler) {
+        this.handler = handler;
+    }
+
+    protected void setParent(Object parent, Object child) {
+    }
+
+    protected Object createNode(Object name) {
+        doStartElement(name, emptyAttributes);
+        return name;
+    }
+
+    protected Object createNode(Object name, Object value) {
+        doStartElement(name, emptyAttributes);
+        doText(value);
+        return name;
+    }
+
+    /**
+     * @param value
+     */
+    private void doText(Object value) {
+        try {
+            char[] text = value.toString().toCharArray();
+            handler.characters(text, 0, text.length);
+        }
+        catch (SAXException e) {
+            handleException(e);
+        }
+    }
+
+    protected Object createNode(Object name, Map attributeMap, Object text) {
+        AttributesImpl attributes = new AttributesImpl();
+        for (Iterator iter = attributeMap.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            Object key = entry.getKey();
+            Object value = entry.getValue();
+            String uri = "";
+            String localName = null;
+            String qualifiedName = "";
+            String valueText = (value != null) ? value.toString() : "";
+            if (key instanceof QName) {
+                QName qname = (QName) key;
+                uri = qname.getNamespaceURI();
+                localName = qname.getLocalPart();
+                qualifiedName = qname.getQualifiedName();
+            }
+            else {
+                localName = key.toString();
+                qualifiedName = localName;
+            }
+
+            attributes.addAttribute(uri, localName, qualifiedName, "CDATA", valueText);
+        }
+        doStartElement(name, attributes);
+        if (text != null) {
+            doText(text);
+        }
+        return name;
+    }
+
+    protected void doStartElement(Object name, Attributes attributes) {
+        String uri = "";
+        String localName = null;
+        String qualifiedName = "";
+        if (name instanceof QName) {
+            QName qname = (QName) name;
+            uri = qname.getNamespaceURI();
+            localName = qname.getLocalPart();
+            qualifiedName = qname.getQualifiedName();
+        }
+        else {
+            localName = name.toString();
+            qualifiedName = localName;
+        }
+        try {
+            handler.startElement(uri, localName, qualifiedName, attributes);
+        }
+        catch (SAXException e) {
+            handleException(e);
+        }
+    }
+
+    protected void nodeCompleted(Object parent, Object name) {
+        String uri = "";
+        String localName = null;
+        String qualifiedName = "";
+        if (name instanceof QName) {
+            QName qname = (QName) name;
+            uri = qname.getNamespaceURI();
+            localName = qname.getLocalPart();
+            qualifiedName = qname.getQualifiedName();
+        }
+        else {
+            localName = name.toString();
+            qualifiedName = localName;
+        }
+        try {
+            handler.endElement(uri, localName, qualifiedName);
+        }
+        catch (SAXException e) {
+            handleException(e);
+        }
+    }
+
+    protected void handleException(SAXException e) {
+        throw new RuntimeException(e);
+    }
+
+    /* (non-Javadoc)
+     * @see groovy.util.BuilderSupport#createNode(java.lang.Object, java.util.Map, java.lang.Object)
+     */
+    protected Object createNode(Object name, Map attributes) {
+        return createNode(name, attributes, null);
+    }
+}
diff --git a/groovy/src/main/groovy/xml/StreamingDOMBuilder.groovy b/groovy/src/main/groovy/xml/StreamingDOMBuilder.groovy
new file mode 100644
index 0000000..097abdc
--- /dev/null
+++ b/groovy/src/main/groovy/xml/StreamingDOMBuilder.groovy
@@ -0,0 +1,202 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.xml
+
+import javax.xml.parsers.DocumentBuilderFactory
+import org.w3c.dom.Node
+
+import groovy.xml.streamingmarkupsupport.AbstractStreamingBuilder
+import groovy.xml.streamingmarkupsupport.StreamingMarkupWriter
+import groovy.xml.streamingmarkupsupport.BaseMarkupBuilder
+
+class StreamingDOMBuilder extends AbstractStreamingBuilder {
+    def pendingStack = []
+    def commentClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, dom ->
+        def comment = dom.document.createComment(body)
+        if (comment != null) {
+            dom.element.appendChild(comment)
+        }
+    }
+    def piClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, dom ->
+        attrs.each {target, instruction ->
+            def pi = null
+            if (instruction instanceof Map) {
+                def buf = new StringBuffer()
+                instruction.each { name, value ->
+                    if (value.toString().contains('"')) {
+                        buf.append(" $name='$value'")
+                    } else {
+                        buf.append(" $name=\"$value\"" )
+                    }
+                }
+                pi = dom.document.createProcessingInstruction(target, buf.toString())
+            } else {
+                pi = dom.document.createProcessingInstruction(target, instruction)
+            }
+            if (pi != null) {
+                dom.element.appendChild(pi)
+            }
+        }
+    }
+    def noopClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, dom ->
+        if (body instanceof Closure) {
+            def body1 = body.clone()
+            body1.delegate = doc
+            body1(doc)
+        } else if (body instanceof Buildable) {
+            body.build(doc)
+        } else if (body != null) {
+            body.each {
+                if (it instanceof Closure) {
+                    def body1 = it.clone()
+                    body1.delegate = doc
+                    body1(doc)
+                } else if (it instanceof Buildable) {
+                    it.build(doc)
+                } else {
+                    dom.element.appendChild(dom.document.createTextNode(it))
+                }
+            }
+        }
+    }
+    def tagClosure = {tag, doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, dom ->
+        def attributes = []
+        def nsAttributes = []
+
+        attrs.each {key, value ->
+            if (key.contains('$')) {
+                def parts = key.tokenize('$')
+                def namespaceUri = null
+
+                if (namespaces.containsKey(parts[0])) {
+                    namespaceUri = namespaces[parts[0]]
+
+                    nsAttributes.add([namespaceUri, "${parts[0]}:${parts[1]}", value])
+
+                } else {
+                    throw new GroovyRuntimeException("bad attribute namespace tag in ${key}")
+                }
+            } else {
+                attributes.add([key, value])
+            }
+        }
+
+        def hiddenNamespaces = [:]
+
+        pendingNamespaces.each {key, value ->
+            hiddenNamespaces[key] = namespaces[key]
+            namespaces[key] = value
+            nsAttributes.add(["http://www.w3.org/2000/xmlns/", "xmlns:${key}", value])
+
+        }
+
+        // setup the tag info
+
+        def uri = ""
+        def qualifiedName = tag
+
+        if (prefix != "") {
+            if (namespaces.containsKey(prefix)) {
+                uri = namespaces[prefix]
+            } else if (pendingNamespaces.containsKey(prefix)) {
+                uri = pendingNamespaces[prefix]
+            } else {
+                throw new GroovyRuntimeException("Namespace prefix: ${prefix} is not bound to a URI")
+            }
+            if (prefix != ":") {
+                qualifiedName = prefix + ":" + tag
+            }
+        }
+
+        def element = dom.document.createElementNS(uri, qualifiedName)
+
+        nsAttributes.each {
+            element.setAttributeNS(it[0], it[1], it[2])
+        }
+        attributes.each {
+            element.setAttribute(it[0], it[1])
+        }
+
+        dom.element.appendChild(element)
+        dom.element = element
+
+        if (body != null) {
+            pendingStack.add pendingNamespaces.clone()
+            pendingNamespaces.clear()
+
+            if (body instanceof Closure) {
+                def body1 = body.clone()
+
+                body1.delegate = doc
+                body1(doc)
+            } else if (body instanceof Buildable) {
+                body.build(doc)
+            } else {
+                body.each {
+                    if (it instanceof Closure) {
+                        def body1 = it.clone()
+
+                        body1.delegate = doc
+                        body1(doc)
+                    } else if (it instanceof Buildable) {
+                        it.build(doc)
+                    } else {
+                        dom.element.appendChild(dom.document.createTextNode(it))
+                    }
+                }
+            }
+
+            pendingNamespaces.clear()
+            pendingNamespaces.putAll pendingStack.pop()
+        }
+
+        dom.element = dom.element.getParentNode()
+
+        hiddenNamespaces.each { key, value ->
+            if (value == null) namespaces.remove key
+            else namespaces[key] = value
+        }
+    }
+
+    def builder = null
+
+    StreamingDOMBuilder() {
+        specialTags.putAll(['yield':noopClosure,
+                            'yieldUnescaped':noopClosure,
+                            'comment':commentClosure,
+                            'pi':piClosure])
+        def nsSpecificTags = [':'                                          : [tagClosure, tagClosure, [:]],    // the default namespace
+                          'http://www.w3.org/XML/1998/namespace'           : [tagClosure, tagClosure, [:]],
+                          'http://www.codehaus.org/Groovy/markup/keywords' : [badTagClosure, tagClosure, specialTags]]
+        this.builder = new BaseMarkupBuilder(nsSpecificTags)
+    }
+
+    def bind(closure) {
+        def boundClosure = this.builder.bind(closure)
+        return {
+            if (it instanceof Node) {
+                def document = it.getOwnerDocument()
+                boundClosure.trigger = ['document' : document, 'element' : it]
+                return document
+            } else {
+                def newDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument()
+                boundClosure.trigger = ['document' : newDocument, 'element' : newDocument]
+                return newDocument
+            }
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/xml/StreamingMarkupBuilder.groovy b/groovy/src/main/groovy/xml/StreamingMarkupBuilder.groovy
new file mode 100644
index 0000000..eaa3151
--- /dev/null
+++ b/groovy/src/main/groovy/xml/StreamingMarkupBuilder.groovy
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml
+
+import groovy.xml.streamingmarkupsupport.AbstractStreamingBuilder
+import groovy.xml.streamingmarkupsupport.StreamingMarkupWriter
+import groovy.xml.streamingmarkupsupport.BaseMarkupBuilder
+
+class StreamingMarkupBuilder extends AbstractStreamingBuilder {
+    def pendingStack = []
+    def commentClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
+        out.unescaped() << "<!--"
+        out.escaped() << body
+        out.unescaped() << "-->"
+    }
+    def piClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
+        attrs.each {target, instruction ->
+            out.unescaped() << "<?"
+            if (instruction instanceof Map) {
+                out.unescaped() << target
+                instruction.each {name, value ->
+                    if (value.toString().contains('"')) {
+                        out.unescaped() << " $name='$value'"
+                    } else {
+                        out.unescaped() << " $name=\"$value\""
+                    }
+                }
+            } else {
+                out.unescaped() << "$target $instruction"
+            }
+            out.unescaped() << "?>"
+        }
+    }
+    def declarationClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
+        out.unescaped() << '<?xml version="1.0" encoding="'
+        out.escaped() << "${out.encoding}"
+        out.unescaped() << '"?>\n'
+    }
+    def noopClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
+        body.each {
+            if (it instanceof Closure) {
+                def body1 = it.clone()
+
+                body1.delegate = doc
+                body1(doc)
+            } else if (it instanceof Buildable) {
+                it.build(doc)
+            } else {
+                out.escaped() << it
+            }
+        }
+    }
+    def unescapedClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
+        out.unescaped() << body
+    }
+    def tagClosure = {tag, doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, out ->
+        if (prefix != "") {
+            if (!(namespaces.containsKey(prefix) || pendingNamespaces.containsKey(prefix))) {
+                throw new GroovyRuntimeException("Namespace prefix: ${prefix} is not bound to a URI")
+            }
+
+            if (prefix != ":") tag = prefix + ":" + tag
+        }
+
+        out = out.unescaped() << "<${tag}"
+
+        attrs.each {key, value ->
+            if (key.contains('$')) {
+                def parts = key.tokenize('$')
+
+                if (namespaces.containsKey(parts[0]) || pendingNamespaces.containsKey(parts[0])) {
+                    key = parts[0] + ":" + parts[1]
+                } else {
+                    throw new GroovyRuntimeException("bad attribute namespace tag: ${parts[0]} in ${key}")
+                }
+            }
+
+            out << " ${key}='"
+            out.writingAttribute = true
+            "${value}".build(doc)
+            out.writingAttribute = false
+            out << "'"
+        }
+
+        def hiddenNamespaces = [:]
+
+        pendingNamespaces.each {key, value ->
+            hiddenNamespaces[key] = namespaces[key]
+            namespaces[key] = value
+            out << ((key == ":") ? " xmlns='" : " xmlns:${key}='")
+            out.writingAttribute = true
+            "${value}".build(doc)
+            out.writingAttribute = false
+            out << "'"
+        }
+
+        if (body == null) {
+            out << "/>"
+        } else {
+            out << ">"
+
+            pendingStack.add pendingNamespaces.clone()
+            pendingNamespaces.clear()
+
+            body.each {
+                if (it instanceof Closure) {
+                    def body1 = it.clone()
+
+                    body1.delegate = doc
+                    body1(doc)
+                } else if (it instanceof Buildable) {
+                    it.build(doc)
+                } else {
+                    out.escaped() << it
+                }
+            }
+
+            pendingNamespaces.clear()
+            pendingNamespaces.putAll pendingStack.pop()
+
+            out << "</${tag}>"
+        }
+
+        hiddenNamespaces.each {key, value ->
+            if (value == null) {
+                namespaces.remove key
+            } else {
+                namespaces[key] = value
+            }
+        }
+    }
+
+    def builder = null
+
+    StreamingMarkupBuilder() {
+        specialTags.putAll(['yield': noopClosure,
+        'yieldUnescaped': unescapedClosure,
+        'xmlDeclaration': declarationClosure,
+        'comment': commentClosure,
+        'pi': piClosure])
+
+        def nsSpecificTags = [':': [tagClosure, tagClosure, [:]], // the default namespace
+        'http://www.w3.org/XML/1998/namespace': [tagClosure, tagClosure, [:]],
+        'http://www.codehaus.org/Groovy/markup/keywords': [badTagClosure, tagClosure, specialTags]]
+
+        this.builder = new BaseMarkupBuilder(nsSpecificTags)
+    }
+
+    def encoding = null
+
+    public bind(closure) {
+        def boundClosure = this.builder.bind(closure);
+        def enc = encoding; // take a snapshot of the encoding when the closure is bound to the builder
+
+        {out ->
+            out = new StreamingMarkupWriter(out, enc)
+            boundClosure.trigger = out
+            out.flush()
+        }.asWritable()
+    }
+}
diff --git a/groovy/src/main/groovy/xml/StreamingSAXBuilder.groovy b/groovy/src/main/groovy/xml/StreamingSAXBuilder.groovy
new file mode 100644
index 0000000..e50a0d6
--- /dev/null
+++ b/groovy/src/main/groovy/xml/StreamingSAXBuilder.groovy
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.xml
+
+import groovy.xml.streamingmarkupsupport.AbstractStreamingBuilder
+import groovy.xml.streamingmarkupsupport.StreamingMarkupWriter
+import groovy.xml.streamingmarkupsupport.BaseMarkupBuilder
+
+
+import org.xml.sax.helpers.AttributesImpl
+import org.xml.sax.ext.LexicalHandler
+
+    class StreamingSAXBuilder extends AbstractStreamingBuilder {
+        def pendingStack = []
+        def commentClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, contentHandler ->
+                                      if (contentHandler instanceof LexicalHandler) {
+                                          contentHandler.comment(body.toCharArray(), 0, body.size())
+                                      }
+                                   }
+        def piClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, contentHandler ->
+                                  attrs.each {target, instruction ->
+                                     if (instruction instanceof Map) {
+                                     def buf = new StringBuffer()
+                                        
+                                        instruction.each { name, value ->
+                                          if (value.toString().contains('"')) {
+                                            buf.append(" $name='$value'")
+                                          } else {
+                                            buf.append(" $name=\"$value\"" )                                        
+                                          }
+                                        }
+                                        contentHandler.processingInstruction(target, buf.toString())
+                                      } else {
+                                        contentHandler.processingInstruction(target, instruction)
+                                      }
+                                    }
+                               }
+        def noopClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, contentHandler ->
+                                  if (body instanceof Closure) {
+                                    def body1 = body.clone()
+                                    
+                                    body1.delegate = doc
+                                    body1(doc)
+                                  } else if (body instanceof Buildable) {
+                                    body.build(doc)
+                                  } else if (body != null) {
+                                    body.each {
+                                      if (it instanceof Closure) {
+                                        def body1 = it.clone()
+                                        
+                                        body1.delegate = doc
+                                        body1(doc)
+                                      } else if (it instanceof Buildable) {
+                                        it.build(doc)
+                                      } else {
+                                          def chars = it.toCharArray()
+                                          contentHandler.characters(chars, 0, chars.size())
+                                        }
+                                      }
+                                  }
+                                }
+        def tagClosure = {tag, doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, body, contentHandler ->
+                                  def attributes = new AttributesImpl()
+          
+                                  attrs.each {key, value ->
+                                          if (key.contains('$')) {
+                                              def parts = key.tokenize('$')
+          
+                                              if (namespaces.containsKey(parts[0])) {
+                                                  def namespaceUri = namespaces[parts[0]]          
+                                                  attributes.addAttribute(namespaceUri, parts[1], "${parts[0]}:${parts[1]}", "CDATA", value)
+                                              } else {
+                                                  throw new GroovyRuntimeException("bad attribute namespace tag in ${key}")
+                                              }
+                                          } else {
+                                              attributes.addAttribute("", key, key, "CDATA", value)
+                                          }
+                                    }
+          
+                                  def hiddenNamespaces = [:]
+          
+                                  pendingNamespaces.each {key, value ->
+                                      hiddenNamespaces[key] = namespaces[key]
+                                      namespaces[key] = value
+                                      attributes.addAttribute("http://www.w3.org/2000/xmlns/", key, "xmlns:${key}", "CDATA", value)
+                                      contentHandler.startPrefixMapping(key, value)
+                                  }
+          
+                                  // setup the tag info
+          
+                                  def uri = ""
+                                  def qualifiedName = tag
+          
+                                  if (prefix != "") {
+                                      if (namespaces.containsKey(prefix)) {
+                                          uri = namespaces[prefix]
+                                      } else if (pendingNamespaces.containsKey(prefix)) {
+                                          uri = pendingNamespaces[prefix]
+                                      } else {
+                                          throw new GroovyRuntimeException("Namespace prefix: ${prefix} is not bound to a URI")
+                                      }
+          
+                                      if (prefix != ":") {
+                                          qualifiedName = prefix + ":" + tag
+                                      }
+                                  }
+          
+                                  contentHandler.startElement(uri, tag, qualifiedName, attributes)
+          
+                                  if (body != null) {
+                                      pendingStack.add pendingNamespaces.clone()
+                                      pendingNamespaces.clear()
+          
+                                      if (body instanceof Closure) {
+                                        def body1 = body.clone()
+                                        
+                                        body1.delegate = doc
+                                        body1(doc)
+                                      } else if (body instanceof Buildable) {
+                                        body.build(doc)
+                                      } else {
+                                        body.each {
+                                          if (it instanceof Closure) {
+                                            def body1 = it.clone()
+                                            
+                                            body1.delegate = doc
+                                            body1(doc)
+                                          } else if (it instanceof Buildable) {
+                                            it.build(doc)
+                                          } else {
+                                              def chars = it.toCharArray()
+                                              contentHandler.characters(chars, 0, chars.size())
+                                            }
+                                          }
+                                      }
+          
+                                      pendingNamespaces.clear()
+                                      pendingNamespaces.putAll pendingStack.pop()
+                                  }
+          
+                                  contentHandler.endElement(uri, tag, qualifiedName)
+          
+                                  hiddenNamespaces.each {key, value ->
+                                                              contentHandler.endPrefixMapping(key)
+          
+                                                              if (value == null) {
+                                                                  namespaces.remove key
+                                                              } else {
+                                                                  namespaces[key] = value
+                                                              }
+                                                         }
+                              }
+
+        def builder = null
+
+        StreamingSAXBuilder() {
+            specialTags.putAll(['yield':noopClosure,
+                                'yieldUnescaped':noopClosure,
+                                'comment':commentClosure,
+                                'pi':piClosure])
+
+            def nsSpecificTags = [':'                                          : [tagClosure, tagClosure, [:]],    // the default namespace
+                                  'http://www.w3.org/XML/1998/namespace'           : [tagClosure, tagClosure, [:]],
+                                  'http://www.codehaus.org/Groovy/markup/keywords' : [badTagClosure, tagClosure, specialTags]]
+
+            this.builder = new BaseMarkupBuilder(nsSpecificTags)
+        }
+
+        public bind(closure) {
+            def boundClosure = this.builder.bind(closure)
+
+            return {contentHandler ->
+                boundClosure.trigger = contentHandler
+            }
+        }
+    }
diff --git a/groovy/src/main/groovy/xml/dom/DOMCategory.java b/groovy/src/main/groovy/xml/dom/DOMCategory.java
new file mode 100644
index 0000000..670a803
--- /dev/null
+++ b/groovy/src/main/groovy/xml/dom/DOMCategory.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml.dom;
+
+import groovy.xml.QName;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.w3c.dom.*;
+
+import java.util.*;
+
+/**
+ * @author sam
+ * @author paulk
+ */
+public class DOMCategory {
+
+    private static boolean trimWhitespace = true;
+
+    public static Object get(Object o, String elementName) {
+        if (o instanceof Element) {
+            return get((Element) o, elementName);
+        }
+        if (o instanceof NodeList) {
+            return get((NodeList) o, elementName);
+        }
+        if (o instanceof NamedNodeMap) {
+            return get((NamedNodeMap) o, elementName);
+        }
+        return null;
+    }
+
+    private static Object get(Element element, String elementName) {
+        return getAt(element, elementName);
+    }
+
+    private static Object get(NodeList nodeList, String elementName) {
+        return getAt(nodeList, elementName);
+    }
+
+    private static Object get(NamedNodeMap nodeMap, String elementName) {
+        return getAt(nodeMap, elementName);
+    }
+
+    private static Object getAt(Element element, String elementName) {
+        if ("..".equals(elementName)) {
+            return parent(element);
+        }
+        if ("**".equals(elementName)) {
+            return depthFirst(element);
+        }
+        if (elementName.startsWith("@")) {
+            return element.getAttribute(elementName.substring(1));
+        }
+        return getChildElements(element, elementName);
+    }
+
+    private static Object getAt(NodeList nodeList, String elementName) {
+        List results = new ArrayList();
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            Node node = nodeList.item(i);
+            if (node instanceof Element) {
+                addResult(results, get(node, elementName));
+            }
+        }
+        if (elementName.startsWith("@")) {
+            return results;
+        }
+        return new NodeListsHolder(results);
+    }
+
+    public static NamedNodeMap attributes(Element element) {
+        return element.getAttributes();
+    }
+
+    private static String getAt(NamedNodeMap namedNodeMap, String elementName) {
+        Attr a = (Attr) namedNodeMap.getNamedItem(elementName);
+        return a.getValue();
+    }
+
+    public static int size(NamedNodeMap namedNodeMap) {
+        return namedNodeMap.getLength();
+    }
+
+    public static Node getAt(Node o, int i) {
+        return nodeGetAt(o, i);
+    }
+
+    public static Node getAt(NodeListsHolder o, int i) {
+        return nodeGetAt(o, i);
+    }
+
+    public static Node getAt(NodesHolder o, int i) {
+        return nodeGetAt(o, i);
+    }
+
+    private static Node nodeGetAt(Object o, int i) {
+        if (o instanceof Element) {
+            Node n = getAt((Element)o, i);
+            if (n != null) return n;
+        }
+        if (o instanceof NodeList) {
+            return getAt((NodeList)o, i);
+        }
+        return null;
+    }
+
+    private static Node getAt(Element element, int i) {
+        if (hasChildElements(element, "*")) {
+            NodeList nodeList = getChildElements(element, "*");
+            return nodeList.item(i);
+        }
+        return null;
+    }
+
+    private static Node getAt(NodeList nodeList, int i) {
+        if (i >= 0 && i < nodeList.getLength()) {
+            return nodeList.item(i);
+        }
+        return null;
+    }
+
+    public static String name(Element element) {
+        return element.getNodeName();
+    }
+
+    public static Node parent(Node node) {
+        return node.getParentNode();
+    }
+
+    public static String text(Object o) {
+        if (o instanceof Element) {
+            return text((Element) o);
+        }
+        if (o instanceof Node) {
+            Node n = (Node) o;
+            if (n.getNodeType() == Node.TEXT_NODE) {
+                return n.getNodeValue();
+            }
+        }
+        if (o instanceof NodeList) {
+            return text((NodeList) o);
+        }
+        return null;
+    }
+
+    private static String text(Element element) {
+        if (!element.hasChildNodes()) {
+            return "";
+        }
+        if (element.getFirstChild().getNodeType() != Node.TEXT_NODE) {
+            return "";
+        }
+        return element.getFirstChild().getNodeValue();
+    }
+
+    private static String text(NodeList nodeList) {
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            sb.append(text(nodeList.item(i)));
+        }
+        return sb.toString();
+    }
+
+    public static List list(NodeList self) {
+        List answer = new ArrayList();
+        Iterator it = DefaultGroovyMethods.iterator(self);
+        while (it.hasNext()) {
+            answer.add(it.next());
+        }
+        return answer;
+    }
+
+    public static NodeList depthFirst(Element self) {
+        List result = new ArrayList();
+        result.add(createNodeList(self));
+        result.add(self.getElementsByTagName("*"));
+        return new NodeListsHolder(result);
+    }
+
+    public static void setValue(Element self, String value) {
+        self.getFirstChild().setNodeValue(value);
+    }
+
+    public static void putAt(Element self, String property, Object value) {
+        if (property.startsWith("@")) {
+            String attributeName = property.substring(1);
+            Document doc = self.getOwnerDocument();
+            Attr newAttr = doc.createAttribute(attributeName);
+            newAttr.setValue(value.toString());
+            self.setAttributeNode(newAttr);
+            return;
+        }
+        InvokerHelper.setProperty(self, property, value);
+    }
+
+    public static Element appendNode(Element self, Object name) {
+        return appendNode(self, name, (String)null);
+    }
+
+    public static Element appendNode(Element self, Object name, Map attributes) {
+        return appendNode(self, name, attributes, null);
+    }
+
+    public static Element appendNode(Element self, Object name, String value) {
+        Document doc = self.getOwnerDocument();
+        Element newChild;
+        if (name instanceof QName) {
+            QName qn = (QName) name;
+            newChild = doc.createElementNS(qn.getNamespaceURI(), qn.getQualifiedName());
+        } else {
+            newChild = doc.createElement(name.toString());
+        }
+        if (value != null) {
+            Text text = doc.createTextNode(value);
+            newChild.appendChild(text);
+        }
+        self.appendChild(newChild);
+        return newChild;
+    }
+
+    public static Element appendNode(Element self, Object name, Map attributes, String value) {
+        Element result = appendNode(self, name, value);
+        for (Iterator iterator = attributes.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry e = (Map.Entry) iterator.next();
+            putAt(result, "@" + e.getKey().toString(), e.getValue());
+        }
+        return result;
+    }
+
+    private static NodeList createNodeList(Element self) {
+        List first = new ArrayList();
+        first.add(self);
+        return new NodesHolder(first);
+    }
+
+    public static NodeList breadthFirst(Element self) {
+        List result = new ArrayList();
+        NodeList thisLevel = createNodeList(self);
+        while (thisLevel.getLength() > 0) {
+            result.add(thisLevel);
+            thisLevel = getNextLevel(thisLevel);
+        }
+        return new NodeListsHolder(result);
+    }
+
+    private static NodeList getNextLevel(NodeList thisLevel) {
+        List result = new ArrayList();
+        for (int i = 0; i < thisLevel.getLength(); i++) {
+            Node n = thisLevel.item(i);
+            if (n instanceof Element) {
+                result.add(getChildElements((Element) n, "*"));
+            }
+        }
+        return new NodeListsHolder(result);
+    }
+
+    public static NodeList children(Element self) {
+        return getChildElements(self, "*");
+    }
+
+    private static boolean hasChildElements(Element self, String elementName) {
+        return getChildElements(self, elementName).getLength() > 0;
+    }
+
+    private static NodeList getChildElements(Element self, String elementName) {
+        List result = new ArrayList();
+        NodeList nodeList = self.getChildNodes();
+        for (int i = 0; i < nodeList.getLength(); i++) {
+            Node node = nodeList.item(i);
+            if (node.getNodeType() == Node.ELEMENT_NODE) {
+                Element child = (Element) node;
+                if ("*".equals(elementName) || child.getTagName().equals(elementName)) {
+                    result.add(child);
+                }
+            } else if (node.getNodeType() == Node.TEXT_NODE) {
+                String value = node.getNodeValue();
+                if (trimWhitespace) {
+                    value = value.trim();
+                }
+                if ("*".equals(elementName) && value.length() > 0) {
+                    node.setNodeValue(value);
+                    result.add(node);
+                }
+            }
+        }
+        return new NodesHolder(result);
+    }
+
+    public static String toString(Object o) {
+        if (o instanceof Node) {
+            if (((Node) o).getNodeType() == Node.TEXT_NODE) {
+                return ((Node) o).getNodeValue();
+            }
+        }
+        if (o instanceof NodeList) {
+            return toString((NodeList) o);
+        }
+        return o.toString();
+    }
+
+    private static String toString(NodeList self) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("[");
+        Iterator it = DefaultGroovyMethods.iterator(self);
+        while (it.hasNext()) {
+            if (sb.length() > 1) sb.append(", ");
+            sb.append(it.next().toString());
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public static int size(NodeList self) {
+        return self.getLength();
+    }
+
+    public static boolean isEmpty(NodeList self) {
+        return size(self) == 0;
+    }
+
+    private static void addResult(List results, Object result) {
+        if (result != null) {
+            if (result instanceof Collection) {
+                results.addAll((Collection) result);
+            } else {
+                results.add(result);
+            }
+        }
+    }
+
+    private static final class NodeListsHolder implements NodeList {
+        private List nodeLists;
+
+        private NodeListsHolder(List nodeLists) {
+            this.nodeLists = nodeLists;
+        }
+
+        public int getLength() {
+            int length = 0;
+            for (int i = 0; i < nodeLists.size(); i++) {
+                NodeList nl = (NodeList) nodeLists.get(i);
+                length += nl.getLength();
+            }
+            return length;
+        }
+
+        public Node item(int index) {
+            int relativeIndex = index;
+            for (int i = 0; i < nodeLists.size(); i++) {
+                NodeList nl = (NodeList) nodeLists.get(i);
+                if (relativeIndex < nl.getLength()) {
+                    return nl.item(relativeIndex);
+                }
+                relativeIndex -= nl.getLength();
+            }
+            return null;
+        }
+
+        public String toString() {
+            return DOMCategory.toString(this);
+        }
+    }
+
+    private static final class NodesHolder implements NodeList {
+        private List nodes;
+
+        private NodesHolder(List nodes) {
+            this.nodes = nodes;
+        }
+
+        public int getLength() {
+            return nodes.size();
+        }
+
+        public Node item(int index) {
+            if (index < 0 || index >= getLength()) {
+                return null;
+            }
+            return (Node) nodes.get(index);
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/groovy/xml/dom/DOMUtil.java b/groovy/src/main/groovy/xml/dom/DOMUtil.java
new file mode 100644
index 0000000..5c3fb81
--- /dev/null
+++ b/groovy/src/main/groovy/xml/dom/DOMUtil.java
@@ -0,0 +1,37 @@
+package groovy.xml.dom;
+
+import org.w3c.dom.Element;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.OutputStream;
+import java.io.StringWriter;
+
+public class DOMUtil {
+    public static String serialize(Element element) {
+        StringWriter sw = new StringWriter();
+        serialize(element, new StreamResult(sw));
+        return sw.toString();
+    }
+
+    public static void serialize(Element element, OutputStream os) {
+        serialize(element, new StreamResult(os));
+    }
+
+    private static void serialize(Element element, StreamResult outputTarget) {
+        TransformerFactory transformerFactory = TransformerFactory.newInstance();
+        StringWriter sw = new StringWriter();
+        try {
+            Transformer transformer = transformerFactory.newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.transform(new DOMSource(element), outputTarget);
+        }
+        catch (TransformerException e) {
+            // ignore
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/xml/dom/package.html b/groovy/src/main/groovy/xml/dom/package.html
new file mode 100644
index 0000000..3b04de9
--- /dev/null
+++ b/groovy/src/main/groovy/xml/dom/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.xml.dom.*</title>
+  </head>
+  <body>
+    <p>Groovy XML Dom processing classes.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/xml/package.html b/groovy/src/main/groovy/xml/package.html
new file mode 100644
index 0000000..9a636eb
--- /dev/null
+++ b/groovy/src/main/groovy/xml/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.xml.*</title>
+  </head>
+  <body>
+    <p>Groovy markup builder classes for working with SAX and W3C DOM and Groovy markup.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/groovy/xml/streamingmarkupsupport/AbstractStreamingBuilder.groovy b/groovy/src/main/groovy/xml/streamingmarkupsupport/AbstractStreamingBuilder.groovy
new file mode 100644
index 0000000..ea470b1
--- /dev/null
+++ b/groovy/src/main/groovy/xml/streamingmarkupsupport/AbstractStreamingBuilder.groovy
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.xml.streamingmarkupsupport
+
+class AbstractStreamingBuilder {
+    def badTagClosure = {tag, doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, Object[] rest ->
+        def uri = pendingNamespaces[prefix]
+        if (uri == null) {
+            uri = namespaces[prefix]
+        }
+        throw new GroovyRuntimeException("Tag ${tag} is not allowed in namespace ${uri}")
+    }
+    def namespaceSetupClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, Object[] rest ->
+        attrs.each { key, value ->
+            if ( key == "") {
+                key = ":"    // marker for default namespace
+            }
+            value = value.toString()     // in case it's not a string
+            if (namespaces[key] != value) {
+                pendingNamespaces[key] = value
+            }
+            if (!namespaceSpecificTags.containsKey(value)) {
+                def baseEntry = namespaceSpecificTags[':']
+                namespaceSpecificTags[value] = [baseEntry[0], baseEntry[1], [:]].toArray()
+            }
+        }
+    }
+    def aliasSetupClosure = {doc, pendingNamespaces, namespaces, namespaceSpecificTags, prefix, attrs, Object[] rest ->
+        attrs.each { key, value ->
+            if (value instanceof Map) {
+                // key is a namespace prefix value is the mapping
+                def info = null
+                if (namespaces.containsKey(key)) {
+                    info = namespaceSpecificTags[namespaces[key]]
+                } else if (pendingNamespaces.containsKey(key)) {
+                    info = namespaceSpecificTags[pendingNamespaces[key]]
+                } else {
+                    throw new GroovyRuntimeException("namespace prefix ${key} has not been declared")
+                }
+                value.each { to, from -> info[2][to] = info[1].curry(from) }
+            } else {
+                def info = namespaceSpecificTags[':']
+                info[2][key] = info[1].curry(value)
+            }
+        }
+    }
+    def getNamespaceClosure = {doc, pendingNamespaces, namespaces, Object[] rest -> [namespaces, pendingNamespaces]}
+
+    def specialTags = ['declareNamespace':namespaceSetupClosure,
+                           'declareAlias':aliasSetupClosure,
+                          'getNamespaces':getNamespaceClosure]
+
+    def builder = null
+}
diff --git a/groovy/src/main/groovy/xml/streamingmarkupsupport/BaseMarkupBuilder.java b/groovy/src/main/groovy/xml/streamingmarkupsupport/BaseMarkupBuilder.java
new file mode 100644
index 0000000..8341540
--- /dev/null
+++ b/groovy/src/main/groovy/xml/streamingmarkupsupport/BaseMarkupBuilder.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml.streamingmarkupsupport;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyInterceptable;
+import groovy.lang.GroovyObjectSupport;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class BaseMarkupBuilder extends Builder {
+    public BaseMarkupBuilder(final Map namespaceMethodMap) {
+        super(namespaceMethodMap);
+    }
+
+    public Object bind(final Closure root) {
+        return new Document(root, this.namespaceMethodMap);
+    }
+
+    private static class Document extends Built implements GroovyInterceptable {
+        private Object out;
+        private final Map pendingNamespaces = new HashMap();
+        private final Map namespaces = new HashMap();
+        private final Map specialProperties = new HashMap();
+        private String prefix = "";
+
+        {
+
+            namespaces.put("xml", "http://www.w3.org/XML/1998/namespace");             // built in namespace
+            namespaces.put("mkp", "http://www.codehaus.org/Groovy/markup/keywords");   // pseudo namespace for markup keywords
+
+            specialProperties.put("out", new OutputSink("out") {
+                public Object leftShift(final Object value) {
+                    return leftShift("yield", value);
+                }
+            });
+            specialProperties.put("unescaped", new OutputSink("unescaped") {
+                public Object leftShift(final Object value) {
+                    return leftShift("yieldUnescaped", value);
+                }
+            });
+            specialProperties.put("namespaces", new OutputSink("namespaces") {
+                public Object leftShift(final Object value) {
+                    return leftShift("declareNamespace", value);
+                }
+            });
+            specialProperties.put("pi", new OutputSink("pi") {
+                public Object leftShift(final Object value) {
+                    return leftShift("pi", value);
+                }
+            });
+            specialProperties.put("comment", new OutputSink("comment") {
+                public Object leftShift(final Object value) {
+                    return leftShift("comment", value);
+                }
+            });
+        }
+
+        private abstract class OutputSink extends GroovyObjectSupport {
+            private final String name;
+
+            public OutputSink(final String name) {
+                this.name = name;
+            }
+
+            public Object invokeMethod(final String name, final Object args) {
+                Document.this.prefix = this.name;
+                return Document.this.invokeMethod(name, args);
+            }
+
+            public abstract Object leftShift(Object item);
+
+            protected Object leftShift(final String command, final Object value) {
+                Document.this.getProperty("mkp");
+                Document.this.invokeMethod(command, new Object[]{value});
+                return this;
+            }
+        }
+
+        public Document(final Closure root, final Map namespaceMethodMap) {
+            super(root, namespaceMethodMap);
+        }
+
+        /* (non-Javadoc)
+           * @see groovy.lang.GroovyObject#invokeMethod(java.lang.String, java.lang.Object)
+           */
+        public Object invokeMethod(final String name, final Object args) {
+            final Object[] arguments = (Object[]) args;
+            Map attrs = Collections.EMPTY_MAP;
+            Object body = null;
+
+            //
+            // Sort the parameters out
+            //
+            for (int i = 0; i != arguments.length; i++) {
+                final Object arg = arguments[i];
+
+                if (arg instanceof Map) {
+                    attrs = (Map) arg;
+                } else if (arg instanceof Closure) {
+                    final Closure c = ((Closure) arg);
+
+                    c.setDelegate(this);
+                    body = c.asWritable();
+                } else {
+                    body = arg;
+                }
+            }
+
+            //
+            // call the closure corresponding to the tag
+            //
+            final Object uri;
+
+            if (this.pendingNamespaces.containsKey(this.prefix)) {
+                uri = this.pendingNamespaces.get(this.prefix);
+            } else if (this.namespaces.containsKey(this.prefix)) {
+                uri = this.namespaces.get(this.prefix);
+            } else {
+                uri = ":";
+            }
+
+            final Object[] info = (Object[]) this.namespaceSpecificTags.get(uri);
+            final Map tagMap = (Map) info[2];
+            final Closure defaultTagClosure = (Closure) info[0];
+
+            final String prefix = this.prefix;
+            this.prefix = "";
+
+            if (tagMap.containsKey(name)) {
+                return ((Closure) tagMap.get(name)).call(new Object[]{this, this.pendingNamespaces, this.namespaces, this.namespaceSpecificTags, prefix, attrs, body, this.out});
+            } else {
+                return defaultTagClosure.call(new Object[]{name, this, this.pendingNamespaces, this.namespaces, this.namespaceSpecificTags, prefix, attrs, body, this.out});
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.GroovyObject#getProperty(java.lang.String)
+         */
+        public Object getProperty(final String property) {
+            final Object special = this.specialProperties.get(property);
+
+            if (special == null) {
+                this.prefix = property;
+                return this;
+            } else {
+                return special;
+            }
+        }
+
+        /* (non-Javadoc)
+         * @see groovy.lang.GroovyObject#setProperty(java.lang.String, java.lang.Object)
+         */
+        public void setProperty(String property, Object newValue) {
+            if ("trigger".equals(property)) {
+                this.out = newValue;
+                this.root.call(this);
+            } else {
+                super.setProperty(property, newValue);
+            }
+        }
+    }
+}
diff --git a/groovy/src/main/groovy/xml/streamingmarkupsupport/Builder.java b/groovy/src/main/groovy/xml/streamingmarkupsupport/Builder.java
new file mode 100644
index 0000000..332dfd5
--- /dev/null
+++ b/groovy/src/main/groovy/xml/streamingmarkupsupport/Builder.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.xml.streamingmarkupsupport;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public abstract class Builder extends GroovyObjectSupport {
+	protected final Map namespaceMethodMap = new HashMap();
+	
+	public Builder(final Map namespaceMethodMap) {
+	final Iterator keyIterator = namespaceMethodMap.keySet().iterator();
+		
+		while (keyIterator.hasNext()) {
+		final Object key = keyIterator.next();
+		final List value = (List)namespaceMethodMap.get(key);
+		final Closure dg = ((Closure)value.get(1)).asWritable();
+		
+			this.namespaceMethodMap.put(key, new Object[]{value.get(0), dg, fettleMethodMap(dg, (Map)value.get(2))});
+		}
+	}
+	
+	private static Map fettleMethodMap(final Closure defaultGenerator, final Map methodMap) {
+	final Map newMethodMap = new HashMap();
+	final Iterator keyIterator = methodMap.keySet().iterator();
+		
+		while (keyIterator.hasNext()) {
+		final Object key = keyIterator.next();
+		final Object value = methodMap.get(key);
+		
+			if ((value instanceof Closure)) {
+				newMethodMap.put(key, value);
+			} else {
+				newMethodMap.put(key, defaultGenerator.curry((Object[])value));
+			}
+		}
+		
+		return newMethodMap;
+	}
+	
+	public abstract Object bind(Closure root);
+	
+	protected abstract static class Built extends GroovyObjectSupport {
+	protected final Closure root;
+	protected final Map namespaceSpecificTags = new HashMap();
+		
+		public Built(final Closure root, final Map namespaceTagMap) {
+			this.namespaceSpecificTags.putAll(namespaceTagMap);
+		
+			this.root = (Closure)root.clone();
+			
+			this.root.setDelegate(this);
+		}
+	}
+}
diff --git a/groovy/src/main/groovy/xml/streamingmarkupsupport/StreamingMarkupWriter.java b/groovy/src/main/groovy/xml/streamingmarkupsupport/StreamingMarkupWriter.java
new file mode 100644
index 0000000..786194e
--- /dev/null
+++ b/groovy/src/main/groovy/xml/streamingmarkupsupport/StreamingMarkupWriter.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package groovy.xml.streamingmarkupsupport;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+public class StreamingMarkupWriter extends Writer {
+	protected final Writer writer;
+    protected final String encoding;
+    protected final CharsetEncoder encoder;
+    protected boolean writingAttribute = false;
+    protected boolean haveHighSurrogate = false;
+    protected StringBuffer surrogatePair = new StringBuffer(2);
+	private final Writer escapedWriter =  new Writer() {
+											/* (non-Javadoc)
+											 * @see java.io.Writer#close()
+											 */
+											public void close() throws IOException {
+												StreamingMarkupWriter.this.close();
+											}
+											
+											/* (non-Javadoc)
+											 * @see java.io.Writer#flush()
+											 */
+											public void flush() throws IOException {
+												StreamingMarkupWriter.this.flush();
+											}
+		
+											/* (non-Javadoc)
+											 * @see java.io.Writer#write(int)
+											 */
+											public void write(final int c) throws IOException {
+												if (c == '<') {
+													StreamingMarkupWriter.this.writer.write("&lt;");
+												} else if (c == '>') {
+													StreamingMarkupWriter.this.writer.write("&gt;");
+                                                   } else if (c == '&') {
+                                                       StreamingMarkupWriter.this.writer.write("&amp;");
+                                                   } else {
+													StreamingMarkupWriter.this.write(c);
+												}
+											}
+											
+											/* (non-Javadoc)
+											 * @see java.io.Writer#write(char[], int, int)
+											 */
+											public void write(final char[] cbuf, int off, int len) throws IOException {
+												while (len-- > 0){
+													write(cbuf[off++]);
+												}
+											}
+                                            
+                                               public void setWritingAttribute(final boolean writingAttribute) {
+                                                   StreamingMarkupWriter.this.writingAttribute = writingAttribute;
+                                               }
+											
+											public Writer excaped() {
+												return escapedWriter;
+											}
+											
+											public Writer unescaped() {
+												return StreamingMarkupWriter.this;
+											}
+										};
+
+    public StreamingMarkupWriter(final Writer writer, final String encoding) {
+        this.writer = writer;
+        
+        if (encoding != null) {
+            this.encoding = encoding;
+        } else if (writer instanceof OutputStreamWriter) {
+            this.encoding = ((OutputStreamWriter)writer).getEncoding();
+        } else {
+            this.encoding = "US-ASCII";
+        }
+        
+        this.encoder = Charset.forName(this.encoding).newEncoder();
+    }
+    
+    public StreamingMarkupWriter(final Writer writer) {
+        this(writer, null);
+    }
+    
+    /* (non-Javadoc)
+     * @see java.io.Writer#close()
+     */
+    public void close() throws IOException {
+        this.writer.close();
+    }
+    
+    /* (non-Javadoc)
+     * @see java.io.Writer#flush()
+     */
+    public void flush() throws IOException {
+        this.writer.flush();
+    }
+    
+    /* (non-Javadoc)
+     * @see java.io.Writer#write(int)
+     */
+    public void write(final int c) throws IOException {
+        if (c >= 0XDC00 && c <= 0XDFFF) {
+            // Low surrogate
+            this.surrogatePair.append((char)c);
+            
+            if (this.encoder.canEncode(this.surrogatePair)) {
+                this.writer.write(this.surrogatePair.toString());
+            } else {
+                this.writer.write("&#x");
+                this.writer.write(Integer.toHexString(0X10000 + ((this.surrogatePair.charAt(0) & 0X3FF) << 10) + (c & 0X3FF)));
+                this.writer.write(';');
+            }
+            
+            this.haveHighSurrogate = false;
+            this.surrogatePair.setLength(0);
+        } else {
+            if (this.haveHighSurrogate) {
+                this.haveHighSurrogate = false;
+                this.surrogatePair.setLength(0);
+                throw new IOException("High Surrogate not followed by Low Surrogate");
+            }
+            
+            if (c >= 0XD800 && c <= 0XDBFF) {
+                // High surrogate
+                this.surrogatePair.append((char)c);
+                this.haveHighSurrogate = true;
+            
+            } else if (!this.encoder.canEncode((char)c)) {
+                this.writer.write("&#x");
+                this.writer.write(Integer.toHexString(c));
+                this.writer.write(';');
+            } else if (c == '\'' && this.writingAttribute) {
+                this.writer.write("&apos;");
+            } else {
+                this.writer.write(c);
+            }
+        }
+    }
+    
+    /* (non-Javadoc)
+     * @see java.io.Writer#write(char[], int, int)
+     */
+    public void write(final char[] cbuf, int off, int len) throws IOException {
+        while (len-- > 0){
+            write(cbuf[off++]);
+        }
+    }
+    
+    public void setWritingAttribute(final boolean writingAttribute) {
+        this.writingAttribute = writingAttribute;
+    }
+    
+    public Writer escaped() {
+        return this.escapedWriter;
+    }
+    
+    public Writer unescaped() {
+        return this;
+    }
+    
+    public String getEncoding() {
+        return this.encoding;
+    }
+}
diff --git a/groovy/src/main/groovy/xml/streamingmarkupsupport/package.html b/groovy/src/main/groovy/xml/streamingmarkupsupport/package.html
new file mode 100644
index 0000000..df61ea8
--- /dev/null
+++ b/groovy/src/main/groovy/xml/streamingmarkupsupport/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package groovy.xml.streamingmarkupsupport.*</title>
+  </head>
+  <body>
+    <p>XmlBuilder related classes to support streaming XML.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/GroovyBugError.java b/groovy/src/main/org/codehaus/groovy/GroovyBugError.java
new file mode 100644
index 0000000..71da98d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/GroovyBugError.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy;
+
+/**
+ * This class represents an error that is thrown when a bug is 
+ * recognized inside the runtime. Basically it is thrown when
+ * a constraint is not fullfilled that should be fullfiled. 
+ * 
+ * @author Jochen Theodorou
+ */
+public class GroovyBugError extends AssertionError {
+    
+    // message string
+    private String message;
+    // optional exception
+    private final Exception exception;
+
+    /**
+     * constructs a bug error using the given text
+     * @param message the error message text
+     */
+    public GroovyBugError( String message ) {
+        this(message, null);
+    }
+    
+    /**
+     * Constructs a bug error using the given exception
+     * @param exception cause of this error
+     */
+    public GroovyBugError( Exception exception ) {
+        this(null, exception);
+    }
+    
+    /**
+     * Constructs a bug error using the given exception and
+     * a text with additional information about the cause 
+     * @param msg additional information about this error
+     * @param exception cause of this error
+     */
+    public GroovyBugError( String msg, Exception exception ) {
+        this.exception = exception;
+        this.message = msg;
+    }
+
+    /**
+     * Returns a String representation of this class by calling <code>getMessage()</code>.  
+     * @see #getMessage()
+     */
+    public String toString() {
+        return getMessage();
+    }
+    
+    /**
+     * Returns the detail message string of this error. The message 
+     * will consist of the bug text prefixed by "BUG! " if there this
+     * isntance was created using a message. If this error was 
+     * constructed without using a bug text the message of the cause 
+     * is used prefixed by "BUG! UNCAUGHT EXCEPTION: "
+     *  
+     * @return the detail message string of this error.
+     */
+    public String getMessage() {
+        if( message != null )
+        {
+            return "BUG! "+message;
+        }
+        else
+        {
+            return "BUG! UNCAUGHT EXCEPTION: " + exception.getMessage();
+        }
+    }
+    
+    public Throwable getCause() {
+        return this.exception;
+    }    
+    
+    /**
+     * Returns the bug text to describe this error
+     */
+    public String getBugText(){
+        if( message != null ){
+            return message;
+        } else {
+            return exception.getMessage();
+        }
+    }
+    
+    /**
+     * Sets the bug text to describe this error
+     */
+    public void setBugText(String msg) {
+        this.message = msg;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/GroovyException.java b/groovy/src/main/org/codehaus/groovy/GroovyException.java
new file mode 100644
index 0000000..4c170b3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/GroovyException.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy;
+
+public class GroovyException extends Exception implements GroovyExceptionInterface {
+    private boolean fatal = true;
+
+    public GroovyException() {
+    }
+
+    public GroovyException(String message) {
+        super(message);
+    }
+
+    public GroovyException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public GroovyException(boolean fatal) {
+        super();
+        this.fatal = fatal;
+    }
+
+    public GroovyException(String message, boolean fatal) {
+        super(message);
+        this.fatal = fatal;
+    }
+
+    public boolean isFatal() {
+        return fatal;
+    }
+
+    public void setFatal(boolean fatal) {
+        this.fatal = fatal;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/GroovyExceptionInterface.java b/groovy/src/main/org/codehaus/groovy/GroovyExceptionInterface.java
new file mode 100644
index 0000000..beee5e3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/GroovyExceptionInterface.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy;
+
+/**
+ *  An interface for use by all Groovy compiler exceptions.
+ */
+
+public interface GroovyExceptionInterface {
+
+    boolean isFatal();
+
+    void setFatal( boolean fatal );
+    
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ant/AntProjectPropertiesDelegate.java b/groovy/src/main/org/codehaus/groovy/ant/AntProjectPropertiesDelegate.java
new file mode 100644
index 0000000..e0d3816
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/AntProjectPropertiesDelegate.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.ant;
+
+import org.apache.tools.ant.Project;
+
+import java.util.Hashtable;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+
+/**
+ * @author Guillaume Laforge
+ */
+public class AntProjectPropertiesDelegate extends Hashtable {
+
+    private Project project;
+
+    public AntProjectPropertiesDelegate(Project project) {
+        super();
+        this.project = project;
+    }
+
+    public synchronized int hashCode() {
+        return project.getProperties().hashCode();
+    }
+
+    public synchronized int size() {
+        return project.getProperties().size();
+    }
+
+    /**
+     * @throws UnsupportedOperationException is always thrown when this method is invoked. The Project properties are immutable.
+     */    
+    public synchronized void clear() {
+        throw new UnsupportedOperationException("Impossible to clear the project properties.");
+    }
+
+    public synchronized boolean isEmpty() {
+        return project.getProperties().isEmpty();
+    }
+
+    public synchronized Object clone() {
+        return project.getProperties().clone();
+    }
+
+    public synchronized boolean contains(Object value) {
+        return project.getProperties().contains(value);
+    }
+
+    public synchronized boolean containsKey(Object key) {
+        return project.getProperties().containsKey(key);
+    }
+
+    public boolean containsValue(Object value) {
+        return project.getProperties().containsValue(value);
+    }
+
+    public synchronized boolean equals(Object o) {
+        return project.getProperties().equals(o);
+    }
+
+    public synchronized String toString() {
+        return project.getProperties().toString();
+    }
+
+    public Collection values() {
+        return project.getProperties().values();
+    }
+
+    public synchronized Enumeration elements() {
+        return project.getProperties().elements();
+    }
+
+    public synchronized Enumeration keys() {
+        return project.getProperties().keys();
+    }
+
+    public AntProjectPropertiesDelegate(Map t) {
+        super(t);
+    }
+
+    public synchronized void putAll(Map t) {
+        Set keySet = t.keySet();
+        for (Iterator iterator = keySet.iterator(); iterator.hasNext();) {
+            Object key = iterator.next();
+            Object value = t.get(key);
+            put(key, value);
+        }
+    }
+
+    public Set entrySet() {
+        return project.getProperties().entrySet();
+    }
+
+    public Set keySet() {
+        return project.getProperties().keySet();
+    }
+
+    public synchronized Object get(Object key) {
+        return project.getProperties().get(key);
+    }
+
+    /**
+     * @throws UnsupportedOperationException is always thrown when this method is invoked. The Project properties are immutable.
+     */
+    public synchronized Object remove(Object key) {
+        throw new UnsupportedOperationException("Impossible to remove a property from the project properties.");
+    }
+
+    public synchronized Object put(Object key, Object value) {
+        Object oldValue = null;
+        if (containsKey(key)) {
+            oldValue = get(key);
+        }
+        project.setProperty(key.toString(), value.toString());
+        return oldValue;
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/ant/CompileTaskSupport.java b/groovy/src/main/org/codehaus/groovy/ant/CompileTaskSupport.java
new file mode 100644
index 0000000..604fdb4
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/CompileTaskSupport.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.ant;
+
+import groovy.lang.GroovyClassLoader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.tools.ErrorReporter;
+
+import java.io.File;
+import java.io.StringWriter;
+import java.io.PrintWriter;
+
+/**
+ * Support for compilation related tasks.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public abstract class CompileTaskSupport
+    extends MatchingTask
+{
+    protected final LoggingHelper log = new LoggingHelper(this);
+
+    protected Path src;
+
+    protected File destdir;
+
+    protected Path classpath;
+
+    protected CompilerConfiguration config = new CompilerConfiguration();
+
+    protected boolean failOnError = true;
+
+    public void setFailonerror(final boolean fail) {
+        failOnError = fail;
+    }
+
+    public boolean getFailonerror() {
+        return failOnError;
+    }
+
+    public Path createSrc() {
+        if (src == null) {
+            src = new Path(getProject());
+        }
+        return src.createPath();
+    }
+
+    public void setSrcdir(final Path dir) {
+        assert dir != null;
+
+        if (src == null) {
+            src = dir;
+        }
+        else {
+            src.append(dir);
+        }
+    }
+
+    public Path getSrcdir() {
+        return src;
+    }
+
+    public void setDestdir(final File dir) {
+        assert dir != null;
+
+        this.destdir = dir;
+    }
+
+    public void setClasspath(final Path path) {
+        assert path != null;
+
+        if (classpath == null) {
+            classpath = path;
+        }
+        else {
+            classpath.append(path);
+        }
+    }
+
+    public Path getClasspath() {
+        return classpath;
+    }
+
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+
+        return classpath.createPath();
+    }
+
+    public void setClasspathRef(final Reference r) {
+        assert r != null;
+        
+        createClasspath().setRefid(r);
+    }
+
+    public CompilerConfiguration createConfiguration() {
+        return config;
+    }
+    
+    protected void validate() throws BuildException {
+        if (src == null) {
+            throw new BuildException("Missing attribute: srcdir (or one or more nested <src> elements).", getLocation());
+        }
+
+        if (destdir == null) {
+            throw new BuildException("Missing attribute: destdir", getLocation());
+        }
+
+        if (!destdir.exists()) {
+            throw new BuildException("Destination directory does not exist: " + destdir, getLocation());
+        }
+    }
+
+    protected GroovyClassLoader createClassLoader() {
+        ClassLoader parent = ClassLoader.getSystemClassLoader();
+        GroovyClassLoader gcl = new GroovyClassLoader(parent, config);
+
+        Path path = getClasspath();
+        if (path != null) {
+            final String[] filePaths = path.list();
+            for (int i = 0; i < filePaths.length; i++) {
+                String filePath = filePaths[i];
+                gcl.addClasspath(filePath);
+            }
+        }
+
+        return gcl;
+    }
+
+    protected void handleException(final Exception e) throws BuildException {
+        assert e != null;
+        
+        StringWriter writer = new StringWriter();
+        new ErrorReporter(e, false).write(new PrintWriter(writer));
+        String message = writer.toString();
+
+        if (failOnError) {
+            throw new BuildException(message, e, getLocation());
+        }
+        else {
+            log.error(message);
+        }
+    }
+
+    public void execute() throws BuildException {
+        validate();
+
+        try {
+            compile();
+        }
+        catch (Exception e) {
+            handleException(e);
+        }
+    }
+
+    protected abstract void compile() throws Exception;
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/ant/FileIterator.java b/groovy/src/main/org/codehaus/groovy/ant/FileIterator.java
new file mode 100644
index 0000000..f0e9a6a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/FileIterator.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ant;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+
+/** 
+ * <p><code>FileIterator</code> is an iterator over a 
+ * number of files from a collection of FileSet instances.
+ *
+ * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
+ * @version $Revision$
+ */
+public class FileIterator implements Iterator {
+
+    /** The iterator over the FileSet objects */
+    private Iterator fileSetIterator;
+    
+    /** The Ant project */
+    private Project project;
+    
+    /** The directory scanner */
+    private DirectoryScanner ds;
+    
+    /** The file names in the current FileSet scan */
+    private String[] files;
+    
+    /** The current index into the file name array */
+    private int fileIndex = -1;
+    
+    /** The next File object we'll iterate over */
+    private File nextFile;
+
+    /** Have we set a next object? */
+    private boolean nextObjectSet = false;
+
+    /** Return only directories? */
+    private boolean iterateDirectories = false;
+
+    public FileIterator(Project project,
+                        Iterator fileSetIterator) {
+        this( project, fileSetIterator, false);
+    }
+
+    public FileIterator(Project project,
+                        Iterator fileSetIterator,
+                        boolean iterateDirectories) {
+        this.project = project;
+        this.fileSetIterator = fileSetIterator;
+        this.iterateDirectories = iterateDirectories;
+    }
+    
+    // Iterator interface
+    //-------------------------------------------------------------------------
+    
+    /** @return true if there is another object that matches the given predicate */
+    public boolean hasNext() {
+        if ( nextObjectSet ) {
+            return true;
+        } 
+        else {
+            return setNextObject();
+        }
+    }
+
+    /** @return the next object which matches the given predicate */
+    public Object next() {
+        if ( !nextObjectSet ) {
+            if (!setNextObject()) {
+                throw new NoSuchElementException();
+            }
+        }
+        nextObjectSet = false;
+        return nextFile;
+    }
+    
+    /**
+     * throws UnsupportedOperationException 
+     */
+    public void remove() {
+        throw new UnsupportedOperationException();
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Set nextObject to the next object. If there are no more 
+     * objects then return false. Otherwise, return true.
+     */
+    private boolean setNextObject() {
+        while (true) {
+            while (ds == null) {
+                if ( ! fileSetIterator.hasNext() ) {
+                    return false;
+                }
+                FileSet fs = (FileSet) fileSetIterator.next();
+                ds = fs.getDirectoryScanner(project);
+                ds.scan();
+                if (iterateDirectories) {
+                    files = ds.getIncludedDirectories();
+                } 
+                else {
+                    files = ds.getIncludedFiles();
+                }
+                if ( files.length > 0 ) {
+                    fileIndex = -1;
+                    break;
+                }
+                else {
+                    ds = null;
+                }
+            }
+        
+            if ( ds != null && files != null ) {
+                if ( ++fileIndex < files.length ) {
+                    nextFile = new File( ds.getBasedir(), files[fileIndex] );
+                    nextObjectSet = true;
+                    return true;
+                }
+                else {
+                    ds = null;
+                }
+            }
+        }
+    }
+}
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/ant/FileScanner.java b/groovy/src/main/org/codehaus/groovy/ant/FileScanner.java
new file mode 100644
index 0000000..3f8e61b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/FileScanner.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ant;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+
+/** 
+ * <p><code>FileScanner</code> is a bean which allows the iteration
+ * over a number of files from a colleciton of FileSet instances.
+ *
+ * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
+ * @author Marc Guillemot
+ * @version $Revision$
+ */
+public class FileScanner extends Task {
+
+    /** FileSets */
+    private List filesets = new ArrayList();
+
+    public FileScanner() {
+    }
+    
+    public FileScanner(final Project project) {
+        setProject(project);
+    }
+    
+    public Iterator iterator() {
+        return new FileIterator(getProject(), filesets.iterator());
+    }
+
+    public Iterator directories() {
+        return new FileIterator(getProject(), filesets.iterator(), true);
+    }
+
+    public boolean hasFiles() {
+        return filesets.size() > 0;
+    }
+
+    /**
+     * Clears any file sets that have been added to this scanner
+     */
+    public void clear() {
+        filesets.clear();
+    }
+
+    // Properties
+    //-------------------------------------------------------------------------
+
+    /**
+     * Adds a set of files (nested fileset attribute).
+     */
+    public void addFileset(FileSet set) {
+        filesets.add(set);
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ant/GenerateStubsTask.java b/groovy/src/main/org/codehaus/groovy/ant/GenerateStubsTask.java
new file mode 100644
index 0000000..14ce7c5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/GenerateStubsTask.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.ant;
+
+import groovy.lang.GroovyClassLoader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.tools.javac.JavaStubCompilationUnit;
+
+import java.io.File;
+
+/**
+ * Generates Java stubs from Groovy sources.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class GenerateStubsTask
+    extends CompileTaskSupport
+{
+    protected void compile() {
+        GroovyClassLoader gcl = createClassLoader();
+        JavaStubCompilationUnit compilation = new JavaStubCompilationUnit(config, gcl, destdir);
+
+        int count = 0;
+        
+        String[] list = src.list();
+        for (int i = 0; i < list.length; i++) {
+            File basedir = getProject().resolveFile(list[i]);
+            if (!basedir.exists()) {
+                throw new BuildException("Source directory does not exist: " + basedir, getLocation());
+            }
+
+            DirectoryScanner scanner = getDirectoryScanner(basedir);
+            String[] includes = scanner.getIncludedFiles();
+
+            log.debug("Including files from: " + basedir);
+
+            for (int j=0; j < includes.length; j++) {
+                log.debug("    "  + includes[j]);
+                
+                File file = new File(basedir, includes[j]);
+                compilation.addSourceFile(file);
+
+                // Increment the count for each non/java src we found
+                if (!includes[j].endsWith(".java")) {
+                    count++;
+                }
+            }
+        }
+
+        if (count > 0) {
+            log.info("Generating " + count + " Java stub" + (count > 1 ? "s" : "") + " to " + destdir);
+
+            // Generate the stubs
+            compilation.compile(Phases.CONVERSION);
+        }
+        else {
+            log.info("No sources found for stub generation");
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ant/Groovy.java b/groovy/src/main/org/codehaus/groovy/ant/Groovy.java
new file mode 100644
index 0000000..17559b1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/Groovy.java
@@ -0,0 +1,405 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.ant;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyShell;
+import groovy.lang.Script;
+import groovy.util.AntBuilder;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.*;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.tools.ErrorReporter;
+
+import java.io.*;
+import java.lang.reflect.Field;
+import java.util.Vector;
+
+/**
+ * Executes a series of Groovy statements.
+ *
+ * <p>Statements can
+ * either be read in from a text file using the <i>src</i> attribute or from
+ * between the enclosing groovy tags.</p>
+ *
+ * @version $Id$
+ */
+public class Groovy extends Task
+{
+    private final LoggingHelper log = new LoggingHelper(this);
+
+    /**
+     * files to load
+     */
+    private Vector filesets = new Vector();
+
+    /**
+     * input file
+     */
+    private File srcFile = null;
+
+    /**
+     * input command
+     */
+    private String command = "";
+
+    /**
+     * Results Output file.
+     */
+    private File output = null;
+
+    /**
+     * Append to an existing file or overwrite it?
+     */
+    private boolean append = false;
+
+    private Path classpath;
+
+    /**
+     * Compiler configuration.
+     *
+     * Used to specify the debug output to print stacktraces in case something fails.
+     * TODO: Could probably be reused to specify the encoding of the files to load or other properties.
+     */
+    private CompilerConfiguration configuration = new CompilerConfiguration();
+
+    private Commandline cmdline = new Commandline();
+
+    /**
+     * Enable compiler to report stack trace information if a problem occurs
+     * during compilation.
+     * @param stacktrace set to true to enable stacktrace reporting
+     */
+    public void setStacktrace(boolean stacktrace) {
+        configuration.setDebug(stacktrace);
+    }
+
+
+    /**
+     * Set the name of the file to be run. The folder of the file is automatically added to the classpath.
+     * Required unless statements are enclosed in the build file
+     */
+    public void setSrc(final File srcFile) {
+        this.srcFile = srcFile;
+    }
+
+    /**
+     * Set an inline command to execute.
+     * NB: Properties are not expanded in this text.
+     */
+    public void addText(String txt) {
+        log("addText('"+txt+"')", Project.MSG_VERBOSE);
+        this.command += txt;
+    }
+
+    /**
+     * Adds a set of files (nested fileset attribute).
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Set the output file;
+     * optional, defaults to the Ant log.
+     */
+    public void setOutput(File output) {
+        this.output = output;
+    }
+
+    /**
+     * Whether output should be appended to or overwrite
+     * an existing file.  Defaults to false.
+     *
+     * @param append set to true to append
+     */
+    public void setAppend(boolean append) {
+        this.append = append;
+    }
+
+
+    /**
+     * Sets the classpath for loading.
+     * @param classpath The classpath to set
+     */
+    public void setClasspath(final Path classpath) {
+        this.classpath = classpath;
+    }
+
+    /**
+     * Returns a new path element that can be configured.
+     * Gets called for instance by Ant when it encounters a nested &lt;classpath&gt; element.
+     *
+     * @return the resulting created path
+     */
+    public Path createClasspath() {
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        return this.classpath.createPath();
+    }
+
+    /**
+     * Set the classpath for loading
+     * using the classpath reference.
+     *
+     * @param ref the refid to use
+     */
+    public void setClasspathRef(final Reference ref) {
+        createClasspath().setRefid(ref);
+    }
+
+    /**
+     * Gets the classpath.
+     * @return Returns a Path
+     */
+    public Path getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * Load the file and then execute it
+     */
+    public void execute() throws BuildException {
+        log.debug("execute()");
+
+        command = command.trim();
+
+        if (srcFile == null && command.length() == 0
+            && filesets.isEmpty()) {
+            throw new BuildException("Source file does not exist!", getLocation());
+        }
+
+        if (srcFile != null && !srcFile.exists()) {
+            throw new BuildException("Source file does not exist!", getLocation());
+        }
+
+        // deal with the filesets
+        for (int i = 0; i < filesets.size(); i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            File srcDir = fs.getDir(getProject());
+
+            String[] srcFiles = ds.getIncludedFiles();
+        }
+
+        try {
+            PrintStream out = System.out;
+            try {
+                if (output != null) {
+                    log.verbose("Opening PrintStream to output file " + output);
+                    
+                    out = new PrintStream(
+                              new BufferedOutputStream(
+                                  new FileOutputStream(output
+                                                       .getAbsolutePath(),
+                                                       append)));
+                }
+
+                // if there are no groovy statements between the enclosing Groovy tags
+                // then read groovy statements in from a text file using the src attribute
+                if (command == null || command.trim().length() == 0) {
+                	createClasspath().add(new Path(getProject(), srcFile.getParentFile().getCanonicalPath()));
+                    command = getText(new BufferedReader(new FileReader(srcFile)));
+                }
+
+
+                if (command != null) {
+                    execGroovy(command,out);
+                } else {
+                    throw new BuildException("Source file does not exist!", getLocation());
+                }
+
+            } finally {
+                if (out != null && out != System.out) {
+                    out.close();
+                }
+            }
+        } catch (IOException e) {
+            throw new BuildException(e, getLocation());
+        }
+
+        log.verbose("statements executed successfully");
+    }
+
+
+    private static String getText(BufferedReader reader) throws IOException {
+        StringBuffer answer = new StringBuffer();
+        // reading the content of the file within a char buffer allow to keep the correct line endings
+        char[] charBuffer = new char[4096];
+        int nbCharRead = 0;
+        while ((nbCharRead = reader.read(charBuffer)) != -1) {
+            // appends buffer
+            answer.append(charBuffer, 0, nbCharRead);
+        }
+        reader.close();
+        return answer.toString();
+    }
+
+    public Commandline.Argument createArg() {
+        return cmdline.createArgument();
+    }
+
+    /**
+     * Read in lines and execute them.
+     *
+     * @param reader the reader from which to get the groovy source to exec
+     */
+    protected void runStatements(Reader reader, PrintStream out)
+        throws IOException {
+        log.debug("runStatements()");
+        
+        StringBuffer txt = new StringBuffer();
+        String line = "";
+
+        BufferedReader in = new BufferedReader(reader);
+
+        while ((line = in.readLine()) != null) {
+            line = getProject().replaceProperties(line);
+
+            if (line.indexOf("--") >= 0) {
+                txt.append("\n");
+            }
+        }
+        // Catch any statements not followed by ;
+        if (!txt.toString().equals("")) {
+            execGroovy(txt.toString(), out);
+        }
+    }
+
+
+    /**
+     * Exec the statement.
+     *
+     * @param txt the groovy source to exec
+     */
+    protected void execGroovy(final String txt, final PrintStream out) {
+        // TODO: out never used?
+        log.debug("execGroovy()");
+
+        // Check and ignore empty statements
+        if ("".equals(txt.trim())) {
+            return;
+        }
+
+        log.verbose("Groovy: " + txt);
+
+        //log(getClasspath().toString(),Project.MSG_VERBOSE);
+        Object mavenPom = null;
+        final Project project = getProject();
+        final ClassLoader baseClassLoader;
+        // treat the case Ant is run through Maven, and
+        if ("org.apache.commons.grant.GrantProject".equals(project.getClass().getName())) {
+            try {
+               final Object propsHandler = project.getClass().getMethod("getPropsHandler", new Class[0]).invoke(project, new Object[0]);
+               final Field contextField = propsHandler.getClass().getDeclaredField("context");
+               contextField.setAccessible(true);
+               final Object context = contextField.get(propsHandler);
+               mavenPom = InvokerHelper.invokeMethod(context, "getProject", new Object[0]);
+            }
+            catch (Exception e) {
+                throw new BuildException("Impossible to retrieve Maven's Ant project: " + e.getMessage(), getLocation());
+            }
+            // let ASM lookup "root" classloader
+            Thread.currentThread().setContextClassLoader(GroovyShell.class.getClassLoader());
+            // load groovy into "root.maven" classloader instead of "root" so that
+            // groovy script can access Maven classes
+            baseClassLoader = mavenPom.getClass().getClassLoader();
+        } else {
+            baseClassLoader = GroovyShell.class.getClassLoader();
+        }
+
+        final String scriptName = computeScriptName();
+        final GroovyClassLoader classLoader = new GroovyClassLoader(baseClassLoader);
+        addClassPathes(classLoader);
+        
+        final GroovyShell groovy = new GroovyShell(classLoader, new Binding(), configuration);
+        try {
+            final Script script = groovy.parse(txt, scriptName);
+            script.setProperty("ant", new AntBuilder(this));
+            script.setProperty("project", project);
+            script.setProperty("properties", new AntProjectPropertiesDelegate(project));
+            script.setProperty("target", getOwningTarget());
+            script.setProperty("task", this);
+            script.setProperty("args", cmdline.getCommandline());
+            if (mavenPom != null) {
+                script.setProperty("pom", mavenPom);
+            }
+            script.run();
+        }
+        catch (final CompilationFailedException e) {
+            StringWriter writer = new StringWriter();
+            new ErrorReporter( e, false ).write( new PrintWriter(writer) );
+            String message = writer.toString();
+            throw new BuildException("Script Failed: "+ message, e, getLocation());
+        }
+    }
+
+
+    /**
+     * Try to build a script name for the script of the groovy task to have an helpful value in stack traces in case of exception
+     * @return the name to use when compiling the script
+     */
+	private String computeScriptName() {
+		if (srcFile != null) {
+			return srcFile.getAbsolutePath();
+		}
+		else {
+			String name = "embedded_script_in_";
+			if (getLocation().getFileName().length() > 0)
+				name += getLocation().getFileName().replaceAll("[^\\w_\\.]", "_");
+			else
+				name += "groovy_Ant_task";
+
+			return name;
+		}
+	}
+
+
+	/**
+	 * Adds the class pathes (if any)
+	 * @param classLoader the classloader to configure
+	 */
+	protected void addClassPathes(final GroovyClassLoader classLoader)
+	{
+		if (classpath != null)
+		{
+			for (int i = 0; i < classpath.list().length; i++)
+			{
+	            classLoader.addClasspath(classpath.list()[i]);
+			}
+		}
+	}
+
+    /**
+     * print any results in the statement.
+     * @param out the output PrintStream to print to
+     */
+    protected void printResults(PrintStream out) {
+        log.debug("printResults()");
+        StringBuffer line = new StringBuffer();
+        out.println(line);
+        line = new StringBuffer();
+        out.println();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ant/Groovyc.java b/groovy/src/main/org/codehaus/groovy/ant/Groovyc.java
new file mode 100644
index 0000000..bccc1b8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/Groovyc.java
@@ -0,0 +1,643 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ant;
+
+import groovy.lang.GroovyClassLoader;
+import org.apache.commons.cli.*;
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.listener.AnsiColorLogger;
+import org.apache.tools.ant.taskdefs.Javac;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.GlobPatternMapper;
+import org.apache.tools.ant.util.SourceFileScanner;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.tools.ErrorReporter;
+import org.codehaus.groovy.tools.StringHelper;
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit;
+import org.codehaus.groovy.tools.javac.JavaCompiler;
+import org.codehaus.groovy.tools.javac.JavaCompilerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Compiles Groovy source files. This task can take the following
+ * arguments:
+ * <ul>
+ * <li>srcdir
+ * <li>destdir
+ * <li>classpath
+ * <li>stacktrace
+ * <li>jointCompilation
+ * <li>verbose
+ * <li>encoding
+ * <li>failonerror
+ * </ul>
+ * Of these arguments, the <b>srcdir</b> and <b>destdir</b> are required.
+ * <p>
+ * When this task executes, it will recursively scan srcdir and
+ * destdir looking for Groovy source files to compile. This task makes its
+ * compile decision based on timestamp.
+ * 
+ * Based heavily on the Javac implementation in Ant
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Hein Meling
+ * @version $Revision$ 
+ */
+public class Groovyc extends MatchingTask
+{
+    private final LoggingHelper log = new LoggingHelper(this);
+
+
+    private Path compileClasspath;
+    private Path compileSourcepath;
+    private String encoding;
+    private Javac javac;
+
+    protected File destDir;
+    protected Path src;    
+    protected CompilerConfiguration configuration = new CompilerConfiguration();
+    protected boolean failOnError = true;
+    protected boolean listFiles = false;
+    protected File[] compileList = new File[0];
+    
+    private boolean jointCompilation;
+
+    public static void main(String[] args) {
+
+        Project project = new Project();
+        DefaultLogger logger = new AnsiColorLogger();
+        logger.setMessageOutputLevel(Project.MSG_INFO);
+        project.addBuildListener(logger);
+        // project.init();
+        
+        Groovyc compiler = new Groovyc();
+        compiler.setProject(project);
+
+        args = compiler.evalCompilerFlags(args);
+        
+        String dest = ".";
+        String src = ".";
+        boolean listFiles = false;
+        if (args.length > 0) {
+            dest = args[0];
+        }
+        if (args.length > 1) {
+            src = args[1];
+        }
+        if (args.length > 2) {
+            String flag = args[2];
+            if (flag.equalsIgnoreCase("true")) {
+                listFiles = true;
+            }
+        }
+        
+        compiler.setSrcdir(new Path(project, src));
+        compiler.setDestdir(project.resolveFile(dest));
+        compiler.setListfiles(listFiles);
+        
+        compiler.execute();
+    }
+
+    private String[] evalCompilerFlags(String[] args) {
+        //
+        // Parse the command line
+        
+        Options options = new Options();
+        options.addOption(
+                OptionBuilder.withArgName( "property=value" )
+                .withValueSeparator('=')
+                .hasArgs(2)
+                .create( "J" ));
+        options.addOption(
+                OptionBuilder.withArgName( "property=value" )
+                .hasArg()
+                .create( "F" ));
+        options.addOption(OptionBuilder.withLongOpt("jointCompilation").create('j'));
+        
+        PosixParser cliParser = new PosixParser();
+
+        CommandLine cli;
+        try {
+            cli = cliParser.parse(options, args);
+        } catch (ParseException e) {
+            throw new BuildException(e);
+        }
+        
+        jointCompilation = cli.hasOption('j');
+        if (jointCompilation) {
+            Map compilerOptions =  new HashMap();
+            
+            String[] opts = cli.getOptionValues("J");
+            compilerOptions.put("namedValues", opts);
+            
+            opts = cli.getOptionValues("F");
+            compilerOptions.put("flags", opts);
+            
+            compilerOptions.put("stubDir", createTempDir());    
+            configuration.setJointCompilationOptions(compilerOptions);
+        }            
+        
+        return cli.getArgs();
+    }
+
+    public Groovyc() {
+    }
+
+    /**
+     * Adds a path for source compilation.
+     *
+     * @return a nested src element.
+     */
+    public Path createSrc() {
+        if (src == null) {
+            src = new Path(getProject());
+        }
+        return src.createPath();
+    }
+
+    /**
+     * Recreate src.
+     *
+     * @return a nested src element.
+     */
+    protected Path recreateSrc() {
+        src = null;
+        return createSrc();
+    }
+
+    /**
+     * Set the source directories to find the source Java files.
+     * @param srcDir the source directories as a path
+     */
+    public void setSrcdir(Path srcDir) {
+        if (src == null) {
+            src = srcDir;
+        }
+        else {
+            src.append(srcDir);
+        }
+    }
+
+    /**
+     * Gets the source dirs to find the source java files.
+     * @return the source directorys as a path
+     */
+    public Path getSrcdir() {
+        return src;
+    }
+
+    /**
+     * Set the destination directory into which the Java source
+     * files should be compiled.
+     * @param destDir the destination director
+     */
+    public void setDestdir(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * Enable verbose compiling which will display which files
+     * are being compiled
+     */
+    public void setVerbose(boolean verbose) {
+        configuration.setVerbose( verbose );
+    }
+
+    /**
+     * Enable compiler to report stack trace information if a problem occurs
+     * during compilation.
+     */
+    public void setStacktrace(boolean stacktrace) {
+        configuration.setDebug(stacktrace);
+    }
+
+    /**
+     * Gets the destination directory into which the java source files
+     * should be compiled.
+     * @return the destination directory
+     */
+    public File getDestdir() {
+        return destDir;
+    }
+
+    /**
+     * Set the sourcepath to be used for this compilation.
+     * @param sourcepath the source path
+     */
+    public void setSourcepath(Path sourcepath) {
+        if (compileSourcepath == null) {
+            compileSourcepath = sourcepath;
+        }
+        else {
+            compileSourcepath.append(sourcepath);
+        }
+    }
+
+    /**
+     * Gets the sourcepath to be used for this compilation.
+     * @return the source path
+     */
+    public Path getSourcepath() {
+        return compileSourcepath;
+    }
+
+    /**
+     * Adds a path to sourcepath.
+     * @return a sourcepath to be configured
+     */
+    public Path createSourcepath() {
+        if (compileSourcepath == null) {
+            compileSourcepath = new Path(getProject());
+        }
+        return compileSourcepath.createPath();
+    }
+
+    /**
+     * Adds a reference to a source path defined elsewhere.
+     * @param r a reference to a source path
+     */
+    public void setSourcepathRef(Reference r) {
+        createSourcepath().setRefid(r);
+    }
+
+    /**
+     * Set the classpath to be used for this compilation.
+     *
+     * @param classpath an Ant Path object containing the compilation classpath.
+     */
+    public void setClasspath(Path classpath) {
+        if (compileClasspath == null) {
+            compileClasspath = classpath;
+        }
+        else {
+            compileClasspath.append(classpath);
+        }
+    }
+
+    /**
+     * Gets the classpath to be used for this compilation.
+     * @return the class path
+     */
+    public Path getClasspath() {
+        return compileClasspath;
+    }
+
+    /**
+     * Adds a path to the classpath.
+     * @return a class path to be configured
+     */
+    public Path createClasspath() {
+        if (compileClasspath == null) {
+            compileClasspath = new Path(getProject());
+        }
+        return compileClasspath.createPath();
+    }
+
+    /**
+     * Adds a reference to a classpath defined elsewhere.
+     * @param r a reference to a classpath
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * Returns the encoding to be used when creating files.<br/>
+     * If no encoding value has been set it will default to
+     * System.properties("file.encoding")
+     *
+     * @return the file encoding to use
+     */
+    public String createEncoding() {
+        if (encoding == null) {
+            encoding = System.getProperty("file.encoding");
+        }
+        return encoding;
+    }
+
+    /**
+     * Sets the file encoding for generated files.
+     * @param encoding the file encoding to be used
+     */ 
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Returns the encoding to be used when creating files.
+     * @return the file encoding to use
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * If true, list the source files being handed off to the compiler.
+     * @param list if true list the source files
+     */
+    public void setListfiles(boolean list) {
+        listFiles = list;
+    }
+
+    /**
+     * Get the listfiles flag.
+     * @return the listfiles flag
+     */
+    public boolean getListfiles() {
+        return listFiles;
+    }
+
+    /**
+     * Indicates whether the build will continue
+     * even if there are compilation errors; defaults to true.
+     * @param fail if true halt the build on failure
+     */
+    public void setFailonerror(boolean fail) {
+        failOnError = fail;
+    }
+
+    /**
+     * @param proceed inverse of failonerror
+     */
+    public void setProceed(boolean proceed) {
+        failOnError = !proceed;
+    }
+
+    /**
+     * Gets the failonerror flag.
+     * @return the failonerror flag
+     */
+    public boolean getFailonerror() {
+        return failOnError;
+    }
+
+    /**
+     * Executes the task.
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        checkParameters();
+        resetFileLists();
+        if (javac!=null) jointCompilation=true;
+
+        // scan source directories and dest directory to build up
+        // compile lists
+        String[] list = src.list();
+        for (int i = 0; i < list.length; i++) {
+            File srcDir = getProject().resolveFile(list[i]);
+            if (!srcDir.exists()) {
+                throw new BuildException("srcdir \"" + srcDir.getPath() + "\" does not exist!", getLocation());
+            }
+
+            DirectoryScanner ds = this.getDirectoryScanner(srcDir);
+            String[] files = ds.getIncludedFiles();
+
+            scanDir(srcDir, destDir != null ? destDir : srcDir, files);
+        }
+
+        compile();
+    }
+
+    /**
+     * Clear the list of files to be compiled and copied..
+     */
+    protected void resetFileLists() {
+        compileList = new File[0];
+    }
+
+    /**
+     * Scans the directory looking for source files to be compiled.
+     * The results are returned in the class variable compileList
+     *
+     * @param srcDir   The source directory
+     * @param destDir  The destination directory
+     * @param files    An array of filenames
+     */
+    protected void scanDir(File srcDir, File destDir, String[] files) {
+        GlobPatternMapper m = new GlobPatternMapper();
+        m.setFrom("*.groovy");
+        m.setTo("*.class");
+        SourceFileScanner sfs = new SourceFileScanner(this);
+        File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);
+        addToCompileList(newFiles);
+        
+        if (jointCompilation) {
+            m.setFrom("*.java");
+            m.setTo("*.class");
+            newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);
+            addToCompileList(newFiles);
+        }
+    }
+    
+    protected void addToCompileList(File[] newFiles) {
+        if (newFiles.length > 0) {
+            File[] newCompileList = new File[compileList.length + newFiles.length];
+            System.arraycopy(compileList, 0, newCompileList, 0, compileList.length);
+            System.arraycopy(newFiles, 0, newCompileList, compileList.length, newFiles.length);
+            compileList = newCompileList;
+        }
+    }
+
+    /**
+     * Gets the list of files to be compiled.
+     * @return the list of files as an array
+     */
+    public File[] getFileList() {
+        return compileList;
+    }
+
+    protected void checkParameters() throws BuildException {
+        if (src == null) {
+            throw new BuildException("srcdir attribute must be set!", getLocation());
+        }
+        if (src.size() == 0) {
+            throw new BuildException("srcdir attribute must be set!", getLocation());
+        }
+
+        if (destDir != null && !destDir.isDirectory()) {
+            throw new BuildException(
+                "destination directory \"" + destDir + "\" does not exist or is not a directory",
+                getLocation());
+        }
+
+        if (encoding != null && !Charset.isSupported(encoding)) {
+            throw new BuildException("encoding \"\" not supported");
+        }
+    }
+
+    protected void compile() {
+        if (compileList.length == 0) {
+            log.info("No sources to compile");
+            return;
+        }
+
+        log.info("Compiling " + compileList.length +
+                " source file" + (compileList.length == 1 ? "" : "s") +
+                " to " + destDir);
+
+        if (listFiles) {
+            for (int i = 0; i < compileList.length; i++) {
+                String filename = compileList[i].getAbsolutePath();
+                log.info("    " + filename);
+            }
+        }
+
+        try {
+            Path classpath = getClasspath();
+            if (classpath != null) {
+                configuration.setClasspath(classpath.toString());
+            }
+            configuration.setTargetDirectory(destDir);
+
+            if (encoding != null) {
+                configuration.setSourceEncoding(encoding);
+            }
+
+            CompilationUnit unit = makeCompileUnit();
+            unit.addSources(compileList);
+            unit.compile();
+        }
+        catch (Exception e) {
+
+            StringWriter writer = new StringWriter();
+            new ErrorReporter( e, false ).write( new PrintWriter(writer) );
+            String message = writer.toString();
+
+            if (failOnError) {
+                throw new BuildException(message, e, getLocation());
+            }
+            else {
+                log(message, Project.MSG_ERR);
+            }
+
+        }
+    }
+    
+    protected CompilationUnit makeCompileUnit() {
+        if (javac!=null) {
+            Map compilerOptions =  new HashMap();
+            compilerOptions.put("stubDir", createTempDir());    
+            configuration.setJointCompilationOptions(compilerOptions);
+            jointCompilation = true;
+        }
+        
+        if (jointCompilation) {            
+            JavaAwareCompilationUnit unit = new JavaAwareCompilationUnit(configuration, buildClassLoaderFor());
+            if (javac!=null) {
+                final JavaCompiler compiler = new JavaCompiler() {
+                    public void compile(List files, CompilationUnit cu) {
+                        // forward options
+                        if (javac.getClasspath()==null) {
+                            javac.setClasspath(compileClasspath);
+                        }
+                        if (javac.getSourcepath()==null && compileSourcepath!=null) {
+                            javac.createSourcepath().add(compileSourcepath);
+                        }
+                        if (javac.getEncoding()==null) {
+                            javac.setEncoding(encoding);
+                        }
+                        javac.setDestdir(destDir);
+                        
+                        Path p = javac.createSrc();
+                        p.add(src);
+                        
+                        Path tmpDir = new Path(getProject());
+                        File dir = (File) cu.getConfiguration().getJointCompilationOptions().get("stubDir");
+                        tmpDir.setLocation(dir);
+                        p.add(tmpDir);
+                        javac.execute();
+                    }
+                };
+                unit.setCompilerFactory(new JavaCompilerFactory() {
+                    public JavaCompiler createCompiler(CompilerConfiguration config) {
+                        return compiler;
+                    }
+                });
+            }
+            return unit;
+        } else {
+            return new CompilationUnit(configuration, null, buildClassLoaderFor());
+        }
+    }
+
+    protected File createTempDir()  {
+        File tempFile;
+        try {
+            tempFile = File.createTempFile("generated-", "java-source");
+            tempFile.delete();
+            tempFile.mkdirs();
+        } catch (IOException e) {
+            throw new BuildException(e);
+        }
+        return tempFile;
+    }
+
+    protected GroovyClassLoader buildClassLoaderFor() {
+        ClassLoader parent = this.getClass().getClassLoader();
+        if (parent instanceof AntClassLoader) {
+            AntClassLoader antLoader = (AntClassLoader) parent;
+            String[] pathElm = antLoader.getClasspath().split(File.pathSeparator);
+            List classpath = configuration.getClasspath();
+            /*
+             * Iterate over the classpath provided to groovyc, and add any missing path
+             * entries to the AntClassLoader.  This is a workaround, since for some reason
+             * 'directory' classpath entries were not added to the AntClassLoader' classpath. 
+             */
+            for (Iterator iter = classpath.iterator(); iter.hasNext();) {
+                String cpEntry = (String) iter.next();
+                boolean found = false;
+                for (int i = 0; i < pathElm.length; i++) {
+                    if (cpEntry.equals(pathElm[i])) {
+                        found = true;
+                        break;
+                    }
+                }
+		/*
+		 * fix for GROOVY-2284
+		 * seems like AntClassLoader doesn't check if the file
+		 * may not exist in the classpath yet
+		 */ 
+                if (!found && new File(cpEntry).exists())
+                    antLoader.addPathElement(cpEntry);
+            }
+        }
+        return new GroovyClassLoader(parent, configuration);
+    }
+    
+    public void setJointCompilationOptions(String options) {
+        String[] args = StringHelper.tokenizeUnquoted(options);
+        evalCompilerFlags(args);
+    }  
+    
+    public Javac createJavac() {
+        if (javac!=null) throw new BuildException("nested javac element allowed only once");
+        javac = new Javac();
+        return javac;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ant/GroovycTask.java b/groovy/src/main/org/codehaus/groovy/ant/GroovycTask.java
new file mode 100644
index 0000000..500110b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/GroovycTask.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.ant;
+
+import groovy.lang.GroovyClassLoader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.GlobPatternMapper;
+import org.apache.tools.ant.util.SourceFileScanner;
+
+import org.codehaus.groovy.control.CompilationUnit;
+
+import java.io.File;
+
+/**
+ * Compiles Groovy source files.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class GroovycTask
+    extends CompileTaskSupport
+{
+    protected boolean force;
+
+    public void setForce(final boolean flag) {
+        this.force = flag;
+    }
+
+    protected void compile() {
+        Path path = getClasspath();
+        if (path != null) {
+            config.setClasspath(path.toString());
+        }
+
+        config.setTargetDirectory(destdir);
+
+        GroovyClassLoader gcl = createClassLoader();
+        CompilationUnit compilation = new CompilationUnit(config, null, gcl);
+
+        GlobPatternMapper mapper = new GlobPatternMapper();
+        mapper.setFrom("*.groovy");
+        mapper.setTo("*.class");
+        
+        int count = 0;
+        String[] list = src.list();
+
+        for (int i = 0; i < list.length; i++) {
+            File basedir = getProject().resolveFile(list[i]);
+            
+            if (!basedir.exists()) {
+                throw new BuildException("Source directory does not exist: " + basedir, getLocation());
+            }
+
+            DirectoryScanner scanner = getDirectoryScanner(basedir);
+            String[] includes = scanner.getIncludedFiles();
+
+            if (force) {
+                log.debug("Forcefully including all files from: " + basedir);
+
+                for (int j=0; j < includes.length; j++) {
+                    File file = new File(basedir, includes[j]);
+                    log.debug("    "  + file);
+
+                    compilation.addSource(file);
+                    count++;
+                }
+            }
+            else {
+                log.debug("Including changed files from: " + basedir);
+
+                SourceFileScanner sourceScanner = new SourceFileScanner(this);
+                File[] files = sourceScanner.restrictAsFiles(includes, basedir, destdir, mapper);
+
+                for (int j=0; j < files.length; j++) {
+                    log.debug("    "  + files[j]);
+
+                    compilation.addSource(files[j]);
+                    count++;
+                }
+            }
+        }
+
+        if (count > 0) {
+            log.info("Compiling " + count + " source file" + (count > 1 ? "s" : "") + " to " + destdir);
+
+            compilation.compile();
+        }
+        else {
+            log.info("No sources found to compile");
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/ant/Groovydoc.java b/groovy/src/main/org/codehaus/groovy/ant/Groovydoc.java
new file mode 100644
index 0000000..0061de7
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/Groovydoc.java
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ant;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PatternSet;
+import org.codehaus.groovy.tools.groovydoc.ClasspathResourceManager;
+import org.codehaus.groovy.tools.groovydoc.FileOutputTool;
+import org.codehaus.groovy.tools.groovydoc.GroovyDocTool;
+
+/**
+ * Access to the GroovyDoc tool from Ant.
+ *
+ * @version $Id$
+ */
+public class Groovydoc extends Task
+{
+    private final LoggingHelper log = new LoggingHelper(this);
+    
+    private Path sourcePath;
+    private File destDir;
+    private List packageNames;
+    private List excludePackageNames;
+	private String windowTitle;
+	private boolean privateScope;
+    private boolean useDefaultExcludes;
+    private boolean includeNoSourcePackages;
+    private List packageSets;
+	private List sourceFilesToDoc;
+
+
+    public Groovydoc() {
+    	packageNames = new ArrayList();
+    	excludePackageNames = new ArrayList();
+    	packageSets = new ArrayList();
+    	sourceFilesToDoc = new ArrayList();
+        privateScope = false;
+        useDefaultExcludes = true;
+        includeNoSourcePackages = false;
+    }
+    /**
+     * Specify where to find source file
+     *
+     * @param src a Path instance containing the various source directories.
+     */
+    public void setSourcepath(Path src) {
+        if (sourcePath == null) {
+            sourcePath = src;
+        } else {
+            sourcePath.append(src);
+        }
+    }
+
+    /**
+     * Set the directory where the Javadoc output will be generated.
+     *
+     * @param dir the destination directory.
+     */
+    public void setDestdir(File dir) {
+        destDir = dir;
+        // todo: maybe tell groovydoc to use file output
+    }
+    
+    /**
+     * Set the package names to be processed.
+     *
+     * @param packages a comma separated list of packages specs
+     *        (may be wildcarded).
+     *
+     */
+    public void setPackagenames(String packages) {
+        StringTokenizer tok = new StringTokenizer(packages, ",");
+        while (tok.hasMoreTokens()) {
+            String packageName = tok.nextToken();
+            packageNames.add(packageName);
+        }
+    }
+
+    public void setUse(boolean b) {
+    	//ignore as 'use external file' irrelevant with groovydoc :-)
+    }
+    
+    /**
+     * Set the title to be placed in the HTML &lt;title&gt; tag of the
+     * generated documentation.
+     *
+     * @param title the window title to use.
+     */
+    public void setWindowtitle(String title) {
+    	windowTitle = title;
+    }
+    
+    /**
+     * Indicate whether all classes and
+     * members are to be included in the scope processed
+     *
+     * @param b true if scope is to be private level.
+     */
+    public void setPrivate(boolean b) {
+    	privateScope = b;
+    }
+
+    /**
+     * Add the directories matched by the nested dirsets to the Vector
+     * and the base directories of the dirsets to the Path.  It also
+     * handles the packages and excludepackages attributes and
+     * elements.
+     *
+     * @since 1.5
+     */
+    private void parsePackages(List resultantPackages, Path sp) {
+        List addedPackages = new ArrayList();
+        List dirSets = new ArrayList(packageSets);
+
+        // for each sourcePath entry, add a directoryset with includes
+        // taken from packagenames attribute and nested package
+        // elements and excludes taken from excludepackages attribute
+        // and nested excludepackage elements
+        if (sourcePath != null) {
+            PatternSet ps = new PatternSet();
+            if (packageNames.size() > 0) {
+                Iterator itr = packageNames.iterator();
+                while (itr.hasNext()) {
+                    String p = (String) itr.next();
+                    String pkg = p.replace('.', '/');
+                    if (pkg.endsWith("*")) {
+                        pkg += "*";
+                    }
+                    ps.createInclude().setName(pkg);
+                }
+            } else {
+                ps.createInclude().setName("**");
+            }
+
+            Iterator itr2 = excludePackageNames.iterator();
+            while (itr2.hasNext()) {
+                String p = (String) itr2.next();
+                String pkg = p.replace('.', '/');
+                if (pkg.endsWith("*")) {
+                    pkg += "*";
+                }
+                ps.createExclude().setName(pkg);
+            }
+
+
+            String[] pathElements = sourcePath.list();
+            for (int i = 0; i < pathElements.length; i++) {
+                File dir = new File(pathElements[i]);
+                if (dir.isDirectory()) {
+                    DirSet ds = new DirSet();
+                    ds.setDefaultexcludes(useDefaultExcludes);
+                    ds.setDir(dir);
+                    ds.createPatternSet().addConfiguredPatternset(ps);
+                    dirSets.add(ds);
+                } else {
+                    log.warn("Skipping " + pathElements[i] + " since it is no directory.");
+                }
+            }
+        }
+
+        Iterator itr3 = dirSets.iterator();
+        while (itr3.hasNext()) {
+            DirSet ds = (DirSet) itr3.next();
+            File baseDir = ds.getDir(getProject());
+            log.debug("scanning " + baseDir + " for packages.");
+            DirectoryScanner dsc = ds.getDirectoryScanner(getProject());
+            String[] dirs = dsc.getIncludedDirectories();
+            boolean containsPackages = false;
+            for (int i = 0; i < dirs.length; i++) {
+                // are there any groovy or java files in this directory?
+                File pd = new File(baseDir, dirs[i]);
+                String[] files = pd.list(new FilenameFilter () {
+                        public boolean accept(File dir1, String name) {
+                            return name.endsWith(".java")
+                                || name.endsWith(".groovy")
+                                || name.endsWith(".gv")
+                                || name.endsWith(".gvy")
+                                || name.endsWith(".gsh")
+                                || (includeNoSourcePackages
+                                    && name.equals("package.html"));
+                        }
+                    });
+
+                Iterator itr4 = Arrays.asList(files).iterator();
+                while (itr4.hasNext()) {
+                	String filename = (String) itr4.next();
+                	sourceFilesToDoc.add(dirs[i] + File.separator + filename);
+                }
+                
+                if (files.length > 0) {
+                    if ("".equals(dirs[i])) {
+                        log.warn(baseDir
+                            + " contains source files in the default package,"
+                            + " you must specify them as source files"
+                            + " not packages.");
+                    } else {
+                        containsPackages = true;
+                        String packageName =
+                            dirs[i].replace(File.separatorChar, '.');
+                        if (!addedPackages.contains(packageName)) {
+                            addedPackages.add(packageName);
+                            resultantPackages.add(packageName);
+                        }
+                    }
+                }
+            }
+            if (containsPackages) {
+                // We don't need to care for duplicates here,
+                // Path.list does it for us.
+                sp.createPathElement().setLocation(baseDir);
+            } else {
+                log.verbose(baseDir + " doesn\'t contain any packages, dropping it.");
+            }
+        }
+    }
+
+    
+    public void execute() throws BuildException {
+    	// do it
+
+        List packagesToDoc = new ArrayList();
+        Path sourceDirs = new Path(getProject());
+
+        if (sourcePath != null) {
+            sourceDirs.addExisting(sourcePath);
+        }
+        parsePackages(packagesToDoc, sourceDirs);      
+        
+  		GroovyDocTool htmlTool = new GroovyDocTool(
+				new ClasspathResourceManager(), // we're gonna get the default templates out of the dist jar file
+				sourcePath.toString(), // sourcepath                     - TODO multiple paths need to be handled here
+				new String[] { // top level templates
+						"org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/index.html",
+						"org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/overview-frame.html", // needs all package names
+						"org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/allclasses-frame.html", // needs all packages / class names
+						"org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/overview-summary.html", // needs all packages
+						"org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/stylesheet.css",
+				},
+				new String[] { // package level templates
+						"org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/package-frame.html", 
+						"org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/package-summary.html"
+				},
+				new String[] { // class level templates
+						"org/codehaus/groovy/tools/groovydoc/gstring-templates/class-level/classDocName.html"
+				}
+				);
+
+		try {
+			Iterator itr = sourceFilesToDoc.iterator();
+			while (itr.hasNext()) {
+				htmlTool.add((String) itr.next());
+			}
+			
+			FileOutputTool output = new FileOutputTool();
+			htmlTool.renderToOutput(output, destDir.getCanonicalPath()); // TODO push destDir through APIs?
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ant/LoggingHelper.java b/groovy/src/main/org/codehaus/groovy/ant/LoggingHelper.java
new file mode 100644
index 0000000..1480350
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/LoggingHelper.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.ant;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Helper to make logging from Ant easier.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class LoggingHelper
+{
+    private Task owner;
+
+    public LoggingHelper(final Task owner) {
+        assert owner != null;
+
+        this.owner = owner;
+    }
+
+    public void error(final String msg) {
+        owner.log(msg, Project.MSG_ERR);
+    }
+
+    public void warn(final String msg) {
+        owner.log(msg, Project.MSG_WARN);
+    }
+
+    public void info(final String msg) {
+        owner.log(msg, Project.MSG_INFO);
+    }
+
+    public void verbose(final String msg) {
+        owner.log(msg, Project.MSG_VERBOSE);
+    }
+
+    public void debug(final String msg) {
+        owner.log(msg, Project.MSG_DEBUG);
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/ant/RootLoaderRef.java b/groovy/src/main/org/codehaus/groovy/ant/RootLoaderRef.java
new file mode 100644
index 0000000..5c1ab00
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/RootLoaderRef.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ 
+package org.codehaus.groovy.ant;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.codehaus.groovy.tools.LoaderConfiguration;
+import org.codehaus.groovy.tools.RootLoader;
+
+ 
+/**
+ * Sets the RootLoader as reference.
+ * Reexecution of this task will set a new instance of RootLoader for
+ * the reference. 
+ *
+ * arguments:
+ * <ul>
+ * <li>ref</li>
+ * <li>classpath</li>
+ * </ul>
+ * 
+ * all arguments are requiered. 
+ *
+ * As ant requieres an AntClassLoader as reference, this will create a RootLoader
+ * and set an AntClassLoader as child and stored in the reference. The AntClassLoader
+ * instance will not have a classpath nor will it have access to the classpath somehow,
+ * all loading is done by the RootLoader parent. To avoid problems with loading classes 
+ * multiple times and using them at the same time, this task will filter out the ant jars
+ * and the commons-logging jars. This only works if the ant jars are starting with "ant-" and
+ * the logging jar starts with "commons-logging-".
+ * 
+ * This was needed because if ant wants to access a task argument that uses for example a Path
+ * it look for a matching method which includes a matching class. But two classes of the same name
+ * with different classloaders are different, so ant would not be able to find the method.
+ *
+ * @see org.codehaus.groovy.tools.RootLoader
+ * @author Jochen Theodorou
+ * @version $Revision$ 
+ */
+public class RootLoaderRef extends MatchingTask {
+    private String name;
+    private Path taskClasspath;
+    
+    /**
+     * sets the name of the reference which should store the Loader
+     */
+    public void setRef(String n){
+        name = n;
+    }
+    
+    public void execute() throws BuildException {
+        if (taskClasspath==null || taskClasspath.size()==0) {
+            throw new BuildException("no classpath given");
+        }
+        Project project = getProject();
+        AntClassLoader loader = new AntClassLoader(makeRoot(),true);
+        project.addReference(name,loader);
+    }
+    
+    private RootLoader makeRoot() {
+        String[] list = taskClasspath.list();
+        LoaderConfiguration lc = new LoaderConfiguration();
+        for (int i=0; i<list.length; i++) {
+            if (list[i].matches(".*ant-[^/]*jar$")) {
+                continue;
+            }
+            if (list[i].matches(".*commons-logging-[^/]*jar$")) {
+                continue;
+            }
+            if (list[i].matches(".*xerces-[^/]*jar$")) {
+                continue;
+            }
+            lc.addFile(list[i]);
+        }
+        return new RootLoader(lc);
+    }
+    
+    /**
+     * Set the classpath to be used for this compilation.
+     *
+     * @param classpath an Ant Path object containing the compilation classpath.
+     */
+    public void setClasspath(Path classpath) {
+        if (taskClasspath == null) {
+            taskClasspath = classpath;
+        }
+        else {
+            taskClasspath.append(classpath);
+        }
+    }
+    
+    /**
+     * Adds a reference to a classpath defined elsewhere.
+     * @param r a reference to a classpath
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+    
+    /**
+     * Adds a path to the classpath.
+     * @return a class path to be configured
+     */
+    public Path createClasspath() {
+        if (taskClasspath == null) {
+            taskClasspath = new Path(getProject());
+        }
+        return taskClasspath.createPath();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ant/UberCompileTask.java b/groovy/src/main/org/codehaus/groovy/ant/UberCompileTask.java
new file mode 100644
index 0000000..9ea68fd
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/UberCompileTask.java
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.ant;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Javac;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Compiles Java and Groovy source files.
+ *
+ * This works by invoking the {@link GenerateStubsTask} task, then the
+ * {@link Javac} task and then the {@link GroovycTask}.  Each task can
+ * be configured by creating a nested element.  Common configuration
+ * such as the source dir and classpath is picked up from this tasks
+ * configuration.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class UberCompileTask
+    extends Task
+{
+    private final LoggingHelper log = new LoggingHelper(this);
+
+    private Path src;
+
+    private File destdir;
+
+    private Path classpath;
+
+    private GenStubsAdapter genStubsTask;
+
+    private GroovycAdapter groovycTask;
+
+    private JavacAdapter javacTask;
+
+    public Path createSrc() {
+        if (src == null) {
+            src = new Path(getProject());
+        }
+        return src.createPath();
+    }
+
+    public void setSrcdir(final Path dir) {
+        assert dir != null;
+
+        if (src == null) {
+            src = dir;
+        }
+        else {
+            src.append(dir);
+        }
+    }
+
+    public Path getSrcdir() {
+        return src;
+    }
+
+    public void setDestdir(final File dir) {
+        assert dir != null;
+
+        this.destdir = dir;
+    }
+
+    public void setClasspath(final Path path) {
+        assert path != null;
+        
+        if (classpath == null) {
+            classpath = path;
+        }
+        else {
+            classpath.append(path);
+        }
+    }
+
+    public Path getClasspath() {
+        return classpath;
+    }
+
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+
+        return classpath.createPath();
+    }
+
+    public void setClasspathRef(final Reference r) {
+        assert r != null;
+
+        createClasspath().setRefid(r);
+    }
+
+    public GenStubsAdapter createGeneratestubs() {
+        if (genStubsTask == null) {
+            genStubsTask = new GenStubsAdapter();
+            genStubsTask.setProject(getProject());
+        }
+        return genStubsTask;
+    }
+
+    public GroovycAdapter createGroovyc() {
+        if (groovycTask == null) {
+            groovycTask = new GroovycAdapter();
+            groovycTask.setProject(getProject());
+        }
+        return groovycTask;
+    }
+
+    public JavacAdapter createJavac() {
+        if (javacTask == null) {
+            javacTask = new JavacAdapter();
+            javacTask.setProject(getProject());
+        }
+        return javacTask;
+    }
+
+    protected void validate() throws BuildException {
+        if (src == null) {
+            throw new BuildException("Missing attribute: srcdir (or one or more nested <src> elements).", getLocation());
+        }
+
+        if (destdir == null) {
+            throw new BuildException("Missing attribute: destdir", getLocation());
+        }
+
+        if (!destdir.exists()) {
+            throw new BuildException("Destination directory does not exist: " + destdir, getLocation());
+        }
+    }
+    
+    public void execute() throws BuildException {
+        validate();
+
+        FileSet fileset;
+        
+        GenStubsAdapter genstubs = createGeneratestubs();
+        genstubs.classpath = classpath;
+        genstubs.src = src;
+        if (genstubs.destdir == null) {
+            genstubs.destdir = createTempDir();
+        }
+
+        fileset = genstubs.getFileSet();
+        if (!fileset.hasPatterns()) {
+            genstubs.createInclude().setName("**/*.java");
+            genstubs.createInclude().setName("**/*.groovy");
+        }
+                
+        JavacAdapter javac = createJavac();
+        javac.setSrcdir(src);
+        javac.setDestdir(destdir);
+        javac.setClasspath(classpath);
+
+        fileset = javac.getFileSet();
+        if (!fileset.hasPatterns()) {
+            javac.createInclude().setName("**/*.java");
+        }
+
+        // Include the stubs in the Javac compilation
+        javac.createSrc().createPathElement().setLocation(genstubs.destdir);
+
+        GroovycAdapter groovyc = createGroovyc();
+        groovyc.classpath = classpath;
+        groovyc.src = src;
+        groovyc.destdir = destdir;
+
+        //
+        // HACK: For now force all classes to compile, so we pick up stub changes
+        //
+        groovyc.force = true;
+        
+        fileset = groovyc.getFileSet();
+        if (!fileset.hasPatterns()) {
+            groovyc.createInclude().setName("**/*.groovy");
+        }
+
+        // Invoke each task in the right order
+        genstubs.execute();
+        javac.execute();
+        groovyc.execute();
+    }
+
+    private File createTempDir()  {
+        try {
+            File dir = File.createTempFile("groovy-", "stubs");
+            dir.delete();
+            dir.mkdirs();
+            return dir;
+        }
+        catch (IOException e) {
+            throw new BuildException(e, getLocation());
+        }
+    }
+
+    //
+    // Nested task adapters
+    //
+    
+    private class GenStubsAdapter
+        extends GenerateStubsTask
+    {
+        public FileSet getFileSet() {
+            return super.getImplicitFileSet();
+        }
+
+        public String getTaskName() {
+            return UberCompileTask.this.getTaskName() + ":genstubs";
+        }
+    }
+
+    private class JavacAdapter
+        extends Javac
+    {
+        public FileSet getFileSet() {
+            return super.getImplicitFileSet();
+        }
+
+        public String getTaskName() {
+            return UberCompileTask.this.getTaskName() + ":javac";
+        }
+    }
+
+    private class GroovycAdapter
+        extends GroovycTask
+    {
+        public FileSet getFileSet() {
+            return super.getImplicitFileSet();
+        }
+
+        public String getTaskName() {
+            return UberCompileTask.this.getTaskName() + ":groovyc";
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/ant/VerifyClass.java b/groovy/src/main/org/codehaus/groovy/ant/VerifyClass.java
new file mode 100644
index 0000000..be49469
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/VerifyClass.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ant;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.tree.AbstractInsnNode;
+import org.objectweb.asm.tree.ClassNode;
+import org.objectweb.asm.tree.MethodNode;
+import org.objectweb.asm.tree.analysis.Analyzer;
+import org.objectweb.asm.tree.analysis.Frame;
+import org.objectweb.asm.tree.analysis.SimpleVerifier;
+import org.objectweb.asm.util.CheckClassAdapter;
+import org.objectweb.asm.util.TraceMethodVisitor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.List;
+
+/**
+ * Verify Class files. This task can take the following
+ * arguments:
+ * <ul>
+ * <li>dir
+ * </ul>
+ * When this task executes, it will recursively scan the dir and
+ * look for class files to verify.
+ */
+public class VerifyClass extends MatchingTask {
+    private String topDir = null;
+    private boolean verbose = false;
+
+    public VerifyClass() {
+    }
+
+    public void execute() throws BuildException {
+        if (topDir == null) throw new BuildException("no dir attribute is set");
+        File top = new File(topDir);
+        if (!top.exists()) throw new BuildException("the directory " + top + " does not exist");
+        log("top dir is " + top);
+        int fails = execute(top);
+        if (fails == 0) {
+            log("no bytecode problems found");
+        } else {
+            log("found " + fails + " failing classes");
+        }
+    }
+
+    public void setDir(String dir) throws BuildException {
+        topDir = dir;
+    }
+
+    public void setVerbose(boolean v) {
+        verbose = v;
+    }
+
+    private int execute(File dir) {
+        int fails = 0;
+        File[] files = dir.listFiles();
+        for (int i = 0; i < files.length; i++) {
+            File f = files[i];
+            if (f.isDirectory()) {
+                fails += execute(f);
+            } else if (f.getName().endsWith(".class")) {
+                try {
+                    boolean ok = readClass(f.getCanonicalPath());
+                    if (!ok) fails++;
+                } catch (IOException ioe) {
+                    log(ioe.getMessage());
+                    throw new BuildException(ioe);
+                }
+            }
+        }
+        return fails;
+    }
+
+    private boolean readClass(String clazz) throws IOException {
+        ClassReader cr = new ClassReader(new FileInputStream(clazz));
+        ClassNode ca = new ClassNode() {
+            public void visitEnd() {
+                //accept(cv);
+            }
+        };
+        cr.accept(new CheckClassAdapter(ca), true);
+        boolean failed = false;
+
+        List methods = ca.methods;
+        for (int i = 0; i < methods.size(); ++i) {
+            MethodNode method = (MethodNode) methods.get(i);
+            if (method.instructions.size() > 0) {
+                Analyzer a = new Analyzer(new SimpleVerifier());
+                try {
+                    a.analyze(ca.name, method);
+                    continue;
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+                final Frame[] frames = a.getFrames();
+
+                if (!failed) {
+                    failed = true;
+                    log("verifying of class " + clazz + " failed");
+                }
+                if (verbose) log(method.name + method.desc);
+                TraceMethodVisitor mv = new TraceMethodVisitor(null) {
+                    public void visitMaxs(int maxStack, int maxLocals) {
+                        StringBuffer buffer = new StringBuffer();
+                        for (int i = 0; i < text.size(); ++i) {
+                            String s = frames[i] == null ? "null" : frames[i].toString();
+                            while (s.length() < maxStack + maxLocals + 1) {
+                                s += " ";
+                            }
+                            buffer.append(Integer.toString(i + 100000).substring(1));
+                            buffer.append(" ");
+                            buffer.append(s);
+                            buffer.append(" : ");
+                            buffer.append(text.get(i));
+                        }
+                        if (verbose) log(buffer.toString());
+                    }
+                };
+                for (int j = 0; j < method.instructions.size(); ++j) {
+                    Object insn = method.instructions.get(j);
+                    if (insn instanceof AbstractInsnNode) {
+                        ((AbstractInsnNode) insn).accept(mv);
+                    } else {
+                        mv.visitLabel((Label) insn);
+                    }
+                }
+                mv.visitMaxs(method.maxStack, method.maxLocals);
+            }
+        }
+        return !failed;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ant/package.html b/groovy/src/main/org/codehaus/groovy/ant/package.html
new file mode 100644
index 0000000..aa78a68
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ant/package.html
@@ -0,0 +1,11 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.ant.*</title>
+  </head>
+  <body>
+    <p>
+      Ant tasks for working with Groovy - such as groovyc for compiling Groovy source code to
+      Java bytecode
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/antlib.xml b/groovy/src/main/org/codehaus/groovy/antlib.xml
new file mode 100644
index 0000000..3e34e84
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlib.xml
@@ -0,0 +1,4 @@
+<antlib>
+    <taskdef name="groovyc" classname="org.codehaus.groovy.ant.Groovyc"/>
+    <taskdef name="groovy"  classname="org.codehaus.groovy.ant.Groovy"/>
+</antlib>
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/ASTParserException.java b/groovy/src/main/org/codehaus/groovy/antlr/ASTParserException.java
new file mode 100644
index 0000000..0880986
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/ASTParserException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr;
+
+import antlr.collections.AST;
+import org.codehaus.groovy.syntax.ParserException;
+
+/**
+ * Thrown when trying to parse the AST
+ *
+ * @version $Revision$
+ */
+public class ASTParserException extends ParserException {
+    private final AST ast;
+
+    public ASTParserException(ASTRuntimeException e) {
+        super(e.getMessage(), e, e.getLine(), e.getColumn());
+        this.ast = e.getAst();
+    }
+
+    public ASTParserException(String message, ASTRuntimeException e) {
+        super(message, e, e.getLine(), e.getColumn());
+        this.ast = e.getAst();
+    }
+
+    public AST getAst() {
+        return ast;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/ASTRuntimeException.java b/groovy/src/main/org/codehaus/groovy/antlr/ASTRuntimeException.java
new file mode 100644
index 0000000..c779efa
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/ASTRuntimeException.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr;
+
+import antlr.collections.AST;
+
+/**
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @version $Revision$
+ */
+public class ASTRuntimeException extends RuntimeException {
+    private final AST ast;
+
+    public ASTRuntimeException(AST ast, String message) {
+        super(message + description(ast));
+        this.ast = ast;
+    }
+
+    public ASTRuntimeException(AST ast, String message, Throwable throwable) {
+        super(message + description(ast), throwable);
+        this.ast = null;
+    }
+
+    protected static String description(AST node) {
+        return (node != null) ? " at line: " + node.getLine() + " column: " + node.getColumn() : "";
+    }
+
+    public AST getAst() {
+        return ast;
+    }
+
+    public int getLine() {
+        return ast != null ? ast.getLine() : -1;
+    }
+
+    public int getColumn() {
+        return ast != null ? ast.getColumn() : -1;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/AntlrASTProcessSnippets.java b/groovy/src/main/org/codehaus/groovy/antlr/AntlrASTProcessSnippets.java
new file mode 100644
index 0000000..2e667cb
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/AntlrASTProcessSnippets.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.antlr;
+
+/**
+ * Process to decorate antlr AST with ending line/col info, and if
+ * possible the snipppet of source from the start/end line/col for each node.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+import antlr.collections.AST;
+import java.util.*;
+
+public class AntlrASTProcessSnippets implements AntlrASTProcessor{
+    private final SourceBuffer sourceBuffer;
+
+    public AntlrASTProcessSnippets(SourceBuffer sourceBuffer) {
+        this.sourceBuffer = sourceBuffer;
+    }
+
+    /**
+     * decorate antlr AST with ending line/col info, and if
+     * possible the snipppet of source from the start/end line/col for each node.
+     * @param t the AST to decorate
+     * @return the decorated AST
+     */
+    public AST process(AST t) {
+        // first visit
+        List l = new ArrayList();
+        t = traverse((GroovySourceAST)t,l,null);
+
+        //System.out.println("l:" + l);
+        // second visit
+        Iterator itr = l.iterator();
+        if (itr.hasNext()) { itr.next(); /* discard first */ }
+        t = traverse((GroovySourceAST)t,null,itr);
+        return t;
+    }
+
+    /**
+     * traverse an AST node
+     * @param t the AST node to traverse
+     * @param l A list to add line/col info to
+     * @param itr An iterator over a list of line/col
+     * @return A decorated AST node
+     */
+    private AST traverse(GroovySourceAST t,List l,Iterator itr) {
+        if (t == null) { return t; }
+
+        // first visit of node
+        if (l != null) {
+            l.add(new LineColumn(t.getLine(),t.getColumn()));
+        }
+
+        // second vist of node
+        if (itr != null && itr.hasNext()) {
+            LineColumn lc = (LineColumn)itr.next();
+            if (t.getLineLast() == 0) {
+                int nextLine = lc.getLine();
+                int nextColumn = lc.getColumn();
+                if (nextLine < t.getLine() || (nextLine == t.getLine() && nextColumn < t.getColumn())) {
+                    nextLine = t.getLine();
+                    nextColumn = t.getColumn();
+                }
+                t.setLineLast(nextLine);
+                t.setColumnLast(nextColumn);
+                // This is a good point to call t.setSnippet(),
+                // but it bulks up the AST too much for production code.
+            }
+        }
+
+        GroovySourceAST child = (GroovySourceAST)t.getFirstChild();
+        if (child != null) {
+            traverse(child,l,itr);
+        }
+
+        GroovySourceAST sibling = (GroovySourceAST)t.getNextSibling();
+        if (sibling != null) {
+            traverse(sibling,l,itr);
+        }
+
+        return t;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/AntlrASTProcessor.java b/groovy/src/main/org/codehaus/groovy/antlr/AntlrASTProcessor.java
new file mode 100644
index 0000000..702a7cf
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/AntlrASTProcessor.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.antlr;
+
+import antlr.collections.AST;
+
+/**
+ * An interface for processing antlr AST objects
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+public interface AntlrASTProcessor {
+    /**
+     * performs some processing on the supplied AST node.
+     * @param t the AST node to process.
+     * @return possibly returns the AST modified or null, depends on the implementation.
+     */
+    AST process(AST t);
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/AntlrParserPlugin.java b/groovy/src/main/org/codehaus/groovy/antlr/AntlrParserPlugin.java
new file mode 100644
index 0000000..c3c1210
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/AntlrParserPlugin.java
@@ -0,0 +1,2567 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+import antlr.TokenStreamRecognitionException;
+import antlr.collections.AST;
+import com.thoughtworks.xstream.XStream;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.antlr.parser.GroovyLexer;
+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+import org.codehaus.groovy.antlr.treewalker.*;
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.*;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.ParserPlugin;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.*;
+import org.objectweb.asm.Opcodes;
+
+import java.io.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A parser plugin which adapts the JSR Antlr Parser to the Groovy runtime
+ *
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @version $Revision$
+ */
+public class AntlrParserPlugin extends ASTHelper implements ParserPlugin, GroovyTokenTypes {
+
+    private AST ast;
+    private ClassNode classNode;
+    private String[] tokenNames;
+
+
+    public Reduction parseCST(final SourceUnit sourceUnit, Reader reader) throws CompilationFailedException {
+        ast = null;
+
+        setController(sourceUnit);
+
+        final SourceBuffer sourceBuffer = new SourceBuffer();
+        UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(reader,sourceBuffer);
+        GroovyLexer lexer = new GroovyLexer(unicodeReader);
+        unicodeReader.setLexer(lexer);
+        GroovyRecognizer parser = GroovyRecognizer.make(lexer);
+        parser.setSourceBuffer(sourceBuffer);
+        tokenNames = parser.getTokenNames();
+        parser.setFilename(sourceUnit.getName());
+
+        // start parsing at the compilationUnit rule
+        try {
+            parser.compilationUnit();
+        }
+        catch (TokenStreamRecognitionException tsre) {
+            RecognitionException e = tsre.recog;
+            SyntaxException se = new SyntaxException(e.getMessage(),e,e.getLine(),e.getColumn());
+            se.setFatal(true);
+            sourceUnit.addError(se);
+        }
+        catch (RecognitionException e) {
+            SyntaxException se = new SyntaxException(e.getMessage(),e,e.getLine(),e.getColumn());
+            se.setFatal(true);
+            sourceUnit.addError(se);
+        }
+        catch (TokenStreamException e) {
+            sourceUnit.addException(e);
+        }
+
+        ast = parser.getAST();
+
+        AntlrASTProcessor snippets = new AntlrASTProcessSnippets(sourceBuffer);
+        ast = snippets.process(ast);
+        
+        AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+            	outputASTInVariousFormsIfNeeded(sourceUnit, sourceBuffer);
+                return null;
+            }
+        });
+        
+        return null; //new Reduction(Tpken.EOF);
+    }
+
+    private void outputASTInVariousFormsIfNeeded(SourceUnit sourceUnit, SourceBuffer sourceBuffer) {
+        // straight xstream output of AST
+        if ("xml".equals(System.getProperty("antlr.ast"))) {
+            saveAsXML(sourceUnit.getName(), ast);
+        }
+
+        // 'pretty printer' output of AST
+        if ("groovy".equals(System.getProperty("antlr.ast"))) {
+            try {
+                PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".pretty.groovy"));
+                Visitor visitor = new SourcePrinter(out,tokenNames);
+                AntlrASTProcessor treewalker = new SourceCodeTraversal(visitor);
+                treewalker.process(ast);
+            } catch (FileNotFoundException e) {
+                System.out.println("Cannot create " + sourceUnit.getName() + ".pretty.groovy");
+            }
+        }
+
+        // output AST in format suitable for opening in http://freemind.sourceforge.net
+        // which is a really nice way of seeing the AST, folding nodes etc
+        if ("mindmap".equals(System.getProperty("antlr.ast"))) {
+            try {
+                PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".mm"));
+                Visitor visitor = new MindMapPrinter(out,tokenNames);
+                AntlrASTProcessor treewalker = new PreOrderTraversal(visitor);
+                treewalker.process(ast);
+            } catch (FileNotFoundException e) {
+                System.out.println("Cannot create " + sourceUnit.getName() + ".mm");
+            }
+        }
+
+        // include original line/col info and source code on the mindmap output
+        if ("extendedMindmap".equals(System.getProperty("antlr.ast"))) {
+            try {
+                PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".mm"));
+                Visitor visitor = new MindMapPrinter(out,tokenNames,sourceBuffer);
+                AntlrASTProcessor treewalker = new PreOrderTraversal(visitor);
+                treewalker.process(ast);
+            } catch (FileNotFoundException e) {
+                System.out.println("Cannot create " + sourceUnit.getName() + ".mm");
+            }
+        }
+
+        // html output of AST
+        if ("html".equals(System.getProperty("antlr.ast"))) {
+            try {
+                PrintStream out = new PrintStream(new FileOutputStream(sourceUnit.getName() + ".html"));
+                List v = new ArrayList();
+                v.add(new NodeAsHTMLPrinter(out,tokenNames));
+                v.add(new SourcePrinter(out,tokenNames));
+                Visitor visitors = new CompositeVisitor(v);
+                AntlrASTProcessor treewalker = new SourceCodeTraversal(visitors);
+                treewalker.process(ast);
+            } catch (FileNotFoundException e) {
+                System.out.println("Cannot create " + sourceUnit.getName() + ".html");
+            }
+        }
+
+
+    }
+
+    private void saveAsXML(String name, AST ast) {
+        XStream xstream = new XStream();
+        try {
+            xstream.toXML(ast, new FileWriter(name + ".antlr.xml"));
+            System.out.println("Written AST to " + name + ".antlr.xml");
+        }
+        catch (Exception e) {
+            System.out.println("Couldn't write to " + name + ".antlr.xml");
+            e.printStackTrace();
+        }
+    }
+
+    public ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduction cst) throws ParserException {
+        setClassLoader(classLoader);
+        makeModule();
+        try {
+            convertGroovy(ast);
+        }
+        catch (ASTRuntimeException e) {
+            throw new ASTParserException(e.getMessage() + ". File: " + sourceUnit.getName(), e);
+        }
+        return output;
+    }
+
+    /**
+     * Converts the Antlr AST to the Groovy AST
+     */
+    protected void convertGroovy(AST node) {
+        while (node != null) {
+            int type = node.getType();
+            switch (type) {
+                case PACKAGE_DEF:
+                    packageDef(node);
+                    break;
+
+                case STATIC_IMPORT:
+                case IMPORT:
+                    importDef(node);
+                    break;
+
+                case CLASS_DEF:
+                    classDef(node);
+                    break;
+
+                case INTERFACE_DEF:
+                    interfaceDef(node);
+                    break;
+
+                case METHOD_DEF:
+                    methodDef(node);
+                    break;
+                    
+                case ENUM_DEF:
+                    enumDef(node);
+                    break;
+
+                default:
+                    {
+                        Statement statement = statement(node);
+                        output.addStatement(statement);
+                    }
+            }
+            node = node.getNextSibling();
+        }
+    }
+
+    // Top level control structures
+    //-------------------------------------------------------------------------
+
+    protected void packageDef(AST packageDef) {
+        AST node = packageDef.getFirstChild();
+        if (isType(ANNOTATIONS, node)) {
+            node = node.getNextSibling();
+        }
+        String name = qualifiedName(node);
+        setPackageName(name);
+    }
+    
+    protected void importDef(AST importNode) {
+        boolean isStatic = importNode.getType() == STATIC_IMPORT;
+
+        AST node = importNode.getFirstChild();
+
+        String alias = null;
+        if (isType(LITERAL_as, node)) {
+            //import is like "import Foo as Bar"
+            node = node.getFirstChild();
+            AST aliasNode = node.getNextSibling();
+            alias = identifier(aliasNode);
+        }
+
+        if (node.getNumberOfChildren()==0) {
+            String name = identifier(node);
+            // import is like  "import Foo"
+            ClassNode type = ClassHelper.make(name);
+            configureAST(type,importNode);
+            importClass(type, name, alias);
+            return;
+        }
+
+        AST packageNode = node.getFirstChild();
+        String packageName = qualifiedName(packageNode);
+        AST nameNode = packageNode.getNextSibling();
+        if (isType(STAR, nameNode)) {
+            if (isStatic) {
+                // import is like "import static foo.Bar.*"
+                // packageName is actually a className in this case
+                ClassNode type = ClassHelper.make(packageName);
+                configureAST(type, importNode);
+                staticImportClassWithStar(type, packageName);
+            } else {
+                // import is like "import foo.*"
+                importPackageWithStar(packageName);
+            }
+
+            if (alias!=null) throw new GroovyBugError(
+                    "imports like 'import foo.* as Bar' are not "+
+                    "supported and should be caught by the grammar");
+        } else {
+            String name = identifier(nameNode);
+            if (isStatic) {
+                // import is like "import static foo.Bar.method"
+                // packageName is really class name in this case
+                ClassNode type = ClassHelper.make(packageName);
+                configureAST(type, importNode);
+                staticImportMethodOrField(type, name, alias);
+            } else {
+                // import is like "import foo.Bar"
+                ClassNode type = ClassHelper.make(packageName+"."+name);
+                configureAST(type, importNode);
+                importClass(type, name, alias);
+            }
+        }
+    }
+
+    protected void interfaceDef(AST classDef) {
+        List annotations = new ArrayList();
+        AST node = classDef.getFirstChild();
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            node = node.getNextSibling();
+        }
+        modifiers |= Opcodes.ACC_ABSTRACT | Opcodes.ACC_INTERFACE;
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+        ClassNode superClass = ClassHelper.OBJECT_TYPE;
+
+        GenericsType[] genericsType = null;
+        if (isType(TYPE_PARAMETERS,node)) {
+            genericsType = makeGenericsType(node);
+            node = node.getNextSibling();
+        }
+        
+        ClassNode[] interfaces = ClassNode.EMPTY_ARRAY;
+        if (isType(EXTENDS_CLAUSE, node)) {
+            interfaces = interfaces(node);
+            node = node.getNextSibling();
+        }
+
+        addNewClassName(name);
+        classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, null);
+        classNode.addAnnotations(annotations);
+        classNode.setGenericsTypes(genericsType);
+        configureAST(classNode, classDef);
+
+        assertNodeType(OBJBLOCK, node);
+        objectBlock(node);
+        output.addClass(classNode);
+        classNode = null;
+    }
+
+    protected void classDef(AST classDef) {
+        List annotations = new ArrayList();
+        AST node = classDef.getFirstChild();
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            node = node.getNextSibling();
+        }
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+        
+        GenericsType[] genericsType = null;
+        if (isType(TYPE_PARAMETERS,node)) {
+            genericsType = makeGenericsType(node);
+            node = node.getNextSibling();
+        }
+
+        ClassNode superClass = null;
+        if (isType(EXTENDS_CLAUSE, node)) {
+            superClass = makeTypeWithArguments(node);
+            node = node.getNextSibling();
+        }
+
+        ClassNode[] interfaces = ClassNode.EMPTY_ARRAY;
+        if (isType(IMPLEMENTS_CLAUSE, node)) {
+            interfaces = interfaces(node);
+            node = node.getNextSibling();
+        }
+
+        // TODO read mixins
+        MixinNode[] mixins = {};
+
+        addNewClassName(name);
+        classNode = new ClassNode(dot(getPackageName(), name), modifiers, superClass, interfaces, mixins);
+        classNode.addAnnotations(annotations);
+        classNode.setGenericsTypes(genericsType);
+        configureAST(classNode, classDef);
+
+        assertNodeType(OBJBLOCK, node);
+        objectBlock(node);
+        output.addClass(classNode);
+        classNode = null;
+    }
+
+    protected void objectBlock(AST objectBlock) {
+        for (AST node = objectBlock.getFirstChild(); node != null; node = node.getNextSibling()) {
+            int type = node.getType();
+            switch (type) {
+                case OBJBLOCK:
+                    objectBlock(node);
+                    break;
+
+                case METHOD_DEF:
+                    methodDef(node);
+                    break;
+
+                case CTOR_IDENT:
+                    constructorDef(node);
+                    break;
+
+                case VARIABLE_DEF:
+                    fieldDef(node);
+                    break;
+
+                case STATIC_INIT:
+                    staticInit(node);
+                    break;
+                    
+                case INSTANCE_INIT:
+                    objectInit(node);
+                    break;
+                    
+                case ENUM_DEF:
+                    enumDef(node);
+                    break;      
+                    
+                case ENUM_CONSTANT_DEF:
+                    enumConstantDef(node);
+                    break;
+                    
+                default:
+                    unknownAST(node);
+            }
+        }
+    }
+    
+    protected void enumDef(AST enumNode) {
+        assertNodeType(ENUM_DEF, enumNode);
+        List annotations = new ArrayList();
+        
+        AST node = enumNode.getFirstChild();
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node,annotations,modifiers);
+            node = node.getNextSibling();
+        }
+        
+        String name = identifier(node);
+        node = node.getNextSibling();
+       
+        ClassNode[] interfaces = interfaces(node);
+        node = node.getNextSibling();
+        
+        ClassNode enumClass = EnumHelper.makeEnumNode(dot(getPackageName(),name),modifiers,interfaces,classNode);
+        ClassNode oldNode = classNode;
+        classNode = enumClass;
+        assertNodeType(OBJBLOCK, node);
+        objectBlock(node);
+        classNode = oldNode;
+        
+        output.addClass(enumClass);
+    }
+    
+    protected void enumConstantDef(AST node) {
+        assertNodeType(ENUM_CONSTANT_DEF, node);
+        AST element = node.getFirstChild();
+        if (isType(ANNOTATIONS,element)) {
+            element = element.getNextSibling();
+        }
+        String identifier = identifier(element);
+        Expression init = null;
+        element = element.getNextSibling();
+        if (element!=null) init = expression(element);
+        EnumHelper.addEnumConstant(classNode, identifier, init);
+    }
+    
+    protected void throwsList(AST node,List list) {
+    	String name;
+    	if (isType(DOT, node)) {
+    		name = qualifiedName(node);
+    	} else {
+    		name = identifier(node);
+    	}
+    	ClassNode exception = ClassHelper.make(name);
+    	list.add(exception);
+    	AST next = node.getNextSibling();
+    	if (next!=null) throwsList(next, list);
+    }
+    
+    protected void methodDef(AST methodDef) {
+        List annotations = new ArrayList();
+        AST node = methodDef.getFirstChild();
+        
+        GenericsType[] generics=null;
+        if (isType(TYPE_PARAMETERS, node)) {
+            generics = makeGenericsType(node);
+            node = node.getNextSibling();
+        }
+        
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            node = node.getNextSibling();
+        }
+
+        if (classNode!=null && (classNode.getModifiers() & Opcodes.ACC_INTERFACE) >0) {
+            modifiers |= Opcodes.ACC_ABSTRACT;
+        }
+
+        ClassNode returnType = null;
+        if (isType(TYPE, node)) {
+            returnType = makeTypeWithArguments(node);
+            node = node.getNextSibling();
+        }
+
+        String name = identifier(node);
+        if (classNode != null) {
+            if (classNode.getNameWithoutPackage().equals(name)) {
+                throw new ASTRuntimeException(methodDef, "Invalid constructor format. Try remove the 'def' expression?");
+            }
+        }
+        node = node.getNextSibling();
+
+        assertNodeType(PARAMETERS, node);
+        Parameter[] parameters = parameters(node);
+        if (parameters==null) parameters = Parameter.EMPTY_ARRAY;
+        node = node.getNextSibling();
+        
+        ClassNode[] exceptions= ClassNode.EMPTY_ARRAY;
+        if (isType(LITERAL_throws, node)) {
+        	AST throwsNode = node.getFirstChild();
+        	List exceptionList = new ArrayList();
+        	throwsList(throwsNode, exceptionList);
+        	exceptions = (ClassNode[]) exceptionList.toArray(exceptions);
+        	node = node.getNextSibling();
+        }
+
+        Statement code = null;
+        if ((modifiers & Opcodes.ACC_ABSTRACT) == 0) {
+            if (node==null) {
+                throw new ASTRuntimeException(methodDef, "You defined a method without body. Try adding a body, or declare it abstract.");
+            }
+            assertNodeType(SLIST, node);
+            code = statementList(node);
+        }
+
+        MethodNode methodNode = new MethodNode(name, modifiers, returnType, parameters, exceptions, code);
+        methodNode.addAnnotations(annotations);
+        methodNode.setGenericsTypes(generics);
+        configureAST(methodNode, methodDef);
+        if (classNode != null) {
+            classNode.addMethod(methodNode);
+        }
+        else {
+            output.addMethod(methodNode);
+        }
+    }
+    
+    protected void staticInit(AST staticInit) {        
+        BlockStatement code = (BlockStatement) statementList(staticInit);
+        classNode.addStaticInitializerStatements(code.getStatements(),false);
+    }
+    
+    protected void objectInit(AST init) {        
+        BlockStatement code = (BlockStatement) statementList(init);
+        classNode.addObjectInitializerStatements(code);
+    }
+    
+    protected void constructorDef(AST constructorDef) {
+        List annotations = new ArrayList();
+        AST node = constructorDef.getFirstChild();
+        int modifiers = Opcodes.ACC_PUBLIC;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            node = node.getNextSibling();
+        }
+
+        assertNodeType(PARAMETERS, node);
+        Parameter[] parameters = parameters(node);
+        if (parameters == null) parameters = Parameter.EMPTY_ARRAY;
+        node = node.getNextSibling();
+
+        ClassNode[] exceptions= ClassNode.EMPTY_ARRAY;
+        if (isType(LITERAL_throws, node)) {
+        	AST throwsNode = node.getFirstChild();
+        	List exceptionList = new ArrayList();
+        	throwsList(throwsNode, exceptionList);
+        	exceptions = (ClassNode[]) exceptionList.toArray(exceptions);
+        	node = node.getNextSibling();
+        }
+        
+        assertNodeType(SLIST, node);
+        Statement code = statementList(node);
+
+        ConstructorNode constructorNode = classNode.addConstructor(modifiers, parameters, exceptions, code);
+        constructorNode.addAnnotations(annotations);
+        configureAST(constructorNode, constructorDef);
+    }
+
+    protected void fieldDef(AST fieldDef) {
+        List annotations = new ArrayList();
+        AST node = fieldDef.getFirstChild();
+
+        int modifiers = 0;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            node = node.getNextSibling();
+        }
+        
+        if (classNode.isInterface()) {
+        	modifiers |= Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
+        	if ( (modifiers & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) == 0) {
+        		modifiers |= Opcodes.ACC_PUBLIC;
+        	}
+        }
+
+        ClassNode type = null;
+        if (isType(TYPE, node)) {
+            type = makeTypeWithArguments(node);
+            node = node.getNextSibling();
+        }
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+
+        Expression initialValue = null;
+        if (node != null) {
+            assertNodeType(ASSIGN, node);
+            initialValue = expression(node);
+        }
+
+        if (initialValue == null && type != null) {
+            if (type==ClassHelper.int_TYPE) {
+                initialValue = new ConstantExpression(new Integer(0));
+            }
+            else if (type==ClassHelper.long_TYPE) {
+                initialValue = new ConstantExpression(new Long(0L));
+            }
+            else if (type==ClassHelper.double_TYPE) {
+                initialValue = new ConstantExpression(new Double(0.0));
+            }
+            else if (type==ClassHelper.float_TYPE) {
+                initialValue = new ConstantExpression(new Float(0.0F));
+            }
+            else if (type==ClassHelper.boolean_TYPE) {
+                initialValue = ConstantExpression.FALSE;
+            }
+            else if (type==ClassHelper.short_TYPE) {
+                initialValue = new ConstantExpression(new Short((short) 0));
+            }
+            else if (type==ClassHelper.byte_TYPE) {
+                initialValue = new ConstantExpression(new Byte((byte) 0));
+            }
+            else if (type==ClassHelper.char_TYPE) {
+                initialValue = new ConstantExpression(new Character((char) 0));
+            }
+        }
+
+
+        FieldNode fieldNode = new FieldNode(name, modifiers, type, classNode, initialValue);
+        fieldNode.addAnnotations(annotations);
+        configureAST(fieldNode, fieldDef);
+
+        if (!hasVisibility(modifiers)) {
+            // lets set the modifiers on the field
+            int fieldModifiers = 0;
+            int flags = Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT | Opcodes.ACC_VOLATILE | Opcodes.ACC_FINAL;
+
+            if (!hasVisibility(modifiers)) {
+                modifiers |= Opcodes.ACC_PUBLIC;
+                fieldModifiers |= Opcodes.ACC_PRIVATE;
+            }
+
+            // lets pass along any other modifiers we need
+            fieldModifiers |= (modifiers & flags);
+            fieldNode.setModifiers(fieldModifiers);
+            
+            PropertyNode propertyNode = new PropertyNode(fieldNode, modifiers, null, null);
+            configureAST(propertyNode, fieldDef);
+            classNode.addProperty(propertyNode);
+        }
+        else {
+            fieldNode.setModifiers(modifiers);
+            classNode.addField(fieldNode);
+        }
+    }
+
+    protected ClassNode[] interfaces(AST node) {
+        List interfaceList = new ArrayList();
+        for (AST implementNode = node.getFirstChild(); implementNode != null; implementNode = implementNode.getNextSibling()) {
+            interfaceList.add(makeTypeWithArguments(implementNode));
+        }
+        ClassNode[] interfaces = ClassNode.EMPTY_ARRAY;
+        if (!interfaceList.isEmpty()) {
+            interfaces = new ClassNode[interfaceList.size()];
+            interfaceList.toArray(interfaces);
+
+        }
+        return interfaces;
+    }
+
+    protected Parameter[] parameters(AST parametersNode) {
+        AST node = parametersNode.getFirstChild();
+        if (node == null) {
+        	if (isType(IMPLICIT_PARAMETERS, parametersNode)) return Parameter.EMPTY_ARRAY;
+            return null;
+        }
+        else {
+            List parameters = new ArrayList();
+            do {
+                parameters.add(parameter(node));
+                node = node.getNextSibling();
+            }
+            while (node != null);
+            Parameter[] answer = new Parameter[parameters.size()];
+            parameters.toArray(answer);
+            return answer;
+        }
+    }
+
+    protected Parameter parameter(AST paramNode) {
+        List annotations = new ArrayList();
+        boolean variableParameterDef = isType(VARIABLE_PARAMETER_DEF,paramNode);
+        AST node = paramNode.getFirstChild();
+
+        int modifiers = 0;
+        if (isType(MODIFIERS, node)) {
+            modifiers = modifiers(node, annotations, modifiers);
+            node = node.getNextSibling();
+        }
+
+        ClassNode type = ClassHelper.DYNAMIC_TYPE;
+        if (isType(TYPE, node)) {
+            type = makeTypeWithArguments(node);
+            if (variableParameterDef) type = type.makeArray();
+            node = node.getNextSibling();
+        }
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+        VariableExpression leftExpression = new VariableExpression(name, type);
+        configureAST(leftExpression, paramNode);
+
+        Parameter parameter = null;
+        if (node != null) {
+            assertNodeType(ASSIGN, node);
+            Expression rightExpression = expression(node.getFirstChild());
+            parameter = new Parameter(type, name, rightExpression);
+        }
+        else
+            parameter = new Parameter(type, name);
+
+        configureAST(parameter, paramNode);
+        parameter.addAnnotations(annotations);
+        return parameter;
+    }
+
+    protected int modifiers(AST modifierNode, List annotations, int defaultModifiers) {
+        assertNodeType(MODIFIERS, modifierNode);
+
+        boolean access = false;
+        int answer = 0;
+
+        for (AST node = modifierNode.getFirstChild(); node != null; node = node.getNextSibling()) {
+            int type = node.getType();
+            switch (type) {
+                case STATIC_IMPORT:
+                    // ignore
+                    break;
+
+                    // annotations
+                case ANNOTATION:
+                    annotations.add(annotation(node));
+                    break;
+
+                    // core access scope modifiers
+                case LITERAL_private:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_PRIVATE);
+                    access = setAccessTrue(node, access);
+                    break;
+
+                case LITERAL_protected:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_PROTECTED);
+                    access = setAccessTrue(node, access);
+                    break;
+
+                case LITERAL_public:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_PUBLIC);
+                    access = setAccessTrue(node, access);
+                    break;
+
+                    // other modifiers
+                case ABSTRACT:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_ABSTRACT);
+                    break;
+
+                case FINAL:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_FINAL);
+                    break;
+
+                case LITERAL_native:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_NATIVE);
+                    break;
+
+                case LITERAL_static:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_STATIC);
+                    break;
+
+                case STRICTFP:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_STRICT);
+                    break;
+
+                case LITERAL_synchronized:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_SYNCHRONIZED);
+                    break;
+
+                case LITERAL_transient:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_TRANSIENT);
+                    break;
+
+                case LITERAL_volatile:
+                    answer = setModifierBit(node, answer, Opcodes.ACC_VOLATILE);
+                    break;
+
+                default:
+                    unknownAST(node);
+            }
+        }
+        if (!access) {
+            answer |= defaultModifiers;
+        }
+        return answer;
+    }
+
+    protected boolean setAccessTrue(AST node, boolean access) {
+        if (!access) {
+            return true;
+        }
+        else {
+            throw new ASTRuntimeException(node, "Cannot specify modifier: " + node.getText() + " when access scope has already been defined");
+        }
+    }
+
+    protected int setModifierBit(AST node, int answer, int bit) {
+        if ((answer & bit) != 0) {
+            throw new ASTRuntimeException(node, "Cannot repeat modifier: " + node.getText());
+        }
+        return answer | bit;
+    }
+
+    protected AnnotationNode annotation(AST annotationNode) {
+        AST node = annotationNode.getFirstChild();
+        String name = qualifiedName(node);
+        AnnotationNode annotatedNode = new AnnotationNode(ClassHelper.make(name));
+        configureAST(annotatedNode, node);
+        while (true) {
+            node = node.getNextSibling();
+            if (isType(ANNOTATION_MEMBER_VALUE_PAIR, node)) {
+                AST memberNode = node.getFirstChild();
+                String param = identifier(memberNode);
+                Expression expression = expression(memberNode.getNextSibling());
+                annotatedNode.addMember(param, expression);
+            }
+            else {
+                break;
+            }
+        }
+        return annotatedNode;
+    }
+
+
+
+    // Statements
+    //-------------------------------------------------------------------------
+
+    protected Statement statement(AST node) {
+        Statement statement = null;
+        int type = node.getType();
+        switch (type) {
+            case SLIST:
+            case LITERAL_finally:
+                statement = statementList(node);
+                break;
+
+            case METHOD_CALL:
+                statement = methodCall(node);
+                break;
+
+            case VARIABLE_DEF:
+                statement = variableDef(node);
+                break;
+
+
+            case LABELED_STAT:
+                statement = labelledStatement(node);
+                break;
+
+            case LITERAL_assert:
+                statement = assertStatement(node);
+                break;
+
+            case LITERAL_break:
+                statement = breakStatement(node);
+                break;
+
+            case LITERAL_continue:
+                statement = continueStatement(node);
+                break;
+
+            case LITERAL_if:
+                statement = ifStatement(node);
+                break;
+
+            case LITERAL_for:
+                statement = forStatement(node);
+                break;
+
+            case LITERAL_return:
+                statement = returnStatement(node);
+                break;
+
+            case LITERAL_synchronized:
+                statement = synchronizedStatement(node);
+                break;
+
+            case LITERAL_switch:
+                statement = switchStatement(node);
+                break;
+
+            case LITERAL_try:
+                statement = tryStatement(node);
+                break;
+
+            case LITERAL_throw:
+                statement = throwStatement(node);
+                break;
+
+            case LITERAL_while:
+                statement = whileStatement(node);
+                break;
+
+            default:
+                statement = new ExpressionStatement(expression(node));
+        }
+        if (statement != null) {
+            configureAST(statement, node);
+        }
+        return statement;
+    }
+
+    protected Statement statementList(AST code) {
+        return statementListNoChild(code.getFirstChild());
+    }
+
+    protected Statement statementListNoChild(AST node) {
+        BlockStatement block = new BlockStatement();
+        // no need to configureAST(block,node); as node is probably null
+        for (; node != null; node = node.getNextSibling()) {
+            block.addStatement(statement(node));
+        }
+        return block;
+    }
+
+    protected Statement assertStatement(AST assertNode) {
+        AST node = assertNode.getFirstChild();
+        BooleanExpression booleanExpression = booleanExpression(node);
+        Expression messageExpression = null;
+
+        node = node.getNextSibling();
+        if (node != null) {
+            messageExpression = expression(node);
+        }
+        else {
+            messageExpression = ConstantExpression.NULL;
+        }
+        AssertStatement assertStatement = new AssertStatement(booleanExpression, messageExpression);
+        configureAST(assertStatement, assertNode);
+        return assertStatement;
+    }
+
+    protected Statement breakStatement(AST node) {
+        BreakStatement breakStatement = new BreakStatement(label(node));
+        configureAST(breakStatement, node);
+        return breakStatement;
+    }
+
+    protected Statement continueStatement(AST node) {
+        ContinueStatement continueStatement = new ContinueStatement(label(node));
+        configureAST(continueStatement, node);
+        return continueStatement;
+    }
+
+    protected Statement forStatement(AST forNode) {
+        AST inNode = forNode.getFirstChild();
+        Expression collectionExpression;
+        Parameter forParameter;
+        if (isType(CLOSURE_LIST, inNode)) {
+            ClosureListExpression clist =  closureListExpression(inNode);
+            int size = clist.getExpressions().size();
+            if (size!=3) {
+                throw new ASTRuntimeException(inNode, "3 expressions are required for the classic for loop, you gave "+size);
+            }
+            collectionExpression = clist;
+            forParameter=ForStatement.FOR_LOOP_DUMMY;
+        } else {        
+            AST variableNode = inNode.getFirstChild();
+            AST collectionNode = variableNode.getNextSibling();
+    
+            ClassNode type = ClassHelper.OBJECT_TYPE;
+            if (isType(VARIABLE_DEF, variableNode)) {
+                AST typeNode = variableNode.getFirstChild();
+                assertNodeType(TYPE, typeNode);
+    
+                type = type(typeNode);
+                variableNode = typeNode.getNextSibling();
+            }
+            String variable = identifier(variableNode);
+    
+            collectionExpression = expression(collectionNode);
+            forParameter = new Parameter(type,variable);
+        }
+
+        Statement block = statement(inNode.getNextSibling());
+        ForStatement forStatement = new ForStatement(forParameter, collectionExpression, block);
+        configureAST(forStatement, forNode);
+        return forStatement;
+    }
+
+    protected Statement ifStatement(AST ifNode) {
+        AST node = ifNode.getFirstChild();
+        assertNodeType(EXPR, node);
+        BooleanExpression booleanExpression = booleanExpression(node);
+
+        node = node.getNextSibling();
+        Statement ifBlock = statement(node);
+
+        Statement elseBlock = EmptyStatement.INSTANCE;
+        node = node.getNextSibling();
+        if (node != null) {
+            elseBlock = statement(node);
+        }
+        IfStatement ifStatement = new IfStatement(booleanExpression, ifBlock, elseBlock);
+        configureAST(ifStatement, ifNode);
+        return ifStatement;
+    }
+
+    protected Statement labelledStatement(AST labelNode) {
+        AST node = labelNode.getFirstChild();
+        String label = identifier(node);
+        Statement statement = statement(node.getNextSibling());
+        statement.setStatementLabel(label);
+        return statement;
+    }
+
+    protected Statement methodCall(AST code) {
+        Expression expression = methodCallExpression(code);
+        ExpressionStatement expressionStatement = new ExpressionStatement(expression);
+        configureAST(expressionStatement, code);
+        return expressionStatement;
+    }
+
+    protected Expression declarationExpression(AST variableDef) {
+        AST node = variableDef.getFirstChild();
+        ClassNode type = null;
+        if (isType(MODIFIERS, node)) {
+            node = node.getNextSibling();
+        }
+        if (isType(TYPE, node)) {
+            type = makeType(node);
+            node = node.getNextSibling();
+        }
+
+        String name = identifier(node);
+        node = node.getNextSibling();
+
+        VariableExpression leftExpression = new VariableExpression(name, type);
+        configureAST(leftExpression, variableDef);
+
+        Expression rightExpression = ConstantExpression.NULL;
+        if (node != null) {
+            assertNodeType(ASSIGN, node);
+
+            rightExpression = expression(node.getFirstChild());
+        }
+        Token token = makeToken(Types.ASSIGN, variableDef);
+
+        // TODO should we have a variable declaration statement?
+        DeclarationExpression expression = new DeclarationExpression(leftExpression, token, rightExpression);
+        configureAST(expression, variableDef);
+        ExpressionStatement expressionStatement = new ExpressionStatement(expression);
+        configureAST(expressionStatement, variableDef);
+        return expression;
+    }
+    
+    protected Statement variableDef(AST variableDef) {
+        ExpressionStatement expressionStatement = new ExpressionStatement(declarationExpression(variableDef));
+        configureAST(expressionStatement, variableDef);
+        return expressionStatement;
+    }
+
+    protected Statement returnStatement(AST node) {
+        AST exprNode = node.getFirstChild();
+
+        // This will pick up incorrect sibling node if 'node' is a plain 'return'
+		//
+		//if (exprNode == null) {
+        //    exprNode = node.getNextSibling();
+        //}
+        if (exprNode != null) {
+            Expression expression = expression(exprNode);
+            if (expression instanceof ConstantExpression) {
+                ConstantExpression constantExpr = (ConstantExpression) expression;
+                if (constantExpr.getValue() == null) {
+                    return ReturnStatement.RETURN_NULL_OR_VOID;
+                }
+            }
+            ReturnStatement returnStatement = new ReturnStatement(expression);
+            configureAST(returnStatement, node);
+            return returnStatement;
+        }
+        else {
+            return ReturnStatement.RETURN_NULL_OR_VOID;
+        }
+    }
+
+    protected Statement switchStatement(AST switchNode) {
+        AST node = switchNode.getFirstChild();
+        Expression expression = expression(node);
+        Statement defaultStatement = EmptyStatement.INSTANCE;
+
+        List list = new ArrayList();
+        for (node = node.getNextSibling(); isType(CASE_GROUP, node); node = node.getNextSibling()) {
+            AST child = node.getFirstChild();
+            if (isType(LITERAL_case, child)) {
+                List cases = new LinkedList();
+                // default statement can be grouped with previous case
+                defaultStatement = caseStatements(child, cases);
+                list.addAll(cases);
+            } else {
+                defaultStatement = statement(child.getNextSibling());
+            }
+        }
+        if (node != null) {
+            unknownAST(node);
+        }
+        SwitchStatement switchStatement = new SwitchStatement(expression, list, defaultStatement);
+        configureAST(switchStatement, switchNode);
+        return switchStatement;
+    }
+
+    protected Statement caseStatements(AST node, List cases) {
+        List expressions = new LinkedList();
+        Statement statement = EmptyStatement.INSTANCE;
+        Statement defaultStatement = EmptyStatement.INSTANCE;
+        AST nextSibling = node;
+        do {
+            Expression expression = expression(nextSibling.getFirstChild());
+            expressions.add(expression);
+            nextSibling = nextSibling.getNextSibling();
+        } while (isType(LITERAL_case, nextSibling));
+        if (nextSibling != null) {
+            if (isType(LITERAL_default, nextSibling)) {
+                defaultStatement = statement(nextSibling.getNextSibling());
+                statement = EmptyStatement.INSTANCE;
+            } else {
+                statement = statement(nextSibling);
+            }
+        }
+        for (Iterator iterator = expressions.iterator(); iterator.hasNext();) {
+            Expression expr = (Expression) iterator.next();
+            Statement stmt;
+            if (iterator.hasNext()) {
+                stmt = new CaseStatement(expr,EmptyStatement.INSTANCE);
+            } else {
+                stmt = new CaseStatement(expr,statement);
+            }
+            configureAST(stmt,node);
+            cases.add(stmt);
+        }
+        return defaultStatement;
+    }
+
+    protected Statement synchronizedStatement(AST syncNode) {
+        AST node = syncNode.getFirstChild();
+        Expression expression = expression(node);
+        Statement code = statement(node.getNextSibling());
+        SynchronizedStatement synchronizedStatement = new SynchronizedStatement(expression, code);
+        configureAST(synchronizedStatement, syncNode);
+        return synchronizedStatement;
+    }
+
+    protected Statement throwStatement(AST node) {
+        AST expressionNode = node.getFirstChild();
+        if (expressionNode == null) {
+            expressionNode = node.getNextSibling();
+        }
+        if (expressionNode == null) {
+            throw new ASTRuntimeException(node, "No expression available");
+        }
+        ThrowStatement throwStatement = new ThrowStatement(expression(expressionNode));
+        configureAST(throwStatement, node);
+        return throwStatement;
+    }
+
+    protected Statement tryStatement(AST tryStatementNode) {
+        AST tryNode = tryStatementNode.getFirstChild();
+        Statement tryStatement = statement(tryNode);
+        Statement finallyStatement = EmptyStatement.INSTANCE;
+        AST node = tryNode.getNextSibling();
+
+        // lets do the catch nodes
+        List catches = new ArrayList();
+        for (; node != null && isType(LITERAL_catch, node); node = node.getNextSibling()) {
+            catches.add(catchStatement(node));
+        }
+
+        if (isType(LITERAL_finally, node)) {
+            finallyStatement = statement(node);
+            node = node.getNextSibling();
+        }
+
+        TryCatchStatement tryCatchStatement = new TryCatchStatement(tryStatement, finallyStatement);
+        configureAST(tryCatchStatement, tryStatementNode);
+        for (Iterator iter = catches.iterator(); iter.hasNext();) {
+            CatchStatement statement = (CatchStatement) iter.next();
+            tryCatchStatement.addCatch(statement);
+        }
+        return tryCatchStatement;
+    }
+
+    protected CatchStatement catchStatement(AST catchNode) {
+        AST node = catchNode.getFirstChild();
+        Parameter parameter = parameter(node);
+        ClassNode exceptionType = parameter.getType();
+        String variable = parameter.getName();
+        node = node.getNextSibling();
+        Statement code = statement(node);
+        Parameter catchParameter = new Parameter(exceptionType,variable);
+        CatchStatement answer = new CatchStatement(catchParameter, code);
+        configureAST(answer, catchNode);
+        return answer;
+    }
+
+    protected Statement whileStatement(AST whileNode) {
+        AST node = whileNode.getFirstChild();
+        assertNodeType(EXPR, node);
+        BooleanExpression booleanExpression = booleanExpression(node);
+
+        node = node.getNextSibling();
+        Statement block = statement(node);
+        WhileStatement whileStatement = new WhileStatement(booleanExpression, block);
+        configureAST(whileStatement, whileNode);
+        return whileStatement;
+    }
+
+
+
+    // Expressions
+    //-------------------------------------------------------------------------
+
+    protected Expression expression(AST node) {
+        return expression(node,false);
+    }
+    
+    protected Expression expression(AST node, boolean convertToConstant) {
+        Expression expression = expressionSwitch(node);
+        if (convertToConstant) {
+            // a method name can never be a VariableExprssion, so it must converted
+            // to a ConstantExpression then. This is needed as the expression
+            // method doesn't know we want a ConstantExpression instead of a
+            // VariableExpression
+            if ( expression != VariableExpression.THIS_EXPRESSION &&
+                 expression != VariableExpression.SUPER_EXPRESSION &&
+                 expression instanceof VariableExpression) 
+            {
+                VariableExpression ve = (VariableExpression) expression;
+                expression = new ConstantExpression(ve.getName());
+            }
+        }
+        configureAST(expression, node);
+        return expression;       
+    }
+
+    protected Expression expressionSwitch(AST node) {
+        int type = node.getType();
+        switch (type) {
+            case EXPR:
+                return expression(node.getFirstChild());
+
+            case ELIST:
+                return expressionList(node);
+
+            case SLIST:
+                return blockExpression(node);
+
+            case CLOSABLE_BLOCK:
+                return closureExpression(node);
+
+            case SUPER_CTOR_CALL:
+                return specialConstructorCallExpression(node,ClassNode.SUPER);
+
+            case METHOD_CALL:
+                return methodCallExpression(node);
+
+            case LITERAL_new:
+                return constructorCallExpression(node.getFirstChild());
+
+            case CTOR_CALL:
+                return specialConstructorCallExpression(node,ClassNode.THIS);
+
+            case QUESTION: 
+            case ELVIS_OPERATOR:
+                return ternaryExpression(node);
+
+            case OPTIONAL_DOT:
+            case SPREAD_DOT:
+            case DOT:
+                return dotExpression(node);
+
+            case IDENT:
+            case LITERAL_boolean:
+            case LITERAL_byte:
+            case LITERAL_char:
+            case LITERAL_double:
+            case LITERAL_float:
+            case LITERAL_int:
+            case LITERAL_long:
+            case LITERAL_short:
+            case LITERAL_void:
+                return variableExpression(node);
+
+            case LIST_CONSTRUCTOR:
+                return listExpression(node);
+
+            case MAP_CONSTRUCTOR:
+                return mapExpression(node);
+
+            case LABELED_ARG:
+                return mapEntryExpression(node);
+
+            case SPREAD_ARG:
+                return spreadExpression(node);
+
+            case SPREAD_MAP_ARG:
+                return spreadMapExpression(node);
+
+            // commented out of groovy.g due to non determinisms
+            //case MEMBER_POINTER_DEFAULT:
+            //    return defaultMethodPointerExpression(node);
+
+            case MEMBER_POINTER:
+                return methodPointerExpression(node);
+
+            case INDEX_OP:
+                return indexExpression(node);
+
+            case LITERAL_instanceof:
+                return instanceofExpression(node);
+
+            case LITERAL_as:
+                return asExpression(node);
+
+            case TYPECAST:
+                return castExpression(node);
+
+                // literals
+
+            case LITERAL_true:
+                return ConstantExpression.TRUE;
+
+            case LITERAL_false:
+                return ConstantExpression.FALSE;
+
+            case LITERAL_null:
+                return ConstantExpression.NULL;
+
+            case STRING_LITERAL:
+                ConstantExpression constantExpression = new ConstantExpression(node.getText());
+                configureAST(constantExpression, node);
+                return constantExpression;
+
+            case STRING_CONSTRUCTOR:
+                return gstring(node);
+
+            case NUM_DOUBLE:
+            case NUM_FLOAT:
+            case NUM_BIG_DECIMAL:
+                return decimalExpression(node);
+
+            case NUM_BIG_INT:
+            case NUM_INT:
+            case NUM_LONG:
+                return integerExpression(node);
+
+            case LITERAL_this:
+                return VariableExpression.THIS_EXPRESSION;
+
+            case LITERAL_super:
+                return VariableExpression.SUPER_EXPRESSION;
+
+
+                // Unary expressions
+            case LNOT:
+                NotExpression notExpression = new NotExpression(expression(node.getFirstChild()));
+                configureAST(notExpression, node);
+                return notExpression;
+
+            case UNARY_MINUS:
+                return unaryMinusExpression(node);
+
+            case BNOT:
+                BitwiseNegationExpression bitwiseNegationExpression = new BitwiseNegationExpression(expression(node.getFirstChild()));
+                configureAST(bitwiseNegationExpression, node);
+                return bitwiseNegationExpression;
+
+            case UNARY_PLUS:
+                return unaryPlusExpression(node);
+
+                // Prefix expressions
+            case INC:
+                return prefixExpression(node, Types.PLUS_PLUS);
+
+            case DEC:
+                return prefixExpression(node, Types.MINUS_MINUS);
+
+                // Postfix expressions
+            case POST_INC:
+                return postfixExpression(node, Types.PLUS_PLUS);
+
+            case POST_DEC:
+                return postfixExpression(node, Types.MINUS_MINUS);
+
+
+                // Binary expressions
+
+            case ASSIGN:
+                return binaryExpression(Types.ASSIGN, node);
+
+            case EQUAL:
+                return binaryExpression(Types.COMPARE_EQUAL, node);
+
+            case NOT_EQUAL:
+                return binaryExpression(Types.COMPARE_NOT_EQUAL, node);
+
+            case COMPARE_TO:
+                return binaryExpression(Types.COMPARE_TO, node);
+
+            case LE:
+                return binaryExpression(Types.COMPARE_LESS_THAN_EQUAL, node);
+
+            case LT:
+                return binaryExpression(Types.COMPARE_LESS_THAN, node);
+
+            case GT:
+                return binaryExpression(Types.COMPARE_GREATER_THAN, node);
+
+            case GE:
+                return binaryExpression(Types.COMPARE_GREATER_THAN_EQUAL, node);
+
+                /**
+                 * TODO treble equal?
+                 return binaryExpression(Types.COMPARE_IDENTICAL, node);
+
+                 case ???:
+                 return binaryExpression(Types.LOGICAL_AND_EQUAL, node);
+
+                 case ???:
+                 return binaryExpression(Types.LOGICAL_OR_EQUAL, node);
+
+                 */
+
+            case LAND:
+                return binaryExpression(Types.LOGICAL_AND, node);
+
+            case LOR:
+                return binaryExpression(Types.LOGICAL_OR, node);
+
+            case BAND:
+                return binaryExpression(Types.BITWISE_AND, node);
+
+            case BAND_ASSIGN:
+                return binaryExpression(Types.BITWISE_AND_EQUAL, node);
+
+            case BOR:
+                return binaryExpression(Types.BITWISE_OR, node);
+
+            case BOR_ASSIGN:
+                return binaryExpression(Types.BITWISE_OR_EQUAL, node);
+
+            case BXOR:
+                return binaryExpression(Types.BITWISE_XOR, node);
+
+            case BXOR_ASSIGN:
+                return binaryExpression(Types.BITWISE_XOR_EQUAL, node);
+
+
+            case PLUS:
+                return binaryExpression(Types.PLUS, node);
+
+            case PLUS_ASSIGN:
+                return binaryExpression(Types.PLUS_EQUAL, node);
+
+
+            case MINUS:
+                return binaryExpression(Types.MINUS, node);
+
+            case MINUS_ASSIGN:
+                return binaryExpression(Types.MINUS_EQUAL, node);
+
+
+            case STAR:
+                return binaryExpression(Types.MULTIPLY, node);
+
+            case STAR_ASSIGN:
+                return binaryExpression(Types.MULTIPLY_EQUAL, node);
+
+
+            case STAR_STAR:
+                return binaryExpression(Types.POWER, node);
+
+            case STAR_STAR_ASSIGN:
+                return binaryExpression(Types.POWER_EQUAL, node);
+
+
+            case DIV:
+                return binaryExpression(Types.DIVIDE, node);
+
+            case DIV_ASSIGN:
+                return binaryExpression(Types.DIVIDE_EQUAL, node);
+
+
+            case MOD:
+                return binaryExpression(Types.MOD, node);
+
+            case MOD_ASSIGN:
+                return binaryExpression(Types.MOD_EQUAL, node);
+
+            case SL:
+                return binaryExpression(Types.LEFT_SHIFT, node);
+
+            case SL_ASSIGN:
+                return binaryExpression(Types.LEFT_SHIFT_EQUAL, node);
+
+            case SR:
+                return binaryExpression(Types.RIGHT_SHIFT, node);
+
+            case SR_ASSIGN:
+                return binaryExpression(Types.RIGHT_SHIFT_EQUAL, node);
+
+            case BSR:
+                return binaryExpression(Types.RIGHT_SHIFT_UNSIGNED, node);
+
+            case BSR_ASSIGN:
+                return binaryExpression(Types.RIGHT_SHIFT_UNSIGNED_EQUAL, node);
+
+            case VARIABLE_DEF:
+                return declarationExpression(node);
+                
+                // Regex
+            case REGEX_FIND:
+                return binaryExpression(Types.FIND_REGEX, node);
+
+            case REGEX_MATCH:
+                return binaryExpression(Types.MATCH_REGEX, node);
+
+
+                // Ranges
+            case RANGE_INCLUSIVE:
+                return rangeExpression(node, true);
+
+            case RANGE_EXCLUSIVE:
+                return rangeExpression(node, false);
+
+            case DYNAMIC_MEMBER:
+                return dynamicMemberExpression(node);
+                
+            case LITERAL_in:
+                return binaryExpression(Types.KEYWORD_IN,node);
+                
+            case ANNOTATION:
+                return new AnnotationConstantExpression(annotation(node));
+
+            case CLOSURE_LIST:
+                return closureListExpression(node);
+                
+            default:
+                unknownAST(node);
+        }
+        return null;
+    }
+    
+    private ClosureListExpression closureListExpression(AST node) {
+        AST exprNode = node.getFirstChild();
+        LinkedList list = new LinkedList();
+        while (exprNode!=null) {
+            if (isType(EXPR,exprNode)) {
+                Expression expr = expression(exprNode);
+                configureAST(expr, exprNode);
+                list.add(expr);                
+            } else {
+                assertNodeType(EMPTY_STAT, exprNode);
+                list.add(EmptyExpression.INSTANCE);
+            }
+            
+            exprNode = exprNode.getNextSibling();
+        }
+        ClosureListExpression cle = new ClosureListExpression(list);
+        configureAST(cle,node);
+        return cle;
+    }
+
+    protected Expression dynamicMemberExpression(AST dynamicMemberNode) {
+        AST node = dynamicMemberNode.getFirstChild();
+        return expression(node);
+    }
+
+    protected Expression ternaryExpression(AST ternaryNode) {
+        AST node = ternaryNode.getFirstChild();
+        Expression base = expression(node);
+        node = node.getNextSibling();
+        Expression left = expression(node);
+        node = node.getNextSibling();
+        Expression ret;
+        if (node==null) {
+            ret = new ElvisOperatorExpression(base, left);
+        } else {
+            Expression right = expression(node);
+            BooleanExpression booleanExpression = new BooleanExpression(base);
+            booleanExpression.setSourcePosition(base);
+            ret = new TernaryExpression(booleanExpression, left, right);
+        }
+        configureAST(ret, ternaryNode);
+        return ret;
+    }
+
+    protected Expression variableExpression(AST node) {
+        String text = node.getText();
+
+        // TODO we might wanna only try to resolve the name if we are
+        // on the left hand side of an expression or before a dot?
+        VariableExpression variableExpression = new VariableExpression(text);
+        configureAST(variableExpression, node);
+        return variableExpression;
+    }
+
+    protected Expression rangeExpression(AST rangeNode, boolean inclusive) {
+        AST node = rangeNode.getFirstChild();
+        Expression left = expression(node);
+        Expression right = expression(node.getNextSibling());
+        RangeExpression rangeExpression = new RangeExpression(left, right, inclusive);
+        configureAST(rangeExpression, rangeNode);
+        return rangeExpression;
+    }
+
+    protected Expression spreadExpression(AST node) {
+        AST exprNode = node.getFirstChild();
+        AST listNode = exprNode.getFirstChild();
+        Expression right = expression(listNode);
+        SpreadExpression spreadExpression = new SpreadExpression(right);
+        configureAST(spreadExpression, node);
+        return spreadExpression;
+    }
+
+    protected Expression spreadMapExpression(AST node) {
+        AST exprNode = node.getFirstChild();
+        Expression expr = expression(exprNode);
+        SpreadMapExpression spreadMapExpression = new SpreadMapExpression(expr);
+        configureAST(spreadMapExpression, node);
+        return spreadMapExpression;
+    }
+
+    protected Expression methodPointerExpression(AST node) {
+        AST exprNode = node.getFirstChild();
+        Expression objectExpression = expression(exprNode);
+        AST mNode = exprNode.getNextSibling();
+        Expression methodName;
+        if (isType(DYNAMIC_MEMBER, mNode)) {
+            methodName = expression(mNode);
+        } else {
+            methodName = new ConstantExpression(identifier(mNode));
+        }
+        MethodPointerExpression methodPointerExpression = new MethodPointerExpression(objectExpression, methodName);
+        configureAST(methodPointerExpression, node);
+        return methodPointerExpression;
+    }
+
+/*  commented out due to groovy.g non-determinisms
+  protected Expression defaultMethodPointerExpression(AST node) {
+        AST exprNode = node.getFirstChild();
+        String methodName = exprNode.toString();
+        MethodPointerExpression methodPointerExpression = new MethodPointerExpression(null, methodName);
+        configureAST(methodPointerExpression, node);
+        return methodPointerExpression;
+    }
+*/
+
+    protected Expression listExpression(AST listNode) {
+        List expressions = new ArrayList();
+        AST elist = listNode.getFirstChild();
+        assertNodeType(ELIST, elist);
+
+        for (AST node = elist.getFirstChild(); node != null; node = node.getNextSibling()) {
+            // check for stray labeled arguments:
+            switch (node.getType()) {
+            case LABELED_ARG:       assertNodeType(COMMA, node);       break;  // helpful error?
+            case SPREAD_MAP_ARG:    assertNodeType(SPREAD_ARG, node);  break;  // helpful error
+            }
+            expressions.add(expression(node));
+        }
+        ListExpression listExpression = new ListExpression(expressions);
+        configureAST(listExpression, listNode);
+        return listExpression;
+    }
+
+    /**
+     * Typically only used for map constructors I think?
+     */
+    protected Expression mapExpression(AST mapNode) {
+        List expressions = new ArrayList();
+        AST elist = mapNode.getFirstChild();
+        if (elist != null) {  // totally empty in the case of [:]
+            assertNodeType(ELIST, elist);
+            for (AST node = elist.getFirstChild(); node != null; node = node.getNextSibling()) {
+                switch (node.getType()) {
+                case LABELED_ARG:
+                case SPREAD_MAP_ARG:
+                    break;  // legal cases
+                case SPREAD_ARG:
+                    assertNodeType(SPREAD_MAP_ARG, node);  break;  // helpful error
+                default:
+                    assertNodeType(LABELED_ARG, node);  break;  // helpful error
+                }
+                expressions.add(mapEntryExpression(node));
+            }
+        }
+        MapExpression mapExpression = new MapExpression(expressions);
+        configureAST(mapExpression, mapNode);
+        return mapExpression;
+    }
+
+    protected MapEntryExpression mapEntryExpression(AST node) {
+        if (node.getType() == SPREAD_MAP_ARG) {
+            AST rightNode = node.getFirstChild();
+            Expression keyExpression = spreadMapExpression(node);
+            Expression rightExpression = expression(rightNode);
+            MapEntryExpression mapEntryExpression = new MapEntryExpression(keyExpression, rightExpression);
+            configureAST(mapEntryExpression, node);
+            return mapEntryExpression;
+        }
+        else {
+            AST keyNode = node.getFirstChild();
+            Expression keyExpression = expression(keyNode);
+            AST valueNode = keyNode.getNextSibling();
+            Expression valueExpression = expression(valueNode);
+            MapEntryExpression mapEntryExpression = new MapEntryExpression(keyExpression, valueExpression);
+            configureAST(mapEntryExpression, node);
+            return mapEntryExpression;
+        }
+    }
+
+
+    protected Expression instanceofExpression(AST node) {
+        AST leftNode = node.getFirstChild();
+        Expression leftExpression = expression(leftNode);
+
+        AST rightNode = leftNode.getNextSibling();
+        ClassNode type = buildName(rightNode);
+        assertTypeNotNull(type, rightNode);
+
+        Expression rightExpression = new ClassExpression(type);
+        configureAST(rightExpression, rightNode);
+        BinaryExpression binaryExpression = new BinaryExpression(leftExpression, makeToken(Types.KEYWORD_INSTANCEOF, node), rightExpression);
+        configureAST(binaryExpression, node);
+        return binaryExpression;
+    }
+
+    protected void assertTypeNotNull(ClassNode type, AST rightNode) {
+        if (type == null) {
+            throw new ASTRuntimeException(rightNode, "No type available for: " + qualifiedName(rightNode));
+        }
+    }
+
+    protected Expression asExpression(AST node) {
+        AST leftNode = node.getFirstChild();
+        Expression leftExpression = expression(leftNode);
+
+        AST rightNode = leftNode.getNextSibling();
+        ClassNode type = buildName(rightNode);
+
+        return CastExpression.asExpression(type, leftExpression);
+    }
+
+    protected Expression castExpression(AST castNode) {
+        AST node = castNode.getFirstChild();
+        ClassNode type = buildName(node);
+        assertTypeNotNull(type, node);
+
+        AST expressionNode = node.getNextSibling();
+        Expression expression = expression(expressionNode);
+
+        CastExpression castExpression = new CastExpression(type, expression);
+        configureAST(castExpression, castNode);
+        return castExpression;
+    }
+
+
+    protected Expression indexExpression(AST indexNode) {
+        AST leftNode = indexNode.getFirstChild();
+        Expression leftExpression = expression(leftNode);
+
+        AST rightNode = leftNode.getNextSibling();
+        Expression rightExpression = expression(rightNode);
+
+        BinaryExpression binaryExpression = new BinaryExpression(leftExpression, makeToken(Types.LEFT_SQUARE_BRACKET, indexNode), rightExpression);
+        configureAST(binaryExpression, indexNode);
+        return binaryExpression;
+    }
+
+    protected Expression binaryExpression(int type, AST node) {
+        Token token = makeToken(type, node);
+
+        AST leftNode = node.getFirstChild();
+        Expression leftExpression = expression(leftNode);
+
+        AST rightNode = leftNode.getNextSibling();
+        if (rightNode == null) {
+            return leftExpression;
+        }
+
+        if (Types.ofType(type, Types.ASSIGNMENT_OPERATOR)) {
+            if (leftExpression instanceof VariableExpression || leftExpression.getClass() == PropertyExpression.class
+                                                             || leftExpression instanceof FieldExpression
+                                                             || leftExpression instanceof AttributeExpression
+                                                             || leftExpression instanceof DeclarationExpression) {
+                // Do nothing.
+            }
+            else if (leftExpression instanceof ConstantExpression) {
+                throw new ASTRuntimeException(node, "\n[" + ((ConstantExpression) leftExpression).getValue() + "] is a constant expression, but it should be a variable expression");
+            }
+            else if (leftExpression instanceof BinaryExpression) {
+                Expression leftexp = ((BinaryExpression) leftExpression).getLeftExpression();
+                int lefttype = ((BinaryExpression) leftExpression).getOperation().getType();
+                if (!Types.ofType(lefttype, Types.ASSIGNMENT_OPERATOR) && lefttype != Types.LEFT_SQUARE_BRACKET) {
+                    throw new ASTRuntimeException(node, "\n" + ((BinaryExpression) leftExpression).getText() + " is a binary expression, but it should be a variable expression");
+                }
+            }
+            else if (leftExpression instanceof GStringExpression) {
+                throw new ASTRuntimeException(node, "\n\"" + ((GStringExpression) leftExpression).getText() + "\" is a GString expression, but it should be a variable expression");
+            }
+            else if (leftExpression instanceof MethodCallExpression) {
+                throw new ASTRuntimeException(node, "\n\"" + ((MethodCallExpression) leftExpression).getText() + "\" is a method call expression, but it should be a variable expression");
+            }
+            else if (leftExpression instanceof MapExpression) {
+                throw new ASTRuntimeException(node, "\n'" + ((MapExpression) leftExpression).getText() + "' is a map expression, but it should be a variable expression");
+            }
+            else {
+                throw new ASTRuntimeException(node, "\n" + leftExpression.getClass() + ", with its value '" + leftExpression.getText() + "', is a bad expression as the LSH of an assignment operator");
+            }
+        }
+        /*if (rightNode == null) {
+            throw new NullPointerException("No rightNode associated with binary expression");
+        }*/
+        Expression rightExpression = expression(rightNode);
+        BinaryExpression binaryExpression = new BinaryExpression(leftExpression, token, rightExpression);
+        configureAST(binaryExpression, node);
+        return binaryExpression;
+    }
+
+    protected Expression prefixExpression(AST node, int token) {
+        Expression expression = expression(node.getFirstChild());
+        PrefixExpression prefixExpression = new PrefixExpression(makeToken(token, node), expression);
+        configureAST(prefixExpression, node);
+        return prefixExpression;
+    }
+
+    protected Expression postfixExpression(AST node, int token) {
+        Expression expression = expression(node.getFirstChild());
+        PostfixExpression postfixExpression = new PostfixExpression(expression, makeToken(token, node));
+        configureAST(postfixExpression, node);
+        return postfixExpression;
+    }
+
+    protected BooleanExpression booleanExpression(AST node) {
+        BooleanExpression booleanExpression = new BooleanExpression(expression(node));
+        configureAST(booleanExpression, node);
+        return booleanExpression;
+    }
+
+    protected Expression dotExpression(AST node) {
+        // lets decide if this is a propery invocation or a method call
+        AST leftNode = node.getFirstChild();
+        if (leftNode != null) {
+            AST identifierNode = leftNode.getNextSibling();
+            if (identifierNode != null) {
+                Expression leftExpression = expression(leftNode);
+                if (isType(SELECT_SLOT, identifierNode)) {
+                    Expression field = expression(identifierNode.getFirstChild(),true);
+                    AttributeExpression attributeExpression = new AttributeExpression(leftExpression, field, node.getType() != DOT);
+                    if (node.getType() == SPREAD_DOT) {
+                        attributeExpression.setSpreadSafe(true);
+                    }
+                    configureAST(attributeExpression, node);
+                    return attributeExpression;
+                }
+                Expression property = expression(identifierNode,true);
+                
+                PropertyExpression propertyExpression = new PropertyExpression(leftExpression, property, node.getType() != DOT);
+                if (node.getType() == SPREAD_DOT) {
+                    propertyExpression.setSpreadSafe(true);
+                } 
+                configureAST(propertyExpression, node);
+                return propertyExpression;
+            }
+        }
+        return methodCallExpression(node);
+    }
+    
+    protected Expression specialConstructorCallExpression(AST methodCallNode, ClassNode special) {
+        AST node = methodCallNode.getFirstChild();
+        Expression arguments = arguments(node);
+        
+        ConstructorCallExpression expression = new ConstructorCallExpression(special, arguments);
+        configureAST(expression, methodCallNode);
+        return expression;
+    }
+
+    private int getTypeInParenthesis(AST node) {
+        if (! isType(EXPR,node) ) node = node.getFirstChild();
+        while (node!=null &&isType(EXPR,node) && node.getNextSibling()==null) {
+            node = node.getFirstChild();
+        }
+        if (node==null) return -1;
+        return node.getType();
+    }
+
+    protected Expression methodCallExpression(AST methodCallNode) {
+        AST node = methodCallNode.getFirstChild();
+        /* // Bad idea, since foo(1)(2) is valid Groovy for foo(1).call(2).
+        if (isType(METHOD_CALL, node)) {
+            // sometimes method calls get wrapped in method calls for some wierd reason
+            return methodCallExpression(node);
+        }
+        */
+
+        Expression objectExpression;
+        AST selector;
+        AST elist = node.getNextSibling();
+        
+        boolean implicitThis = false;
+        boolean safe = isType(OPTIONAL_DOT, node);
+        boolean spreadSafe = isType(SPREAD_DOT, node);
+        if (isType(DOT, node) || safe || spreadSafe) {
+            AST objectNode = node.getFirstChild();
+            objectExpression = expression(objectNode);
+            selector = objectNode.getNextSibling();
+        } else {
+            implicitThis = true;
+            objectExpression = VariableExpression.THIS_EXPRESSION;
+            selector = node;
+        } 
+
+        Expression name = null;
+        if (isType(LITERAL_super, selector)) {
+            implicitThis = true;
+            name = new ConstantExpression("super");
+            if (objectExpression == VariableExpression.THIS_EXPRESSION) {
+                objectExpression = VariableExpression.SUPER_EXPRESSION;
+            }
+        } else if (isPrimitiveTypeLiteral(selector)) {
+            throw new ASTRuntimeException(selector, "Primitive type literal: " + selector.getText()
+                    + " cannot be used as a method name");
+        } else if (isType(SELECT_SLOT, selector)) {
+            Expression field = expression(selector.getFirstChild(),true);
+            AttributeExpression attributeExpression = new AttributeExpression(objectExpression, field, node.getType() != DOT);
+            configureAST(attributeExpression, node);
+            Expression arguments = arguments(elist);
+            MethodCallExpression expression = new MethodCallExpression(attributeExpression, "call", arguments);
+            configureAST(expression, methodCallNode);
+            return expression;
+        } else if  
+               (isType(DYNAMIC_MEMBER, selector) || isType(IDENT,selector) || 
+                isType(STRING_CONSTRUCTOR,selector) || isType (STRING_LITERAL,selector)) 
+        { 
+            name = expression(selector,true);
+        } else {
+            implicitThis = false;
+            name = new ConstantExpression("call");
+            objectExpression = expression(selector,true);
+        } 
+
+        Expression arguments = arguments(elist);
+        MethodCallExpression expression = new MethodCallExpression(objectExpression, name, arguments);
+        expression.setSafe(safe);
+        expression.setSpreadSafe(spreadSafe);
+        expression.setImplicitThis(implicitThis);
+        Expression ret = expression;
+        //FIXME: do we really want this() to create a new object regardless
+        // the position.. for example not as first statement in a constructor
+        // this=first statement in constructor is handled by specialConstructorCallExpression
+        // we may have to add a check and remove this part of the code
+        if (implicitThis && "this".equals(expression.getMethodAsString())) {
+            ret = new ConstructorCallExpression(this.classNode, arguments);
+        }
+        configureAST(ret, methodCallNode);
+        return ret;
+    }
+    
+    protected Expression constructorCallExpression(AST node) {
+        AST constructorCallNode = node;
+        ClassNode type = buildName(constructorCallNode);
+
+        if (isType(CTOR_CALL, node) || isType(LITERAL_new, node)) {
+            node = node.getFirstChild();
+        }
+
+        AST elist = node.getNextSibling();
+
+        if (elist == null && isType(ELIST, node)) {
+            elist = node;
+            if ("(".equals(type.getName())) {
+                type = classNode;
+            }
+        }
+
+        if (isType(ARRAY_DECLARATOR, elist)) {
+            AST expressionNode = elist.getFirstChild();
+            if (expressionNode == null) {
+                throw new ASTRuntimeException(elist, "No expression for the array constructor call");
+            }
+            List size = arraySizeExpression(expressionNode);
+            ArrayExpression arrayExpression = new ArrayExpression(type, null, size);
+            configureAST(arrayExpression, constructorCallNode);
+            return arrayExpression;
+        }
+        Expression arguments = arguments(elist);
+        ConstructorCallExpression expression = new ConstructorCallExpression(type, arguments);
+        configureAST(expression, constructorCallNode);
+        return expression;
+    }
+    
+    protected List arraySizeExpression(AST node) {
+        List list;
+        Expression size = null;
+    	if (isType(ARRAY_DECLARATOR,node)) {
+    		AST right = node.getNextSibling();
+        	if (right!=null) {
+        		size = expression(right);
+        	} else {
+        		size = ConstantExpression.EMTPY_EXPRESSION;
+        	}
+        	list = arraySizeExpression(node.getFirstChild());
+        } else {
+        	size = expression(node);
+        	list = new ArrayList();
+        }
+    	list.add(size);
+    	return list;
+    }
+
+    protected Expression arguments(AST elist) {
+        List expressionList = new ArrayList();
+        // FIXME: all labeled arguments should follow any unlabeled arguments
+        boolean namedArguments = false;
+        for (AST node = elist; node != null; node = node.getNextSibling()) {
+            if (isType(ELIST, node)) {
+                for (AST child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
+                    namedArguments |= addArgumentExpression(child, expressionList);
+                }
+            }
+            else {
+                namedArguments |= addArgumentExpression(node, expressionList);
+            }
+        }
+        if (namedArguments) {
+            if (!expressionList.isEmpty()) {
+                // lets remove any non-MapEntryExpression instances
+                // such as if the last expression is a ClosureExpression
+                // so lets wrap the named method calls in a Map expression
+                List argumentList = new ArrayList();
+                for (Iterator iter = expressionList.iterator(); iter.hasNext();) {
+                    Expression expression = (Expression) iter.next();
+                    if (!(expression instanceof MapEntryExpression)) {
+                        argumentList.add(expression);
+                    }
+                }
+                if (!argumentList.isEmpty()) {
+                    expressionList.removeAll(argumentList);
+                    MapExpression mapExpression = new MapExpression(expressionList);
+                    configureAST(mapExpression, elist);
+                    argumentList.add(0, mapExpression);
+                    ArgumentListExpression argumentListExpression = new ArgumentListExpression(argumentList);
+                    configureAST(argumentListExpression, elist);
+                    return argumentListExpression;
+                }
+            }
+            NamedArgumentListExpression namedArgumentListExpression = new NamedArgumentListExpression(expressionList);
+            configureAST(namedArgumentListExpression, elist);
+            return namedArgumentListExpression;
+        }
+        else {
+            ArgumentListExpression argumentListExpression = new ArgumentListExpression(expressionList);
+            configureAST(argumentListExpression, elist);
+            return argumentListExpression;
+        }
+    }
+
+    protected boolean addArgumentExpression(AST node, List expressionList) {
+        if (node.getType() == SPREAD_MAP_ARG) {
+            AST rightNode = node.getFirstChild();
+            Expression keyExpression = spreadMapExpression(node);
+            Expression rightExpression = expression(rightNode);
+            MapEntryExpression mapEntryExpression = new MapEntryExpression(keyExpression, rightExpression);
+            expressionList.add(mapEntryExpression);
+            return true;
+        }
+        else {
+            Expression expression = expression(node);
+            expressionList.add(expression);
+            return expression instanceof MapEntryExpression;
+        }
+    }
+
+    protected Expression expressionList(AST node) {
+        List expressionList = new ArrayList();
+        for (AST child = node.getFirstChild(); child != null; child = child.getNextSibling()) {
+            expressionList.add(expression(child));
+        }
+        if (expressionList.size() == 1) {
+            return (Expression) expressionList.get(0);
+        }
+        else {
+            ListExpression listExpression = new ListExpression(expressionList);
+            configureAST(listExpression, node);
+            return listExpression;
+        }
+    }
+
+    protected ClosureExpression closureExpression(AST node) {
+        AST paramNode = node.getFirstChild();
+        Parameter[] parameters = null;
+        AST codeNode = paramNode;
+        if (isType(PARAMETERS, paramNode) || isType(IMPLICIT_PARAMETERS, paramNode)) {
+            parameters = parameters(paramNode);
+            codeNode = paramNode.getNextSibling();
+        }
+        Statement code = statementListNoChild(codeNode);
+        ClosureExpression closureExpression = new ClosureExpression(parameters, code);
+        configureAST(closureExpression, node);
+        return closureExpression;
+    }
+
+    protected Expression blockExpression(AST node) {
+        AST codeNode = node.getFirstChild();
+        if (codeNode == null)  return ConstantExpression.NULL;
+        if (codeNode.getType() == EXPR && codeNode.getNextSibling() == null) {
+            // Simplify common case of {expr} to expr.
+            return expression(codeNode);
+        }
+        Parameter[] parameters = Parameter.EMPTY_ARRAY;
+        Statement code = statementListNoChild(codeNode);
+        ClosureExpression closureExpression = new ClosureExpression(parameters, code);
+        configureAST(closureExpression, node);
+        // Call it immediately.
+        String callName = "call";
+        Expression noArguments = new ArgumentListExpression();
+        MethodCallExpression call = new MethodCallExpression(closureExpression, callName, noArguments);
+        configureAST(call, node);
+        return call;
+    }
+
+    protected Expression unaryMinusExpression(AST unaryMinusExpr) {
+        AST node = unaryMinusExpr.getFirstChild();
+
+        // if we are a number literal then lets just parse it
+        // as the negation operator on MIN_INT causes rounding to a long
+        String text = node.getText();
+        switch (node.getType()) {
+            case NUM_DOUBLE:
+            case NUM_FLOAT:
+            case NUM_BIG_DECIMAL:
+                ConstantExpression constantExpression = new ConstantExpression(Numbers.parseDecimal("-" + text));
+                configureAST(constantExpression, unaryMinusExpr);
+                return constantExpression;
+
+            case NUM_BIG_INT:
+            case NUM_INT:
+            case NUM_LONG:
+                ConstantExpression constantLongExpression = new ConstantExpression(Numbers.parseInteger("-" + text));
+                configureAST(constantLongExpression, unaryMinusExpr);
+                return constantLongExpression;
+
+            default:
+                UnaryMinusExpression unaryMinusExpression = new UnaryMinusExpression(expression(node));
+                configureAST(unaryMinusExpression, unaryMinusExpr);
+                return unaryMinusExpression;
+        }
+    }
+
+    protected Expression unaryPlusExpression(AST unaryPlusExpr) {
+        AST node = unaryPlusExpr.getFirstChild();
+        switch (node.getType()) {
+            case NUM_DOUBLE:
+            case NUM_FLOAT:
+            case NUM_BIG_DECIMAL:
+            case NUM_BIG_INT:
+            case NUM_INT:
+            case NUM_LONG:
+                return expression(node);
+
+            default:
+                UnaryPlusExpression unaryPlusExpression = new UnaryPlusExpression(expression(node));
+                configureAST(unaryPlusExpression, unaryPlusExpr);
+                return unaryPlusExpression;
+        }
+    }
+
+    protected ConstantExpression decimalExpression(AST node) {
+        String text = node.getText();
+        ConstantExpression constantExpression = new ConstantExpression(Numbers.parseDecimal(text));
+        configureAST(constantExpression, node);
+        return constantExpression;
+    }
+
+    protected ConstantExpression integerExpression(AST node) {
+        String text = node.getText();
+        ConstantExpression constantExpression = new ConstantExpression(Numbers.parseInteger(text));
+        configureAST(constantExpression, node);
+        return constantExpression;
+    }
+
+    protected Expression gstring(AST gstringNode) {
+        List strings = new ArrayList();
+        List values = new ArrayList();
+
+        StringBuffer buffer = new StringBuffer();
+
+        boolean isPrevString = false;
+
+        for (AST node = gstringNode.getFirstChild(); node != null; node = node.getNextSibling()) {
+            int type = node.getType();
+            String text = null;
+            switch (type) {
+
+                case STRING_LITERAL:
+                    if (isPrevString)  assertNodeType(IDENT, node);  // parser bug
+                    isPrevString = true;
+                    text = node.getText();
+                    ConstantExpression constantExpression = new ConstantExpression(text);
+                    configureAST(constantExpression, node);
+                    strings.add(constantExpression);
+                    buffer.append(text);
+                    break;
+
+                default:
+                    {
+                        if (!isPrevString)  assertNodeType(IDENT, node);  // parser bug
+                        isPrevString = false;
+                        Expression expression = expression(node);
+                        values.add(expression);
+                        buffer.append("$");
+                        buffer.append(expression.getText());
+                    }
+                    break;
+            }
+        }
+        GStringExpression gStringExpression = new GStringExpression(buffer.toString(), strings, values);
+        configureAST(gStringExpression, gstringNode);
+        return gStringExpression;
+    }
+
+    protected ClassNode type(AST typeNode) {
+        // TODO intern types?
+        // TODO configureAST(...)
+        return buildName(typeNode.getFirstChild());
+    }
+
+    public static String qualifiedName(AST qualifiedNameNode) {
+    	if (isType(IDENT, qualifiedNameNode)) {
+        	return qualifiedNameNode.getText();
+        }
+        if (isType(DOT, qualifiedNameNode)) {
+            AST node = qualifiedNameNode.getFirstChild();
+            StringBuffer buffer = new StringBuffer();
+            boolean first = true;
+
+            for (; node != null && !isType(TYPE_ARGUMENTS,node); node = node.getNextSibling()) {
+                if (first) {
+                    first = false;
+                }
+                else {
+                    buffer.append(".");
+                }
+                buffer.append(qualifiedName(node));
+            }
+            return buffer.toString();
+        }
+        else {
+            return qualifiedNameNode.getText();
+        }
+    }
+    
+    private static AST getTypeArgumentsNode(AST root) {
+        while (root!=null && !isType(TYPE_ARGUMENTS,root)) {
+            root = root.getNextSibling();
+        }
+        return root;
+    }
+    
+    private int getBoundType(AST node) {
+        if (node==null) return -1;
+        if (isType(TYPE_UPPER_BOUNDS,node)) return TYPE_UPPER_BOUNDS;
+        if (isType(TYPE_LOWER_BOUNDS,node)) return TYPE_LOWER_BOUNDS;
+        throw new ASTRuntimeException(node, 
+                "Unexpected node type: " + getTokenName(node) + 
+                " found when expecting type: " + getTokenName(TYPE_UPPER_BOUNDS) +
+                " or type: " + getTokenName(TYPE_LOWER_BOUNDS));       
+    }
+    
+    private GenericsType makeGenericsArgumentType(AST rootNode) {
+        GenericsType gt;
+        if (isType(WILDCARD_TYPE,rootNode)) {
+            ClassNode base = ClassHelper.makeWithoutCaching("?");
+            if (rootNode.getNextSibling()!=null) {
+                int boundType = getBoundType(rootNode.getNextSibling());
+                ClassNode[] gts = makeGenericsBounds(rootNode,boundType);
+                if (boundType==TYPE_UPPER_BOUNDS) {
+                    gt = new GenericsType(base,gts,null);
+                } else {
+                    gt = new GenericsType(base,null,gts[0]);
+                }
+            } else {
+                gt = new GenericsType(base,null,null);
+            }
+            gt.setName("?");
+            gt.setWildcard(true);
+        } else {
+            ClassNode argument = makeTypeWithArguments(rootNode);
+            gt = new GenericsType(argument);
+        }
+        configureAST(gt, rootNode);
+        return gt;
+    }
+    
+    protected ClassNode makeTypeWithArguments(AST rootNode) {
+        ClassNode basicType = makeType(rootNode);
+        LinkedList typeArgumentList = new LinkedList();
+        AST node = rootNode.getFirstChild();
+        if (node==null || isType(INDEX_OP, node) || isType(ARRAY_DECLARATOR, node)) return basicType;
+        //TODO: recognize combinatons of inner classes and generic types
+        if (isType(DOT, node)) return basicType;
+        node = node.getFirstChild();
+        if (node==null) return basicType;
+        assertNodeType(TYPE_ARGUMENTS, node);
+        AST typeArgument = node.getFirstChild();
+        
+        while (typeArgument != null) {
+            assertNodeType(TYPE_ARGUMENT, typeArgument);            
+            GenericsType gt = makeGenericsArgumentType(typeArgument.getFirstChild());
+            typeArgumentList.add(gt);
+            typeArgument = typeArgument.getNextSibling();
+        }
+        
+        if (typeArgumentList.size()>0) {
+            basicType.setGenericsTypes((GenericsType[]) typeArgumentList.toArray(new GenericsType[0]));
+        }
+        return basicType;
+    }
+    
+    private ClassNode[] makeGenericsBounds(AST rn, int boundType) {
+        AST boundsRoot = rn.getNextSibling();
+        if (boundsRoot==null) return null;
+        assertNodeType(boundType, boundsRoot);
+        LinkedList bounds = new LinkedList();
+        for ( AST boundsNode = boundsRoot.getFirstChild(); 
+              boundsNode!=null; 
+              boundsNode=boundsNode.getNextSibling()
+        ) {
+            ClassNode bound = null;
+            bound = makeTypeWithArguments(boundsNode);
+            configureAST(bound, boundsNode);
+            bounds.add(bound);
+        }
+        if (bounds.size()==0) return null;
+        return (ClassNode[]) bounds.toArray(new ClassNode[bounds.size()]);
+    }
+    
+    protected GenericsType[] makeGenericsType(AST rootNode) {
+        AST typeParameter = rootNode.getFirstChild();
+        LinkedList ret = new LinkedList();
+        assertNodeType(TYPE_PARAMETER, typeParameter);
+
+        while (isType(TYPE_PARAMETER, typeParameter)) {
+            AST typeNode = typeParameter.getFirstChild();
+            ClassNode type = makeType(typeParameter);
+            
+            GenericsType gt = new GenericsType(type, makeGenericsBounds(typeNode,TYPE_UPPER_BOUNDS),null);
+            configureAST(gt, typeNode);
+            
+            ret.add(gt);
+            typeParameter = typeParameter.getNextSibling();
+        }
+        return (GenericsType[]) ret.toArray(new GenericsType[0]);
+    }
+    
+    protected ClassNode makeType(AST typeNode) {
+        ClassNode answer = ClassHelper.DYNAMIC_TYPE;
+        AST node = typeNode.getFirstChild();
+        if (node != null) {
+            if (isType(INDEX_OP, node) || isType(ARRAY_DECLARATOR, node)) {
+                answer = makeType(node).makeArray();
+            } else {
+                answer = ClassHelper.make(qualifiedName(node));
+            }
+            configureAST(answer,node);
+        }
+        return answer;
+    }
+
+    /**
+     * Performs a name resolution to see if the given name is a type from imports,
+     * aliases or newly created classes
+     */
+    /*protected String resolveTypeName(String name, boolean safe) {
+        if (name == null) {
+            return null;
+        }
+        return resolveNewClassOrName(name, safe);
+    }*/
+
+    /**
+     * Extracts an identifier from the Antlr AST and then performs a name resolution
+     * to see if the given name is a type from imports, aliases or newly created classes
+     */
+    protected ClassNode buildName(AST node) {
+        if (isType(TYPE, node)) {
+            node = node.getFirstChild();
+        }
+        ClassNode answer = null;
+        if (isType(DOT, node) || isType(OPTIONAL_DOT, node)) {
+            answer = ClassHelper.make(qualifiedName(node));
+        }
+        else if (isPrimitiveTypeLiteral(node)) {
+            answer = ClassHelper.make(node.getText());
+        }
+        else if (isType(INDEX_OP, node) || isType(ARRAY_DECLARATOR, node)) {
+            AST child = node.getFirstChild();
+            return buildName(child).makeArray();
+        }
+        else {
+            String identifier = node.getText();
+            answer = ClassHelper.make(identifier);
+        }
+        AST nextSibling = node.getNextSibling();
+        if (isType(INDEX_OP, nextSibling) || isType(ARRAY_DECLARATOR, node)) {
+            return answer.makeArray();
+        }
+        else {
+            return answer;
+        }
+    }
+
+    protected boolean isPrimitiveTypeLiteral(AST node) {
+        int type = node.getType();
+        switch (type) {
+            case LITERAL_boolean:
+            case LITERAL_byte:
+            case LITERAL_char:
+            case LITERAL_double:
+            case LITERAL_float:
+            case LITERAL_int:
+            case LITERAL_long:
+            case LITERAL_short:
+                return true;
+
+            default:
+                return false;
+        }
+    }
+
+    /**
+     * Extracts an identifier from the Antlr AST
+     */
+    protected String identifier(AST node) {
+        assertNodeType(IDENT, node);
+        return node.getText();
+    }
+
+    protected String label(AST labelNode) {
+        AST node = labelNode.getFirstChild();
+        if (node == null) {
+            return null;
+        }
+        return identifier(node);
+    }
+
+
+
+    // Helper methods
+    //-------------------------------------------------------------------------
+
+
+    /**
+     * Returns true if the modifiers flags contain a visibility modifier
+     */
+    protected boolean hasVisibility(int modifiers) {
+        return (modifiers & (Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED | Opcodes.ACC_PUBLIC)) != 0;
+    }
+
+    protected void configureAST(ASTNode node, AST ast) {
+        if (ast==null) throw new ASTRuntimeException(ast, "PARSER BUG: Tried to configure "+node.getClass().getName()+" with null Node");
+        node.setColumnNumber(ast.getColumn());
+        node.setLineNumber(ast.getLine());
+        if (ast instanceof GroovySourceAST) {
+            node.setLastColumnNumber(((GroovySourceAST)ast).getColumnLast());
+            node.setLastLineNumber(((GroovySourceAST)ast).getLineLast());
+        }
+
+        // TODO we could one day store the Antlr AST on the Groovy AST
+        // node.setCSTNode(ast);
+    }
+
+    protected static Token makeToken(int typeCode, AST node) {
+        return Token.newSymbol(typeCode, node.getLine(), node.getColumn());
+    }
+
+    protected String getFirstChildText(AST node) {
+        AST child = node.getFirstChild();
+        return child != null ? child.getText() : null;
+    }
+
+
+    public static boolean isType(int typeCode, AST node) {
+        return node != null && node.getType() == typeCode;
+    }
+
+    private String getTokenName(int token) {
+        if (tokenNames==null) return ""+token;
+        return tokenNames[token];
+    }
+    
+    private String getTokenName(AST node) {
+        if (node==null) return "null";
+        return getTokenName(node.getType());
+    }
+
+    protected void assertNodeType(int type, AST node) {
+        if (node == null) {
+            throw new ASTRuntimeException(node, "No child node available in AST when expecting type: " + getTokenName(type));
+        }
+        if (node.getType() != type) {            
+            throw new ASTRuntimeException(node, "Unexpected node type: " + getTokenName(node) + " found when expecting type: " + getTokenName(type));
+        }
+    }
+
+    protected void notImplementedYet(AST node) {
+        throw new ASTRuntimeException(node, "AST node not implemented yet for type: " + getTokenName(node));
+    }
+
+    protected void unknownAST(AST node) {
+        if (node.getType() == CLASS_DEF) {
+            throw new ASTRuntimeException(node,
+                    "Class definition not expected here. Possible attempt to use inner class. " +
+                            "Inner classes not supported, perhaps try using a closure instead.");
+        }
+        throw new ASTRuntimeException(node, "Unknown type: " + getTokenName(node));
+    }
+
+    protected void dumpTree(AST ast) {
+        for (AST node = ast.getFirstChild(); node != null; node = node.getNextSibling()) {
+            dump(node);
+        }
+    }
+
+    protected void dump(AST node) {
+        System.out.println("Type: " + getTokenName(node) + " text: " + node.getText());
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/AntlrParserPluginFactory.java b/groovy/src/main/org/codehaus/groovy/antlr/AntlrParserPluginFactory.java
new file mode 100644
index 0000000..a6ece81
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/AntlrParserPluginFactory.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr;
+
+import org.codehaus.groovy.control.ParserPlugin;
+import org.codehaus.groovy.control.ParserPluginFactory;
+
+/**
+ * @version $Revision$
+ */
+public class AntlrParserPluginFactory extends ParserPluginFactory {
+
+    public ParserPlugin createParserPlugin() {
+        return new AntlrParserPlugin();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/EnumHelper.java b/groovy/src/main/org/codehaus/groovy/antlr/EnumHelper.java
new file mode 100644
index 0000000..14e2ae3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/EnumHelper.java
@@ -0,0 +1,49 @@
+package org.codehaus.groovy.antlr;

+

+import org.codehaus.groovy.ast.ClassHelper;

+import org.codehaus.groovy.ast.ClassNode;

+import org.codehaus.groovy.ast.FieldNode;

+import org.codehaus.groovy.ast.GenericsType;

+import org.codehaus.groovy.ast.InnerClassNode;

+import org.codehaus.groovy.ast.MixinNode;

+import org.codehaus.groovy.ast.expr.Expression;

+import org.codehaus.groovy.ast.expr.ListExpression;

+import org.objectweb.asm.Opcodes;

+

+

+public class EnumHelper {

+    private static final int FS = Opcodes.ACC_FINAL | Opcodes.ACC_STATIC;

+    private static final int PUBLIC_FS = Opcodes.ACC_PUBLIC | FS; 

+    

+    public static ClassNode makeEnumNode(String name, int modifiers, ClassNode[] interfaces, ClassNode outerClass) {

+        modifiers = modifiers | Opcodes.ACC_FINAL | Opcodes.ACC_ENUM;

+        ClassNode enumClass;

+        if (outerClass==null) {

+            enumClass = new ClassNode(name,modifiers,null,interfaces,MixinNode.EMPTY_ARRAY);

+        } else {

+            name = outerClass.getName() + "$" + name;

+            enumClass = new InnerClassNode(outerClass,name,modifiers,null,interfaces,MixinNode.EMPTY_ARRAY);

+        }

+        

+        // set super class and generics info

+        // "enum X" -> class X extends Enum<X>

+        GenericsType gt = new GenericsType(enumClass);

+        ClassNode superClass = ClassHelper.makeWithoutCaching("java.lang.Enum");

+        superClass.setGenericsTypes(new GenericsType[]{gt});

+        enumClass.setSuperClass(superClass);

+        superClass.setRedirect(ClassHelper.Enum_Type);

+        

+        return enumClass;

+    }

+

+    public static void addEnumConstant(ClassNode enumClass, String name, Expression init) {

+        int modifiers = PUBLIC_FS | Opcodes.ACC_ENUM;

+        if  (init!=null && !(init instanceof ListExpression)) {

+            ListExpression list = new ListExpression();

+            list.addExpression(init);

+            init = list;

+        }

+        FieldNode fn = new FieldNode(name,modifiers,enumClass,enumClass,init);

+        enumClass.addField(fn);

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/antlr/GroovySourceAST.java b/groovy/src/main/org/codehaus/groovy/antlr/GroovySourceAST.java
new file mode 100644
index 0000000..4bbeaf3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/GroovySourceAST.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.antlr;
+
+import antlr.collections.AST;
+import antlr.*;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * We have an AST subclass so we can track source information.
+ * Very odd that ANTLR doesn't do this by default.
+ *
+ * @author Mike Spille
+ * @author Jeremy Rayner <groovy@ross-rayner.com>
+ */
+public class GroovySourceAST extends CommonAST implements Comparable {
+    private int line;
+    private int col;
+    private int lineLast;
+    private int colLast;
+    private String snippet;
+
+    public GroovySourceAST() {
+    }
+
+    public GroovySourceAST(Token t) {
+        super(t);
+    }
+
+    public void initialize(AST ast) {
+        super.initialize(ast);
+        line = ast.getLine();
+        col = ast.getColumn();
+    }
+
+    public void initialize(Token t) {
+        super.initialize(t);
+        line = t.getLine();
+        col = t.getColumn();
+    }
+
+    public void setLast(Token last) {
+        lineLast = last.getLine();
+        colLast = last.getColumn();
+    }
+
+    public int getLineLast() {
+        return lineLast;
+    }
+
+    public void setLineLast(int lineLast) {
+        this.lineLast = lineLast;
+    }
+
+    public int getColumnLast() {
+        return colLast;
+    }
+
+    public void setColumnLast(int colLast) {
+        this.colLast = colLast;
+    }
+
+    public void setLine(int line) {
+        this.line = line;
+    }
+
+    public int getLine() {
+        return (line);
+    }
+
+    public void setColumn(int column) {
+        this.col = column;
+    }
+
+    public int getColumn() {
+        return (col);
+    }
+
+    public void setSnippet(String snippet) {
+        this.snippet = snippet;
+    }
+
+    public String getSnippet() {
+        return snippet;
+    }
+
+    public int compareTo(Object object) {
+        if (object == null) {
+            return 0;
+        }
+        if (!(object instanceof AST)) {
+            return 0;
+        }
+        AST that = (AST) object;
+
+        // todo - possibly check for line/col with values of 0 or less...
+
+        if (this.getLine() < that.getLine()) {
+            return -1;
+        }
+        if (this.getLine() > that.getLine()) {
+            return 1;
+        }
+
+        if (this.getColumn() < that.getColumn()) {
+            return -1;
+        }
+        if (this.getColumn() > that.getColumn()) {
+            return 1;
+        }
+
+        return 0;
+    }
+
+    public GroovySourceAST childAt(int position) {
+        List list = new ArrayList();
+        AST child = this.getFirstChild();
+        while (child != null) {
+            list.add(child);
+            child = child.getNextSibling();
+        }
+        try {
+            return (GroovySourceAST)list.get(position);
+        } catch (IndexOutOfBoundsException e) {
+            return null;
+        }
+    }
+
+    public GroovySourceAST childOfType(int type) {
+        AST child = this.getFirstChild();
+        while (child != null) {
+            if (child.getType() == type) { return (GroovySourceAST)child; }
+            child = child.getNextSibling();
+        }
+        return null;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/LexerFrame.java b/groovy/src/main/org/codehaus/groovy/antlr/LexerFrame.java
new file mode 100644
index 0000000..cc1f59a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/LexerFrame.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.antlr;
+import java.awt.*;
+import java.awt.event.*;
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.Hashtable;
+
+import javax.swing.*;
+import javax.swing.border.Border;
+import javax.swing.text.BadLocationException;
+import org.codehaus.groovy.antlr.parser.*;
+
+import antlr.*;
+
+/**
+ * @author Santhosh Kumar T
+ * @version 1.0
+ */
+
+public class LexerFrame extends JFrame implements ActionListener{
+    JSplitPane jSplitPane1 = new JSplitPane();
+    JScrollPane jScrollPane1 = new JScrollPane();
+    JScrollPane jScrollPane2 = new JScrollPane();
+    JTextPane tokenPane = new HScrollableTextPane();
+    JButton jbutton = new JButton("open");
+    JPanel mainPanel = new JPanel(new BorderLayout());
+    JTextArea scriptPane = new JTextArea();
+    Border border1;
+    Border border2;
+
+    Class lexerClass;
+
+    public LexerFrame(Class lexerClass, Class tokenTypesClass){
+        super("Token Steam Viewer");
+        this.lexerClass = lexerClass;
+        try{
+            jbInit();
+            setSize(500, 500);
+            listTokens(tokenTypesClass);
+
+            final JPopupMenu popup = new JPopupMenu();
+            popup.add(loadFileAction);
+
+            jbutton.setSize(30,30);
+            jbutton.addMouseListener(new MouseAdapter(){
+                public void mouseReleased(MouseEvent e) {
+                    //if(e.isPopupTrigger())
+                        popup.show(scriptPane, e.getX(), e.getY());
+                }
+            });
+            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+        } catch(Exception e){
+            e.printStackTrace();
+        }
+    }
+
+    Hashtable tokens = new Hashtable();
+
+    private void listTokens(Class tokenTypes) throws Exception{
+        Field field[] = tokenTypes.getDeclaredFields();
+        for(int i = 0; i<field.length; i++)
+            tokens.put(field[i].get(null), field[i].getName());
+    }
+
+    public void actionPerformed(ActionEvent ae){
+        Token token = (Token) ((JComponent) ae.getSource()).getClientProperty("token");
+        if(token.getType()==Token.EOF_TYPE){
+            scriptPane.select(0, 0);
+            return;
+        }
+        try{
+            int start = scriptPane.getLineStartOffset(token.getLine()-1)+token.getColumn()-1;
+            scriptPane.select(start, start+token.getText().length());
+            scriptPane.requestFocus();
+        } catch(BadLocationException ex){
+            // IGNORE
+        }
+    }
+
+    private Action loadFileAction = new AbstractAction("Open File..."){
+        public void actionPerformed(ActionEvent ae){
+            JFileChooser jfc = new JFileChooser();
+            int response = jfc.showOpenDialog(LexerFrame.this);
+            if(response!=JFileChooser.APPROVE_OPTION)
+                return;
+            try{
+                scanScript(jfc.getSelectedFile());
+            } catch(Exception ex){
+                ex.printStackTrace();
+            }
+        }
+    };
+
+    private void scanScript(File file) throws Exception{
+        scriptPane.read(new FileReader(file), null);
+
+        // create lexer
+        Constructor constructor = lexerClass.getConstructor(new Class[]{InputStream.class});
+        CharScanner lexer = (CharScanner) constructor.newInstance(new Object[]{new FileInputStream(file)});
+
+        tokenPane.setEditable(true);
+        tokenPane.setText("");
+
+        int line = 1;
+        ButtonGroup bg = new ButtonGroup();
+        Token token = null;
+
+        while(true){
+            token = lexer.nextToken();
+            JToggleButton tokenButton = new JToggleButton((String) tokens.get(new Integer(token.getType())));
+            bg.add(tokenButton);
+            tokenButton.addActionListener(this);
+            tokenButton.setToolTipText(token.getText());
+            tokenButton.putClientProperty("token", token);
+            tokenButton.setMargin(new Insets(0, 1, 0, 1));
+            tokenButton.setFocusPainted(false);
+            if(token.getLine()>line){
+                tokenPane.getDocument().insertString(tokenPane.getDocument().getLength(), "\n", null);
+                line = token.getLine();
+            }
+            insertComponent(tokenButton);
+            if(token.getType()==Token.EOF_TYPE)
+                break;
+        }
+
+        tokenPane.setEditable(false);
+        tokenPane.setCaretPosition(0);
+    }
+
+    private void insertComponent(JComponent comp){
+        try{
+            tokenPane.getDocument().insertString(tokenPane.getDocument().getLength(), " ", null);
+        } catch(BadLocationException ex1){
+            // Ignore
+        }
+        try{
+            tokenPane.setCaretPosition(tokenPane.getDocument().getLength()-1);
+        } catch(Exception ex){
+            tokenPane.setCaretPosition(0);
+        }
+        tokenPane.insertComponent(comp);
+    }
+
+    private void jbInit() throws Exception{
+        border1 = BorderFactory.createEmptyBorder();
+        border2 = BorderFactory.createEmptyBorder();
+        jSplitPane1.setOrientation(JSplitPane.VERTICAL_SPLIT);
+        tokenPane.setEditable(false);
+        tokenPane.setText("");
+        scriptPane.setFont(new java.awt.Font("DialogInput", 0, 12));
+        scriptPane.setEditable(false);
+        scriptPane.setMargin(new Insets(5, 5, 5, 5));
+        scriptPane.setText("");
+        jScrollPane1.setBorder(border1);
+        jScrollPane2.setBorder(border1);
+        jSplitPane1.setMinimumSize(new Dimension(800,600));
+        mainPanel.add(jSplitPane1, BorderLayout.CENTER);
+        mainPanel.add(jbutton,BorderLayout.NORTH);
+        this.getContentPane().add(mainPanel);
+        jSplitPane1.add(jScrollPane1, JSplitPane.LEFT);
+        jScrollPane1.getViewport().add(tokenPane, null);
+        jSplitPane1.add(jScrollPane2, JSplitPane.RIGHT);
+        jScrollPane2.getViewport().add(scriptPane, null);
+
+        jScrollPane1.setColumnHeaderView(new JLabel(" Token Stream:"));
+        jScrollPane2.setColumnHeaderView(new JLabel(" Input Script:"));
+        jSplitPane1.setResizeWeight(0.5);
+    }
+
+    public static void main(String[] args) throws Exception{
+        try{
+            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+        } catch(Exception ignore){
+            // Ignore
+        }
+        new LexerFrame(GroovyLexer.class, GroovyTokenTypes.class).setVisible(true);
+    }
+}
+
+
+class HScrollableTextPane extends JTextPane{
+    public boolean getScrollableTracksViewportWidth(){
+        return(getSize().width<getParent().getSize().width);
+    }
+
+    public void setSize(Dimension d){
+        if(d.width<getParent().getSize().width){
+            d.width = getParent().getSize().width;
+        }
+        super.setSize(d);
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/LineColumn.java b/groovy/src/main/org/codehaus/groovy/antlr/LineColumn.java
new file mode 100644
index 0000000..9490720
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/LineColumn.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.antlr;
+
+/**
+ * An object representing a line and column position
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+public class LineColumn {
+    private int line;
+    private int column;
+
+    public LineColumn(int line, int column) {
+        this.line = line;
+        this.column = column;
+    }
+
+    public int getLine() {
+        return line;
+    }
+
+    public int getColumn() {
+        return column;
+    }
+
+    public boolean equals(Object that) {
+        if (this == that) return true;
+        if (that == null || getClass() != that.getClass()) return false;
+
+        final LineColumn lineColumn = (LineColumn) that;
+
+        if (column != lineColumn.column) return false;
+        if (line != lineColumn.line) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result;
+        result = line;
+        result = 29 * result + column;
+        return result;
+    }
+
+    public String toString() {
+        return "[" + line + "," + column + "]";
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/Main.java b/groovy/src/main/org/codehaus/groovy/antlr/Main.java
new file mode 100644
index 0000000..d207789
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/Main.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.antlr;
+
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.io.FileReader;
+
+import org.codehaus.groovy.antlr.parser.GroovyLexer;
+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
+
+import antlr.ASTFactory;
+import antlr.CommonAST;
+import antlr.Token;
+import antlr.collections.AST;
+import antlr.debug.misc.ASTFrame;
+
+class Main {
+
+    static boolean whitespaceIncluded = false;
+
+	static boolean showTree = false;
+    //static boolean xml = false;
+	static boolean verbose = false;
+    public static void main(String[] args) {
+		// Use a try/catch block for parser exceptions
+		try {
+			// if we have at least one command-line argument
+			if (args.length > 0 ) {
+				System.err.println("Parsing...");
+
+				// for each directory/file specified on the command line
+				for(int i=0; i< args.length;i++) {
+					if ( args[i].equals("-showtree") ) {
+						showTree = true;
+					}
+                    //else if ( args[i].equals("-xml") ) {
+                    //    xml = true;
+                    //}
+					else if ( args[i].equals("-verbose") ) {
+						verbose = true;
+					}
+					else if ( args[i].equals("-trace") ) {
+						GroovyRecognizer.tracing = true;
+						GroovyLexer.tracing = true;
+					}
+					else if ( args[i].equals("-traceParser") ) {
+						GroovyRecognizer.tracing = true;
+					}
+					else if ( args[i].equals("-traceLexer") ) {
+						GroovyLexer.tracing = true;
+					}
+                                        else if ( args[i].equals("-whitespaceIncluded") ) {
+                                            whitespaceIncluded = true;
+                                        }
+                                        else {
+						doFile(new File(args[i])); // parse it
+					}
+				} }
+			else
+				System.err.println("Usage: java -jar groovyc.jar [-showtree] [-verbose] [-trace{,Lexer,Parser}]"+
+                                   "<directory or file name>");
+		}
+		catch(Exception e) {
+			System.err.println("exception: "+e);
+			e.printStackTrace(System.err);   // so we can get stack trace
+		}
+	}
+
+
+	// This method decides what action to take based on the type of
+	//   file we are looking at
+	public static void doFile(File f)
+							  throws Exception {
+		// If this is a directory, walk each file/dir in that directory
+		if (f.isDirectory()) {
+			String files[] = f.list();
+			for(int i=0; i < files.length; i++)
+				doFile(new File(f, files[i]));
+		}
+
+		// otherwise, if this is a groovy file, parse it!
+		else if (f.getName().endsWith(".groovy")) {
+			System.err.println(" --- "+f.getAbsolutePath());
+			// parseFile(f.getName(), new FileInputStream(f));
+            SourceBuffer sourceBuffer = new SourceBuffer();
+            UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(new FileReader(f),sourceBuffer);
+            GroovyLexer lexer = new GroovyLexer(unicodeReader);
+            unicodeReader.setLexer(lexer);
+			parseFile(f.getName(),lexer,sourceBuffer);
+		}
+	}
+
+	// Here's where we do the real work...
+	public static void parseFile(String f, GroovyLexer l, SourceBuffer sourceBuffer)
+								 throws Exception {
+		try {
+			// Create a parser that reads from the scanner
+			GroovyRecognizer parser = GroovyRecognizer.make(l);
+            parser.setSourceBuffer(sourceBuffer);
+			parser.setFilename(f);
+                        
+                        if (whitespaceIncluded) {
+                            GroovyLexer lexer = parser.getLexer();
+                            lexer.setWhitespaceIncluded(true);
+                            while (true) {
+                                Token t = lexer.nextToken();
+                                System.out.println(t);
+                                if (t == null || t.getType() == Token.EOF_TYPE)  break;
+                            }
+                            return;
+                        }
+
+			// start parsing at the compilationUnit rule
+			parser.compilationUnit();
+			
+			System.out.println("parseFile "+f+" => "+parser.getAST());
+
+			// do something with the tree
+			doTreeAction(f, parser.getAST(), parser.getTokenNames());
+		}
+		catch (Exception e) {
+			System.err.println("parser exception: "+e);
+			e.printStackTrace();   // so we can get stack trace		
+		}
+	}
+	
+	public static void doTreeAction(String f, AST t, String[] tokenNames) {
+		if ( t==null ) return;
+		if ( showTree ) {
+			CommonAST.setVerboseStringConversion(true, tokenNames);
+			ASTFactory factory = new ASTFactory();
+			AST r = factory.create(0,"AST ROOT");
+			r.setFirstChild(t);
+			final ASTFrame frame = new ASTFrame("Groovy AST", r);
+			frame.setVisible(true);
+			frame.addWindowListener(
+				new WindowAdapter() {
+                   public void windowClosing (WindowEvent e) {
+                       frame.setVisible(false); // hide the Frame
+                       frame.dispose();
+                       System.exit(0);
+                   }
+		        }
+			);
+			if (verbose)  System.out.println(t.toStringList());
+		}
+        /*if ( xml ) {
+			((CommonAST)t).setVerboseStringConversion(true, tokenNames);
+			ASTFactory factory = new ASTFactory();
+			AST r = factory.create(0,"AST ROOT");
+			r.setFirstChild(t);
+            XStream xstream = new XStream();
+            xstream.alias("ast", CommonAST.class);
+			try {
+                xstream.toXML(r,new FileWriter(f + ".xml"));
+                System.out.println("Written AST to " + f + ".xml");
+            } catch (Exception e) {
+                System.out.println("couldn't write to " + f + ".xml");
+                e.printStackTrace();
+            }
+			//if (verbose)  System.out.println(t.toStringList());
+		}*/
+	/*@todo
+		GroovyTreeParser tparse = new GroovyTreeParser();
+		try {
+			tparse.compilationUnit(t);
+			if (verbose)  System.out.println("successful walk of result AST for "+f);
+		}
+		catch (RecognitionException e) {
+			System.err.println(e.getMessage());
+			e.printStackTrace();
+		}
+	@todo*/
+
+	}
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/SourceBuffer.java b/groovy/src/main/org/codehaus/groovy/antlr/SourceBuffer.java
new file mode 100644
index 0000000..4281dbe
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/SourceBuffer.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * A simple buffer that provides line/col access to chunks of source code
+ * held within itself.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+public class SourceBuffer {
+    private final List lines;
+    private StringBuffer current;
+
+    public SourceBuffer() {
+        lines = new ArrayList();
+        //lines.add(new StringBuffer()); // dummy row for position [0] in the List
+
+        current = new StringBuffer();
+        lines.add(current);
+    }
+
+    /**
+     * Obtains a snippet of the source code within the bounds specified
+     * @param start (inclusive line/ inclusive column)
+     * @param end (inclusive line / exclusive column)
+     * @return specified snippet of source code as a String, or null if no source available
+     */
+    public String getSnippet(LineColumn start, LineColumn end) {
+        // preconditions
+        if (start == null || end == null) { return null; } // no text to return
+        if (start.equals(end)) { return null; } // no text to return
+        if (lines.size() == 1 && current.length() == 0) { return null; } // buffer hasn't been filled yet
+
+        // working variables
+        int startLine = start.getLine();
+        int startColumn = start.getColumn();
+        int endLine = end.getLine();
+        int endColumn = end.getColumn();
+
+        // reset any out of bounds requests
+        if (startLine < 1) { startLine = 1;}
+        if (endLine < 1) { endLine = 1;}
+        if (startColumn < 1) { startColumn = 1;}
+        if (endColumn < 1) { endColumn = 1;}
+        if (startLine > lines.size()) { startLine = lines.size(); }
+        if (endLine > lines.size()) { endLine = lines.size(); }
+
+        // obtain the snippet from the buffer within specified bounds
+        StringBuffer snippet = new StringBuffer();
+        for (int i = startLine - 1; i < endLine;i++) {
+            String line = ((StringBuffer)lines.get(i)).toString();
+            if (startLine == endLine) {
+                // reset any out of bounds requests (again)
+                if (startColumn > line.length()) { startColumn = line.length();}
+                if (startColumn < 1) { startColumn = 1;}
+                if (endColumn > line.length()) { endColumn = line.length() + 1;}
+                if (endColumn < 1) { endColumn = 1;}
+
+                line = line.substring(startColumn - 1, endColumn - 1);
+            } else {
+                if (i == startLine - 1) {
+                    if (startColumn - 1 < line.length()) {
+                        line = line.substring(startColumn - 1);
+                    }
+                }
+                if (i == endLine - 1) {
+                    if (endColumn - 1 < line.length()) {
+                        line = line.substring(0,endColumn - 1);
+                    }
+                }
+            }
+            snippet.append(line);
+        }
+        return snippet.toString();
+    }
+
+    /**
+     * Writes the specified character into the buffer
+     * @param c
+     */
+    public void write(int c) {
+        if (c != -1) {
+            current.append((char)c);
+        }
+        if (c == '\n') {
+            current = new StringBuffer();
+            lines.add(current);
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/UnicodeEscapingReader.java b/groovy/src/main/org/codehaus/groovy/antlr/UnicodeEscapingReader.java
new file mode 100644
index 0000000..3a2c4cf
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/UnicodeEscapingReader.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.codehaus.groovy.antlr;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import antlr.CharScanner;
+
+/**
+ * Translates GLS-defined unicode escapes into characters. Throws an exception
+ * in the event of an invalid unicode escape being detected.
+ *
+ * <p>No attempt has been made to optimise this class for speed or
+ * space.</p>
+ *
+ * @version $Revision$
+ */
+public class UnicodeEscapingReader extends Reader {
+
+    private final Reader reader;
+    private CharScanner lexer;
+    private boolean hasNextChar = false;
+    private int nextChar;
+    private final SourceBuffer sourceBuffer;
+
+    /**
+     * Constructor.
+     * @param reader The reader that this reader will filter over.
+     */
+    public UnicodeEscapingReader(Reader reader,SourceBuffer sourceBuffer) {
+        this.reader = reader;
+        this.sourceBuffer = sourceBuffer;
+    }
+
+    /**
+     * Sets the lexer that is using this reader. Must be called before the
+     * lexer is used.
+     */
+    public void setLexer(CharScanner lexer) {
+        this.lexer = lexer;
+    }
+
+    /**
+     * Reads characters from the underlying reader.
+     * @see java.io.Reader#read(char[],int,int)
+     */
+    public int read(char cbuf[], int off, int len) throws IOException {
+        int c = 0;
+        int count = 0;
+        while (count < len && (c = read())!= -1) {
+            cbuf[off + count] = (char) c;
+            count++;
+        }
+        return (count == 0 && c == -1) ? -1 : count;
+    }
+
+    /**
+     * Gets the next character from the underlying reader,
+     * translating escapes as required.
+     * @see java.io.Reader#close()
+     */
+    public int read() throws IOException {
+        if (hasNextChar) {
+            hasNextChar = false;
+            write(nextChar);
+            return nextChar;
+        }
+
+        int c = reader.read();
+        if (c != '\\') {
+            write(c);
+            return c;
+        }
+
+        // Have one backslash, continue if next char is 'u'
+        c = reader.read();
+        if (c != 'u') {
+            hasNextChar = true;
+            nextChar = c;
+            write('\\');
+            return '\\';
+        }
+
+        // Swallow multiple 'u's
+        do {
+            c = reader.read();
+        } while (c == 'u');
+
+        // Get first hex digit
+        checkHexDigit(c);
+        StringBuffer charNum = new StringBuffer();
+        charNum.append((char) c);
+
+        // Must now be three more hex digits
+        for (int i = 0; i < 3; i++) {
+            c = reader.read();
+            checkHexDigit(c);
+            charNum.append((char) c);
+        }
+        int rv = Integer.parseInt(charNum.toString(), 16);
+        write(rv);
+        return rv;
+    }
+    private void write(int c) {
+        if (sourceBuffer != null) {sourceBuffer.write(c);}
+    }
+    /**
+     * Checks that the given character is indeed a hex digit.
+     */
+    private void checkHexDigit(int c) throws IOException {
+        if (c >= '0' && c <= '9') {
+            return;
+        }
+        if (c >= 'a' && c <= 'f') {
+            return;
+        }
+        if (c >= 'A' && c <= 'F') {
+            return;
+        }
+        // Causes the invalid escape to be skipped
+        hasNextChar = true;
+        nextChar = c;
+        throw new IOException("Did not find four digit hex character code."
+                + " line: " + lexer.getLine() + " col:" + lexer.getColumn());
+    }
+
+    /**
+     * Closes this reader by calling close on the underlying reader.
+     * @see java.io.Reader#close()
+     */
+    public void close() throws IOException {
+        reader.close();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/groovy.g b/groovy/src/main/org/codehaus/groovy/antlr/groovy.g
new file mode 100644
index 0000000..41937db
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/groovy.g
@@ -0,0 +1,3763 @@
+// Note: Please don't use physical tabs.  Logical tabs for indent are width 4.
+header {
+package org.codehaus.groovy.antlr.parser;
+import org.codehaus.groovy.antlr.*;
+import java.util.*;
+import java.io.InputStream;
+import java.io.Reader;
+import antlr.InputBuffer;
+import antlr.LexerSharedInputState;
+}
+ 
+/** JSR-241 Groovy Recognizer
+ *
+ * Run 'java Main [-showtree] directory-full-of-groovy-files'
+ *
+ * [The -showtree option pops up a Swing frame that shows
+ *  the AST constructed from the parser.]
+ *
+ * Contributing authors:
+ *              John Mitchell           johnm@non.net
+ *              Terence Parr            parrt@magelang.com
+ *              John Lilley             jlilley@empathy.com
+ *              Scott Stanchfield       thetick@magelang.com
+ *              Markus Mohnen           mohnen@informatik.rwth-aachen.de
+ *              Peter Williams          pete.williams@sun.com
+ *              Allan Jacobs            Allan.Jacobs@eng.sun.com
+ *              Steve Messick           messick@redhills.com
+ *              James Strachan          jstrachan@protique.com
+ *              John Pybus              john@pybus.org
+ *              John Rose               rose00@mac.com
+ *              Jeremy Rayner           groovy@ross-rayner.com
+ *              Alex Popescu            the.mindstorm@gmail.com
+ *
+ * Version 1.00 December 9, 1997 -- initial release
+ * Version 1.01 December 10, 1997
+ *              fixed bug in octal def (0..7 not 0..8)
+ * Version 1.10 August 1998 (parrt)
+ *              added tree construction
+ *              fixed definition of WS,comments for mac,pc,unix newlines
+ *              added unary plus
+ * Version 1.11 (Nov 20, 1998)
+ *              Added "shutup" option to turn off last ambig warning.
+ *              Fixed inner class def to allow named class defs as statements
+ *              synchronized requires compound not simple statement
+ *              add [] after builtInType DOT class in primaryExpression
+ *              "const" is reserved but not valid..removed from modifiers
+ * Version 1.12 (Feb 2, 1999)
+ *              Changed LITERAL_xxx to xxx in tree grammar.
+ *              Updated java.g to use tokens {...} now for 2.6.0 (new feature).
+ *
+ * Version 1.13 (Apr 23, 1999)
+ *              Didn't have (stat)? for else clause in tree parser.
+ *              Didn't gen ASTs for interface extends.  Updated tree parser too.
+ *              Updated to 2.6.0.
+ * Version 1.14 (Jun 20, 1999)
+ *              Allowed final/abstract on local classes.
+ *              Removed local interfaces from methods
+ *              Put instanceof precedence where it belongs...in relationalExpr
+ *                      It also had expr not type as arg; fixed it.
+ *              Missing ! on SEMI in classBlock
+ *              fixed: (expr) + "string" was parsed incorrectly (+ as unary plus).
+ *              fixed: didn't like Object[].class in parser or tree parser
+ * Version 1.15 (Jun 26, 1999)
+ *              Screwed up rule with instanceof in it. :(  Fixed.
+ *              Tree parser didn't like (expr).something; fixed.
+ *              Allowed multiple inheritance in tree grammar. oops.
+ * Version 1.16 (August 22, 1999)
+ *              Extending an interface built a wacky tree: had extra EXTENDS.
+ *              Tree grammar didn't allow multiple superinterfaces.
+ *              Tree grammar didn't allow empty var initializer: {}
+ * Version 1.17 (October 12, 1999)
+ *              ESC lexer rule allowed 399 max not 377 max.
+ *              java.tree.g didn't handle the expression of synchronized
+ *              statements.
+ * Version 1.18 (August 12, 2001)
+ *              Terence updated to Java 2 Version 1.3 by
+ *              observing/combining work of Allan Jacobs and Steve
+ *              Messick.  Handles 1.3 src.  Summary:
+ *              o  primary didn't include boolean.class kind of thing
+ *              o  constructor calls parsed explicitly now:
+ *                 see explicitConstructorInvocation
+ *              o  add strictfp modifier
+ *              o  missing objBlock after new expression in tree grammar
+ *              o  merged local class definition alternatives, moved after declaration
+ *              o  fixed problem with ClassName.super.field
+ *              o  reordered some alternatives to make things more efficient
+ *              o  long and double constants were not differentiated from int/float
+ *              o  whitespace rule was inefficient: matched only one char
+ *              o  add an examples directory with some nasty 1.3 cases
+ *              o  made Main.java use buffered IO and a Reader for Unicode support
+ *              o  supports UNICODE?
+ *                 Using Unicode charVocabulay makes code file big, but only
+ *                 in the bitsets at the end. I need to make ANTLR generate
+ *                 unicode bitsets more efficiently.
+ * Version 1.19 (April 25, 2002)
+ *              Terence added in nice fixes by John Pybus concerning floating
+ *              constants and problems with super() calls.  John did a nice
+ *              reorg of the primary/postfix expression stuff to read better
+ *              and makes f.g.super() parse properly (it was METHOD_CALL not
+ *              a SUPER_CTOR_CALL).  Also:
+ *
+ *              o  "finally" clause was a root...made it a child of "try"
+ *              o  Added stuff for asserts too for Java 1.4, but *commented out*
+ *                 as it is not backward compatible.
+ *
+ * Version 1.20 (October 27, 2002)
+ *
+ *        Terence ended up reorging John Pybus' stuff to
+ *        remove some nondeterminisms and some syntactic predicates.
+ *        Note that the grammar is stricter now; e.g., this(...) must
+ *      be the first statement.
+ *
+ *        Trinary ?: operator wasn't working as array name:
+ *                (isBig ? bigDigits : digits)[i];
+ *
+ *        Checked parser/tree parser on source for
+ *                Resin-2.0.5, jive-2.1.1, jdk 1.3.1, Lucene, antlr 2.7.2a4,
+ *              and the 110k-line jGuru server source.
+ *
+ * Version 1.21 (October 17, 2003)
+ *  Fixed lots of problems including:
+ *  Ray Waldin: add typeDefinition to interfaceBlock in java.tree.g
+ *  He found a problem/fix with floating point that start with 0
+ *  Ray also fixed problem that (int.class) was not recognized.
+ *  Thorsten van Ellen noticed that \n are allowed incorrectly in strings.
+ *  TJP fixed CHAR_LITERAL analogously.
+ *
+ * Version 1.21.2 (March, 2003)
+ *        Changes by Matt Quail to support generics (as per JDK1.5/JSR14)
+ *        Notes:
+ *        o We only allow the "extends" keyword and not the "implements"
+ *              keyword, since thats what JSR14 seems to imply.
+ *        o Thanks to Monty Zukowski for his help on the antlr-interest
+ *              mail list.
+ *        o Thanks to Alan Eliasen for testing the grammar over his
+ *              Fink source base
+ *
+ * Version 1.22 (July, 2004)
+ *        Changes by Michael Studman to support Java 1.5 language extensions
+ *        Notes:
+ *        o Added support for annotations types
+ *        o Finished off Matt Quail's generics enhancements to support bound type arguments
+ *        o Added support for new for statement syntax
+ *        o Added support for static import syntax
+ *        o Added support for enum types
+ *        o Tested against JDK 1.5 source base and source base of jdigraph project
+ *        o Thanks to Matt Quail for doing the hard part by doing most of the generics work
+ *
+ * Version 1.22.1 (July 28, 2004)
+ *        Bug/omission fixes for Java 1.5 language support
+ *        o Fixed tree structure bug with classOrInterface - thanks to Pieter Vangorpto for
+ *              spotting this
+ *        o Fixed bug where incorrect handling of SR and BSR tokens would cause type
+ *              parameters to be recognised as type arguments.
+ *        o Enabled type parameters on constructors, annotations on enum constants
+ *              and package definitions
+ *        o Fixed problems when parsing if ((char.class.equals(c))) {} - solution by Matt Quail at Cenqua
+ *
+ * Version 1.22.2 (July 28, 2004)
+ *        Slight refactoring of Java 1.5 language support
+ *        o Refactored for/"foreach" productions so that original literal "for" literal
+ *          is still used but the for sub-clauses vary by token type
+ *        o Fixed bug where type parameter was not included in generic constructor's branch of AST
+ *
+ * Version 1.22.3 (August 26, 2004)
+ *        Bug fixes as identified by Michael Stahl; clean up of tabs/spaces
+ *        and other refactorings
+ *        o Fixed typeParameters omission in identPrimary and newStatement
+ *        o Replaced GT reconcilliation code with simple semantic predicate
+ *        o Adapted enum/assert keyword checking support from Michael Stahl's java15 grammar
+ *        o Refactored typeDefinition production and field productions to reduce duplication
+ *
+ * Version 1.22.4 (October 21, 2004)
+ *    Small bux fixes
+ *    o Added typeArguments to explicitConstructorInvocation, e.g. new <String>MyParameterised()
+ *    o Added typeArguments to postfixExpression productions for anonymous inner class super
+ *      constructor invocation, e.g. new Outer().<String>super()
+ *    o Fixed bug in array declarations identified by Geoff Roy
+ *
+ * Version 1.22.4.g.1
+ *    o I have taken java.g for Java1.5 from Michael Studman (1.22.4)
+ *      and have applied the groovy.diff from java.g (1.22) by John Rose
+ *      back onto the new root (1.22.4) - Jeremy Rayner (Jan 2005)
+ *    o for a map of the task see... 
+ *      http://groovy.javanicus.com/java-g.png
+ *
+ * This grammar is in the PUBLIC DOMAIN
+ */
+
+class GroovyRecognizer extends Parser;
+options {
+    k = 2;                            // two token lookahead
+    exportVocab=Groovy;               // Call its vocabulary "Groovy"
+    codeGenMakeSwitchThreshold = 2;   // Some optimizations
+    codeGenBitsetTestThreshold = 3;
+    defaultErrorHandler = false;      // Don't generate parser error handlers
+    buildAST = true;
+//  ASTLabelType = "GroovyAST";
+}
+
+tokens {
+    BLOCK; MODIFIERS; OBJBLOCK; SLIST; METHOD_DEF; VARIABLE_DEF;
+    INSTANCE_INIT; STATIC_INIT; TYPE; CLASS_DEF; INTERFACE_DEF;
+    PACKAGE_DEF; ARRAY_DECLARATOR; EXTENDS_CLAUSE; IMPLEMENTS_CLAUSE;
+    PARAMETERS; PARAMETER_DEF; LABELED_STAT; TYPECAST; INDEX_OP;
+    POST_INC; POST_DEC; METHOD_CALL; EXPR;
+    IMPORT; UNARY_MINUS; UNARY_PLUS; CASE_GROUP; ELIST; FOR_INIT; FOR_CONDITION;
+    FOR_ITERATOR; EMPTY_STAT; FINAL="final"; ABSTRACT="abstract";
+    UNUSED_GOTO="goto"; UNUSED_CONST="const"; UNUSED_DO="do";
+    STRICTFP="strictfp"; SUPER_CTOR_CALL; CTOR_CALL; CTOR_IDENT; VARIABLE_PARAMETER_DEF;
+    STRING_CONSTRUCTOR; STRING_CTOR_MIDDLE;
+    CLOSABLE_BLOCK; IMPLICIT_PARAMETERS;
+    SELECT_SLOT; DYNAMIC_MEMBER;
+    LABELED_ARG; SPREAD_ARG; SPREAD_MAP_ARG; //deprecated - SCOPE_ESCAPE;
+    LIST_CONSTRUCTOR; MAP_CONSTRUCTOR;
+    FOR_IN_ITERABLE;
+    STATIC_IMPORT; ENUM_DEF; ENUM_CONSTANT_DEF; FOR_EACH_CLAUSE; ANNOTATION_DEF; ANNOTATIONS;
+    ANNOTATION; ANNOTATION_MEMBER_VALUE_PAIR; ANNOTATION_FIELD_DEF; ANNOTATION_ARRAY_INIT;
+    TYPE_ARGUMENTS; TYPE_ARGUMENT; TYPE_PARAMETERS; TYPE_PARAMETER; WILDCARD_TYPE;
+    TYPE_UPPER_BOUNDS; TYPE_LOWER_BOUNDS; CLOSURE_LIST;
+}
+
+{
+        /** This factory is the correct way to wire together a Groovy parser and lexer. */
+    public static GroovyRecognizer make(GroovyLexer lexer) {
+        GroovyRecognizer parser = new GroovyRecognizer(lexer.plumb());
+        // TODO: set up a common error-handling control block, to avoid excessive tangle between these guys
+        parser.lexer = lexer;
+        lexer.parser = parser;
+        parser.getASTFactory().setASTNodeClass(GroovySourceAST.class);
+        parser.warningList = new ArrayList();
+        return parser;
+    }
+    // Create a scanner that reads from the input stream passed to us...
+    public static GroovyRecognizer make(InputStream in) { return make(new GroovyLexer(in)); }
+    public static GroovyRecognizer make(Reader in) { return make(new GroovyLexer(in)); }
+    public static GroovyRecognizer make(InputBuffer in) { return make(new GroovyLexer(in)); }
+    public static GroovyRecognizer make(LexerSharedInputState in) { return make(new GroovyLexer(in)); }
+    
+    private static GroovySourceAST dummyVariableToforceClassLoaderToFindASTClass = new GroovySourceAST();
+
+    List warningList;
+    public List getWarningList() { return warningList; }
+    
+    GroovyLexer lexer;
+    public GroovyLexer getLexer() { return lexer; }
+    public void setFilename(String f) { super.setFilename(f); lexer.setFilename(f); }
+    private SourceBuffer sourceBuffer;
+    public void setSourceBuffer(SourceBuffer sourceBuffer) {
+        this.sourceBuffer = sourceBuffer;
+    }
+
+    /** Create an AST node with the token type and text passed in, but
+     *  with the same background information as another supplied Token (e.g. line numbers)
+     * to be used in place of antlr tree construction syntax,
+     * i.e. #[TOKEN,"text"]  becomes  create(TOKEN,"text",anotherToken)
+     *
+     * todo - change antlr.ASTFactory to do this instead...
+     */
+    public AST create(int type, String txt, Token first, Token last) {
+        AST t = astFactory.create(type,txt);
+        if ( t != null && first != null) {
+            // first copy details from first token
+            t.initialize(first);
+            // then ensure that type and txt are specific to this new node
+            t.initialize(type,txt);
+        }
+
+        if ((t instanceof GroovySourceAST) && last != null) {
+            GroovySourceAST node = (GroovySourceAST)t;
+            node.setLast(last);
+            // This is a good point to call node.setSnippet(),
+            // but it bulks up the AST too much for production code.
+        }
+        return t;
+    }
+
+
+    // stuff to adjust ANTLR's tracing machinery
+    public static boolean tracing = false;  // only effective if antlr.Tool is run with -traceParser
+    public void traceIn(String rname) throws TokenStreamException {
+        if (!GroovyRecognizer.tracing)  return;
+        super.traceIn(rname);
+    }
+    public void traceOut(String rname) throws TokenStreamException {
+        if (!GroovyRecognizer.tracing)  return;
+        if (returnAST != null)  rname += returnAST.toStringList();
+        super.traceOut(rname);
+    }
+        
+    // Error handling.  This is a funnel through which parser errors go, when the parser can suggest a solution.
+    public void requireFailed(String problem, String solution) throws SemanticException {
+        // TODO: Needs more work.
+        Token lt = null;
+        try { lt = LT(1); }
+        catch (TokenStreamException ee) { }
+        if (lt == null)  lt = Token.badToken;
+        throw new SemanticException(problem + ";\n   solution: " + solution,
+                                    getFilename(), lt.getLine(), lt.getColumn());
+    }
+
+    public void addWarning(String warning, String solution) {
+        Token lt = null;
+        try { lt = LT(1); }
+        catch (TokenStreamException ee) { }
+        if (lt == null)  lt = Token.badToken;
+
+        Map row = new HashMap();
+        row.put("warning" ,warning);
+        row.put("solution",solution);
+        row.put("filename",getFilename());
+        row.put("line"    ,new Integer(lt.getLine()));
+        row.put("column"  ,new Integer(lt.getColumn()));
+        // System.out.println(row);
+        warningList.add(row);
+    }
+
+    // Convenience method for checking of expected error syndromes.
+    private void require(boolean z, String problem, String solution) throws SemanticException {
+        if (!z)  requireFailed(problem, solution);
+    }
+
+
+    // Query a name token to see if it begins with a capital letter.
+    // This is used to tell the difference (w/o symbol table access) between {String x} and {println x}.
+    private boolean isUpperCase(Token x) {
+        if (x == null || x.getType() != IDENT)  return false;  // cannot happen?
+        String xtext = x.getText();
+        return (xtext.length() > 0 && Character.isUpperCase(xtext.charAt(0)));
+    }
+
+    private AST currentClass = null;  // current enclosing class (for constructor recognition)
+    // Query a name token to see if it is identical with the current class name.
+    // This is used to distinguish constructors from other methods.
+    private boolean isConstructorIdent(Token x) {
+        if (currentClass == null)  return false;
+        if (currentClass.getType() != IDENT)  return false;  // cannot happen?
+        String cname = currentClass.getText();
+
+        if (x == null || x.getType() != IDENT)  return false;  // cannot happen?
+        return cname.equals(x.getText());
+    }
+
+    // Scratch variable for last 'sep' token.
+    // Written by the 'sep' rule, read only by immediate callers of 'sep'.
+    // (Not entirely clean, but better than a million xx=sep occurrences.)
+    private int sepToken = EOF;
+
+    // Scratch variable for last argument list; tells whether there was a label.
+    // Written by 'argList' rule, read only by immediate callers of 'argList'.
+    private boolean argListHasLabels = false;
+
+    // Scratch variable, holds most recently completed pathExpression.
+    // Read only by immediate callers of 'pathExpression' and 'expression'.
+    private AST lastPathExpression = null;
+
+    // Inherited attribute pushed into most expression rules.
+    // If not zero, it means that the left context of the expression
+    // being parsed is a statement boundary or an initializer sign '='.
+    // Only such expressions are allowed to reach across newlines
+    // to pull in an LCURLY and appended block.
+    private final int LC_STMT = 1, LC_INIT = 2;
+
+    /**
+     * Counts the number of LT seen in the typeArguments production.
+     * It is used in semantic predicates to ensure we have seen
+     * enough closing '>' characters; which actually may have been
+     * either GT, SR or BSR tokens.
+     */
+    private int ltCounter = 0;
+    
+    /* This symbol is used to work around a known ANTLR limitation.
+     * In a loop with syntactic predicate, ANTLR needs help knowing
+     * that the loop exit is a second alternative.
+     * Example usage:  ( (LCURLY)=> block | {ANTLR_LOOP_EXIT}? )*
+     * Probably should be an ANTLR RFE.
+     */
+    ////// Original comment in Java grammar:
+    // Unfortunately a syntactic predicate can only select one of
+    // multiple alternatives on the same level, not break out of
+    // an enclosing loop, which is why this ugly hack (a fake
+    // empty alternative with always-false semantic predicate)
+    // is necessary.
+    private static final boolean ANTLR_LOOP_EXIT = false;
+}
+
+// Compilation Unit: In Groovy, this is a single file or script. This is the start
+// rule for this parser
+compilationUnit
+    :
+        // The very first characters of the file may be "#!".  If so, ignore the first line.
+        (SH_COMMENT!)?
+
+        // we can have comments at the top of a file
+        nls!
+
+        // A compilation unit starts with an optional package definition
+        (   (annotationsOpt "package")=> packageDefinition
+        |   (statement[EOF])?
+        )
+
+        // The main part of the script is a sequence of any number of statements.
+        // Semicolons and/or significant newlines serve as separators.
+        ( sep! (statement[sepToken])? )*
+        EOF!
+    ;
+
+/** A Groovy script or simple expression.  Can be anything legal inside {...}. */
+snippetUnit
+    :   nls! blockBody[EOF]
+    ;
+
+
+// Package statement: optional annotations followed by "package" then the package identifier.
+packageDefinition
+        //TODO? options {defaultErrorHandler = true;} // let ANTLR handle errors
+    :   annotationsOpt p:"package"^ {#p.setType(PACKAGE_DEF);} identifier
+    ;
+
+
+// Import statement: import followed by a package or class name
+importStatement
+        //TODO? options {defaultErrorHandler = true;}
+        { boolean isStatic = false; }
+    :   i:"import"^ {#i.setType(IMPORT);} ( "static"! {#i.setType(STATIC_IMPORT);} )? identifierStar
+    ;
+
+// TODO REMOVE
+// A type definition is either a class, interface, enum or annotation with possible additional semis.
+//typeDefinition
+//      options {defaultErrorHandler = true;}
+//      :       m:modifiers!
+//              typeDefinitionInternal[#m]
+//      |       SEMI!
+//      ;
+
+// Added this production, even though 'typeDefinition' seems to be obsolete,
+// as this is referenced by many other parts of the grammar.
+// Protected type definitions production for reuse in other productions
+protected typeDefinitionInternal[AST mods]
+    :   cd:classDefinition[#mods]       // inner class
+        {#typeDefinitionInternal = #cd;}
+    |   id:interfaceDefinition[#mods]   // inner interface
+        {#typeDefinitionInternal = #id;}
+    |   ed:enumDefinition[#mods]        // inner enum
+        {#typeDefinitionInternal = #ed;}
+    |   ad:annotationDefinition[#mods]  // inner annotation
+        {#typeDefinitionInternal = #ad;}
+    ;
+
+/** A declaration is the creation of a reference or primitive-type variable,
+ *  or (if arguments are present) of a method.
+ *  Generically, this is called a 'variable' definition, even in the case of a class field or method.
+ *  It may start with the modifiers and/or a declaration keyword "def".
+ *  It may also start with the modifiers and a capitalized type name.
+ *  <p>
+ *  AST effect: Create a separate Type/Var tree for each var in the var list.
+ *  Must be guarded, as in (declarationStart) => declaration.
+ */
+declaration!
+    :
+        // method/variable using a 'def' or a modifier; type is optional
+        m:modifiers
+        (t:typeSpec[false])?
+        v:variableDefinitions[#m, #t]
+        {#declaration = #v;}
+    |
+        // method/variable using a type only
+        t2:typeSpec[false]
+        v2:variableDefinitions[null,#t2]
+        {#declaration = #v2;}
+    ;
+
+genericMethod! 
+	:
+        // method using a 'def' or a modifier; type is optional
+        m:modifiers
+        p:typeParameters
+        t:typeSpec[false]
+        v:variableDefinitions[#m, #t]
+        {
+        	#genericMethod = #v;
+			AST old = #v.getFirstChild();
+        	#genericMethod.setFirstChild(#p);
+        	#p.setNextSibling(old);
+        }
+	;
+
+
+// *TODO* We must also audit the various occurrences of warning
+// suppressions like "options { greedy = true; }".
+
+/** A declaration with one declarator and no initialization, like a parameterDeclaration.
+ *  Used to parse loops like <code>for (int x in y)</code> (up to the <code>in</code> keyword).
+ */
+singleDeclarationNoInit!
+    :
+        // method/variable using a 'def' or a modifier; type is optional
+        m:modifiers
+        (t:typeSpec[false])?
+        v:singleVariable[#m, #t]
+        {#singleDeclarationNoInit = #v;}
+    |
+        // method/variable using a type only
+        t2:typeSpec[false]
+        v2:singleVariable[null,#t2]
+        {#singleDeclarationNoInit = #v2;}
+    ;
+
+/** A declaration with one declarator and optional initialization, like a parameterDeclaration.
+ *  Used to parse declarations used for both binding and effect, in places like argument
+ *  lists and <code>while</code> statements.
+ */
+singleDeclaration
+    :   sd:singleDeclarationNoInit!
+        { #singleDeclaration = #sd; }
+        (varInitializer)?
+    ;
+
+/** Used only as a lookahead predicate, before diving in and parsing a declaration.
+ *  A declaration can be unambiguously introduced with "def", an annotation or a modifier token like "final".
+ *  It may also be introduced by a simple identifier whose first character is an uppercase letter,
+ *  as in {String x}.  A declaration can also be introduced with a built in type like 'int' or 'void'.
+ *  Brackets (array and generic) are allowed, as in {List[] x} or {int[][] y}.
+ *  Anything else is parsed as a statement of some sort (expression or command).
+ *  <p>
+ *  (In the absence of explicit method-call parens, we assume a capitalized name is a type name.
+ *  Yes, this is a little hacky.  Alternatives are to complicate the declaration or command
+ *  syntaxes, or to have the parser query the symbol table.  Parse-time queries are evil.
+ *  And we want both {String x} and {println x}.  So we need a syntactic razor-edge to slip
+ *  between 'println' and 'String'.)
+ *  
+ *   *TODO* The declarationStart production needs to be strengthened to recognize
+ *  things like {List<String> foo}.
+ *  Right now it only knows how to skip square brackets after the type, not
+ *  angle brackets.
+ *  This probably turns out to be tricky because of >> vs. > >. If so,
+ *  just put a TODO comment in.
+ */
+declarationStart!
+    :   (	  "def" nls
+    		| modifier nls 
+    		| annotation nls
+         	| (   upperCaseIdent
+           		|   builtInType
+           		|   qualifiedTypeName
+              ) (typeArguments)? (LBRACK balancedTokens RBRACK)*
+        )+
+        IDENT 
+    ;
+
+/**
+ * lookahead predicate for usage of generics in methods
+ * as parameter for the method. Example:
+ * static <T> T foo(){}
+ * <T> must be first after the modifier.
+ * This rule allows more and does no exact match, but it
+ * is only a lookahead, not the real rule. 
+ */
+genericMethodStart!
+    :   (	  "def" nls
+    		| modifier nls 
+    		| annotation nls
+    	)+ LT	
+    ;		
+
+qualifiedTypeName!
+	:
+		IDENT DOT (IDENT DOT)* upperCaseIdent
+	;
+	
+/** Used to look ahead for a constructor 
+ */
+constructorStart!
+    :
+        modifiersOpt! id:IDENT! {isConstructorIdent(id)}? nls! LPAREN! //...
+    ;
+
+
+/** Used only as a lookahead predicate for nested type declarations. */
+
+/*TODO* The lookahead in typeDeclarationStart needs to skip annotations, not
+just stop at '@', because variable and method declarations can also be
+annotated.
+> typeDeclarationStart!
+>     :   (modifier!)* ("class" | "interface" | "enum" | AT )
+S.B. something like
+>     :   (modifier! | annotationTokens!)* ("class" | "interface" |
+> "enum" )
+(And maybe @interface, if Java 5 allows nested annotation types? Don't
+know offhand.)
+Where annotationTokens can be a quick paren-skipper, as in other
+places: '@' ident '(' balancedTokens ')'.
+*/
+
+typeDeclarationStart!
+    :   modifiersOpt! ("class" | "interface" | "enum" | AT "interface")
+    ;
+    
+/** An IDENT token whose spelling is required to start with an uppercase letter.
+ *  In the case of a simple statement {UpperID name} the identifier is taken to be a type name, not a command name.
+ */
+upperCaseIdent
+    :   {isUpperCase(LT(1))}?
+        IDENT
+    ;
+
+// A type specification is a type name with possible brackets afterwards
+// (which would make it an array type).
+// Set addImagNode true for types inside expressions, not declarations.
+typeSpec[boolean addImagNode]
+    :    classTypeSpec[addImagNode]
+    |    builtInTypeSpec[addImagNode]
+    ;
+
+// also check that 'classOrInterfaceType[false]' is a suitable substitution for 'identifier'
+
+// A class type specification is a class type with either:
+// - possible brackets afterwards
+//   (which would make it an array type).
+// - generic type arguments after
+classTypeSpec[boolean addImagNode]  {Token first = LT(1);}
+    :   ct:classOrInterfaceType[false]!
+        declaratorBrackets[#ct]
+        {
+            if ( addImagNode ) {
+                #classTypeSpec = #(create(TYPE,"TYPE",first,LT(1)), #classTypeSpec);
+            }
+        }
+    ;
+
+// A non-built in type name, with possible type parameters
+classOrInterfaceType[boolean addImagNode]  {Token first = LT(1);}
+    :   IDENT^ (typeArguments)?
+        (   options{greedy=true;}: // match as many as possible
+            DOT^
+            IDENT (typeArguments)?
+        )*
+        {
+            if ( addImagNode ) {
+                #classOrInterfaceType = #(create(TYPE,"TYPE",first,LT(1)), #classOrInterfaceType);
+            }
+        }
+    ;
+
+// A specialised form of typeSpec where built in types must be arrays
+typeArgumentSpec
+    :   classTypeSpec[true]
+    |   builtInTypeArraySpec[true]
+    ;
+
+// A generic type argument is a class type, a possibly bounded wildcard type or a built-in type array
+typeArgument  {Token first = LT(1);}
+    :   (   typeArgumentSpec
+        |   wildcardType
+        )
+        {#typeArgument = #(create(TYPE_ARGUMENT,"TYPE_ARGUMENT",first,LT(1)), #typeArgument);}
+    ;
+
+// Wildcard type indicating all types (with possible constraint)
+wildcardType
+    :   QUESTION 
+        (("extends" | "super")=> typeArgumentBounds)?
+        {#wildcardType.setType(WILDCARD_TYPE);}
+    ;
+
+// Type arguments to a class or interface type
+typeArguments
+{Token first = LT(1);
+int currentLtLevel = 0;}
+    :
+        {currentLtLevel = ltCounter;}
+        LT! {ltCounter++;} nls!
+        typeArgument
+        (   options{greedy=true;}: // match as many as possible
+            {inputState.guessing !=0 || ltCounter == currentLtLevel + 1}?
+            COMMA! nls! typeArgument
+        )*
+        nls!
+        (   // turn warning off since Antlr generates the right code,
+            // plus we have our semantic predicate below
+            options{generateAmbigWarnings=false;}:
+            typeArgumentsOrParametersEnd
+        )?
+
+        // make sure we have gobbled up enough '>' characters
+        // if we are at the "top level" of nested typeArgument productions
+        {(currentLtLevel != 0) || ltCounter == currentLtLevel}?
+
+        {#typeArguments = #(create(TYPE_ARGUMENTS, "TYPE_ARGUMENTS",first,LT(1)), #typeArguments);}
+    ;
+
+// this gobbles up *some* amount of '>' characters, and counts how many
+// it gobbled.
+protected typeArgumentsOrParametersEnd
+    :   GT! {ltCounter-=1;} nls!
+    |   SR! {ltCounter-=2;} nls!
+    |   BSR! {ltCounter-=3;} nls!
+    ;
+
+// Restriction on wildcard types based on super class or derrived class
+typeArgumentBounds
+    {Token first = LT(1);boolean isUpperBounds = false;}
+    :
+        ( "extends"! {isUpperBounds=true;} | "super"! ) nls! classOrInterfaceType[true] nls!
+        {
+            if (isUpperBounds)
+            {
+                #typeArgumentBounds = #(create(TYPE_UPPER_BOUNDS,"TYPE_UPPER_BOUNDS",first,LT(1)), #typeArgumentBounds);
+            }
+            else
+            {
+                #typeArgumentBounds = #(create(TYPE_LOWER_BOUNDS,"TYPE_LOWER_BOUNDS",first,LT(1)), #typeArgumentBounds);
+            }
+        }
+    ;
+
+// A builtin type array specification is a builtin type with brackets afterwards
+builtInTypeArraySpec[boolean addImagNode]  {Token first = LT(1);}
+    :   bt:builtInType!
+        (   (LBRACK)=>   // require at least one []
+            declaratorBrackets[#bt] 
+        |   {require(false,
+                          "primitive type parameters not allowed here",
+                           "use the corresponding wrapper type, such as Integer for int"
+                           );}
+        )
+        {
+            if ( addImagNode ) {
+                #builtInTypeArraySpec = #(create(TYPE,"TYPE",first,LT(1)), #builtInTypeArraySpec);
+            }
+        }
+    ;
+
+// A builtin type specification is a builtin type with possible brackets
+// afterwards (which would make it an array type).
+builtInTypeSpec[boolean addImagNode]  {Token first = LT(1);}
+    :   bt:builtInType!
+        declaratorBrackets[#bt]
+        {
+            if ( addImagNode ) {
+                #builtInTypeSpec = #(create(TYPE,"TYPE",first,LT(1)), #builtInTypeSpec);
+            }
+        }
+    ;
+
+// A type name. which is either a (possibly qualified and parameterized)
+// class name or a primitive (builtin) type
+type
+    :   classOrInterfaceType[false]
+    |   builtInType
+    ;
+
+// The primitive types.
+builtInType
+    :   "void"
+    |   "boolean"
+    |   "byte"
+    |   "char"
+    |   "short"
+    |   "int"
+    |   "float"
+    |   "long"
+    |   "double"
+    ;
+
+// A (possibly-qualified) java identifier. We start with the first IDENT
+// and expand its name by adding dots and following IDENTS
+identifier
+    :   IDENT
+        (   options { greedy = true; } :
+            DOT^ nls! IDENT )*
+    ;
+
+identifierStar
+    :   IDENT
+        (   options { greedy = true; } :
+            DOT^  nls! IDENT )*
+        (   DOT^  nls! STAR
+        |   "as"^ nls! IDENT
+        )?
+    ;
+
+modifiersInternal
+        { int seenDef = 0; }
+    :
+        (
+            // Without this hush, there is a warning that @IDENT and @interface
+            // can follow modifiersInternal.  But how is @IDENT possible after
+            // modifiersInternal?  And how is @interface possible inside modifiersInternal?
+            // Is there an antlr bug?
+            options{generateAmbigWarnings=false;}:
+
+            // 'def' is an empty modifier, for disambiguating declarations
+            {seenDef++ == 0}?       // do not allow multiple "def" tokens
+            "def"! nls!
+        |
+            // Note: Duplication of modifiers is detected when walking the AST.
+            modifier nls!
+        |
+            {LA(1)==AT && !LT(2).getText().equals("interface")}?
+            annotation nls!
+        )+
+    ;
+
+/** A list of one or more modifier, annotation, or "def". */
+modifiers  {Token first = LT(1);}
+    :   modifiersInternal
+        {#modifiers = #(create(MODIFIERS, "MODIFIERS",first,LT(1)), #modifiers);}
+    ;
+
+/** A list of zero or more modifiers, annotations, or "def". */
+modifiersOpt  {Token first = LT(1);}
+    :   (
+            // See comment above on hushing warnings.
+            options{generateAmbigWarnings=false;}:
+
+            modifiersInternal
+        )?
+        {#modifiersOpt = #(create(MODIFIERS, "MODIFIERS",first,LT(1)), #modifiersOpt);}
+    ;
+
+// modifiers for Java classes, interfaces, class/instance vars and methods
+modifier
+    :   "private"
+    |   "public"
+    |   "protected"
+    |   "static"
+    |   "transient"
+    |   "final"
+    |   "abstract"
+    |   "native"
+    |   "threadsafe"
+    |   "synchronized"
+    |   "volatile"
+    |   "strictfp"
+    ;
+
+annotation!  {Token first = LT(1);}
+    :   AT! i:identifier nls! ( LPAREN! ( args:annotationArguments )? RPAREN! )?
+        {#annotation = #(create(ANNOTATION,"ANNOTATION",first,LT(1)), i, args);}
+    ;
+
+annotationsOpt  {Token first = LT(1);}
+    :   (annotation nls!)*
+        {#annotationsOpt = #(create(ANNOTATIONS, "ANNOTATIONS", first, LT(1)), #annotationsOpt);}
+;
+
+annotationArguments
+    :   v:annotationMemberValueInitializer
+    		{ Token itkn = new Token(IDENT, "value");
+    		  AST i;
+    		  #i = #(create(IDENT, "value", itkn, itkn));
+    			#annotationArguments = #(create(ANNOTATION_MEMBER_VALUE_PAIR,"ANNOTATION_MEMBER_VALUE_PAIR",LT(1),LT(1)), i, v);} 
+    		| annotationMemberValuePairs
+    ;
+
+annotationMemberValuePairs
+    :   annotationMemberValuePair ( COMMA! nls! annotationMemberValuePair )*
+    ;
+
+annotationMemberValuePair!  {Token first = LT(1);}
+    :   i:IDENT ASSIGN! nls! v:annotationMemberValueInitializer
+            {#annotationMemberValuePair = #(create(ANNOTATION_MEMBER_VALUE_PAIR,"ANNOTATION_MEMBER_VALUE_PAIR",first,LT(1)), i, v);}
+    ;
+
+annotationMemberValueInitializer
+    :   conditionalExpression[0] | annotation
+    ;
+
+/*OBS*
+// This is an initializer used to set up an annotation member array.
+annotationMemberArrayInitializer
+    :   lc:LCURLY^ {#lc.setType(ANNOTATION_ARRAY_INIT);}
+        (   annotationMemberArrayValueInitializer
+            (
+                // CONFLICT: does a COMMA after an initializer start a new
+                // initializer or start the option ',' at end?
+                // ANTLR generates proper code by matching
+                // the comma as soon as possible.
+                options {
+                        warnWhenFollowAmbig = false;
+                }
+            :
+                COMMA! nls! annotationMemberArrayValueInitializer
+            )*
+            (COMMA! nls!)?
+        )?
+        RCURLY!
+    ;
+*OBS*/
+
+// The two things that can initialize an annotation array element are a conditional expression
+// and an annotation (nested annotation array initialisers are not valid)
+annotationMemberArrayValueInitializer
+    :   conditionalExpression[0]
+    |   annotation nls!
+    ;
+
+superClassClause!
+    {Token first = LT(1);}
+    :
+        ( "extends" nls! c:classOrInterfaceType[false] nls! )?
+        {#superClassClause = #(create(EXTENDS_CLAUSE,"EXTENDS_CLAUSE",first,LT(1)),c);}
+    ;
+
+// Definition of a Java class
+classDefinition![AST modifiers]
+{Token first = LT(1);AST prevCurrentClass = currentClass; }
+    :   "class" IDENT nls!
+       { currentClass = #IDENT; }
+        // it _might_ have type paramaters
+        (tp:typeParameters)?
+        // it _might_ have a superclass...
+        sc:superClassClause
+        // it might implement some interfaces...
+        ic:implementsClause
+        // now parse the body of the class
+        cb:classBlock
+        {#classDefinition = #(create(CLASS_DEF,"CLASS_DEF",first,LT(1)),
+                                                            modifiers,IDENT,tp,sc,ic,cb);}
+        { currentClass = prevCurrentClass; }
+    ;
+
+//TODO - where has superClassClause! production gone???
+
+// Definition of a Java Interface
+interfaceDefinition![AST modifiers]  {Token first = LT(1);}
+    :   "interface" IDENT nls!
+        // it _might_ have type paramaters
+        (tp:typeParameters)?
+        // it might extend some other interfaces
+        ie:interfaceExtends
+        // now parse the body of the interface (looks like a class...)
+        ib:interfaceBlock
+        {#interfaceDefinition = #(create(INTERFACE_DEF,"INTERFACE_DEF",first,LT(1)),
+                                  modifiers,IDENT,tp,ie,ib);}
+    ;
+
+enumDefinition![AST modifiers]  {Token first = LT(1); AST prevCurrentClass = currentClass;}
+    :   "enum" IDENT
+    		{ currentClass = #IDENT; }
+    	nls!
+        // it might implement some interfaces...
+        ic:implementsClause
+        nls!
+        // now parse the body of the enum
+        eb:enumBlock
+        {#enumDefinition = #(create(ENUM_DEF,"ENUM_DEF",first,LT(1)),
+                             modifiers,IDENT,ic,eb);}
+        { currentClass = prevCurrentClass; }
+    ;
+
+annotationDefinition![AST modifiers]  {Token first = LT(1);}
+    :   AT "interface" IDENT
+        // now parse the body of the annotation
+        ab:annotationBlock
+        {#annotationDefinition = #(create(ANNOTATION_DEF,"ANNOTATION_DEF",first,LT(1)),
+                                   modifiers,IDENT,ab);}
+    ;
+
+typeParameters
+{Token first = LT(1);int currentLtLevel = 0;}
+    :
+        {currentLtLevel = ltCounter;}
+        LT! {ltCounter++;} nls!
+        typeParameter (COMMA! nls! typeParameter)*
+        nls!
+        (typeArgumentsOrParametersEnd)?
+
+        // make sure we have gobbled up enough '>' characters
+        // if we are at the "top level" of nested typeArgument productions
+        {(currentLtLevel != 0) || ltCounter == currentLtLevel}?
+
+        {#typeParameters = #(create(TYPE_PARAMETERS, "TYPE_PARAMETERS",first,LT(1)), #typeParameters);}
+    ;
+
+typeParameter  {Token first = LT(1);}
+    :
+        // I'm pretty sure Antlr generates the right thing here:
+        (id:IDENT) ( options{generateAmbigWarnings=false;}: typeParameterBounds )?
+        {#typeParameter = #(create(TYPE_PARAMETER,"TYPE_PARAMETER",first,LT(1)), #typeParameter);}
+    ;
+
+typeParameterBounds  {Token first = LT(1);}
+    :
+        "extends"! nls! classOrInterfaceType[true]
+        (BAND! nls! classOrInterfaceType[true])*
+        {#typeParameterBounds = #(create(TYPE_UPPER_BOUNDS,"TYPE_UPPER_BOUNDS",first,LT(1)), #typeParameterBounds);}
+    ;
+
+// This is the body of a class. You can have classFields and extra semicolons.
+classBlock  {Token first = LT(1);}
+    :   LCURLY!
+        ( classField )? ( sep! ( classField )? )*
+        RCURLY!
+        {#classBlock = #(create(OBJBLOCK, "OBJBLOCK",first,LT(1)), #classBlock);}
+    ;
+
+// This is the body of an interface. You can have interfaceField and extra semicolons.
+interfaceBlock  {Token first = LT(1);}
+    :   LCURLY!
+        ( interfaceField )? ( sep! ( interfaceField )? )*
+        RCURLY!
+        {#interfaceBlock = #(create(OBJBLOCK, "OBJBLOCK",first,LT(1)), #interfaceBlock);}
+    ;
+
+// This is the body of an annotation. You can have annotation fields and extra semicolons,
+// That's about it (until you see what an annoation field is...)
+annotationBlock  {Token first = LT(1);}
+    :   LCURLY!
+        ( annotationField )? ( sep! ( annotationField )? )*
+        RCURLY!
+        {#annotationBlock = #(create(OBJBLOCK, "OBJBLOCK",first,LT(1)), #annotationBlock);}
+    ;
+
+// This is the body of an enum. You can have zero or more enum constants
+// followed by any number of fields like a regular class
+enumBlock  {Token first = LT(1);}
+    :   LCURLY! nls!
+        (
+            // Need a syntactic predicate, since enumConstants
+            // can start with foo() as well as classField.
+            // (It's a true ambiguity, visible in the specification.
+            // To resolve in practice, use "def" before a real method.)
+            (enumConstantsStart)=> enumConstants
+        |   (classField)?
+        )
+        ( sep! (classField)? )*
+        RCURLY!
+        {#enumBlock = #(create(OBJBLOCK, "OBJBLOCK",first,LT(1)), #enumBlock);}
+    ;
+
+/** Guard for enumConstants.  */
+enumConstantsStart
+    :   enumConstant (COMMA | SEMI | NLS | RCURLY)
+    ;
+
+/** Comma-separated list of one or more enum constant definitions.  */
+enumConstants
+    :
+        enumConstant
+        ( options{greedy=true;}: COMMA! nls! enumConstant )*
+        ( COMMA! nls! )?            // trailing extra comma is OK
+    ;
+
+// An annotation field
+annotationField!  {Token first = LT(1);}
+    :   mods:modifiersOpt!
+        (   td:typeDefinitionInternal[#mods]
+            {#annotationField = #td;}
+        |   t:typeSpec[false]               // annotation field
+            (
+                // Need a syntactic predicate, since variableDefinitions
+                // can start with foo() also.  Since method defs are not legal
+                // in this context, there's no harm done.
+                (IDENT LPAREN)=>
+                i:IDENT              // the name of the field
+                LPAREN! RPAREN!
+
+                /*OBS* rt:declaratorBrackets[#t] *OBS*/
+
+                ( "default" nls! amvi:annotationMemberValueInitializer )?
+
+                {#annotationField =
+                        #(create(ANNOTATION_FIELD_DEF,"ANNOTATION_FIELD_DEF",first,LT(1)),
+                                 mods,
+                                 #(create(TYPE,"TYPE",first,LT(1)),t),
+                                 i,amvi
+                                 );}
+            |   v:variableDefinitions[#mods,#t]    // variable
+                {#annotationField = #v;}
+            )
+        )
+    ;
+
+//An enum constant may have optional parameters and may have a
+//a class body
+enumConstant!  {Token first = LT(1);}
+    :   an:annotationsOpt // Note:  Cannot start with "def" or another modifier.
+        i:IDENT
+        (   LPAREN!
+            a:argList
+            RPAREN!
+        )?
+        ( b:enumConstantBlock )?
+        {#enumConstant = #(create(ENUM_CONSTANT_DEF, "ENUM_CONSTANT_DEF",first,LT(1)), an, i, a, b);}
+    ;
+
+//The class-like body of an enum constant
+enumConstantBlock  {Token first = LT(1);}
+    :   LCURLY!
+        (enumConstantField)? ( sep! (enumConstantField)? )*
+        RCURLY!
+        {#enumConstantBlock = #(create(OBJBLOCK, "OBJBLOCK",first,LT(1)), #enumConstantBlock);}
+    ;
+
+//An enum constant field is just like a class field but without
+//the posibility of a constructor definition or a static initializer
+
+// TODO - maybe allow 'declaration' production within this production, 
+// but how to disallow constructors and static initializers...
+enumConstantField!  {Token first = LT(1);}
+    :   mods:modifiersOpt!
+        (   td:typeDefinitionInternal[#mods]
+            {#enumConstantField = #td;}
+        |   // A generic method has the typeParameters before the return type.
+            // This is not allowed for variable definitions, but this production
+            // allows it, a semantic check could be used if you wanted.
+            (tp:typeParameters)? t:typeSpec[false]          // method or variable declaration(s)
+            (
+                // Need a syntactic predicate, since variableDefinitions
+                // can start with foo() also.  Since method defs are not legal
+                // in this context, there's no harm done.
+                (IDENT LPAREN)=>
+
+                IDENT                                     // the name of the method
+
+                // parse the formal parameter declarations.
+                LPAREN! param:parameterDeclarationList RPAREN!
+
+                /*OBS* rt:declaratorBrackets[#t] *OBS*/
+
+                // get the list of exceptions that this method is
+                // declared to throw
+                ((nls "throws") => tc:throwsClause)?
+
+                ( s2:compoundStatement )?
+                // TODO - verify that 't' is useful/correct here, used to be 'rt'
+                {#enumConstantField = #(create(METHOD_DEF,"METHOD_DEF",first,LT(1)),
+                                         mods,
+                                         tp,
+                                         #(create(TYPE,"TYPE",first,LT(1)),t),
+                                         IDENT,
+                                         param,
+                                         tc,
+                                         s2);}
+
+            |   v:variableDefinitions[#mods,#t]
+                {#enumConstantField = #v;}
+            )
+        )
+
+        // "{ ... }" instance initializer
+    |   s4:compoundStatement
+        {#enumConstantField = #(create(INSTANCE_INIT,"INSTANCE_INIT",first,LT(1)), s4);}
+    ;
+
+// An interface can extend several other interfaces...
+interfaceExtends  {Token first = LT(1);}
+    :   (
+            e:"extends"! nls!
+            classOrInterfaceType[true] ( COMMA! nls! classOrInterfaceType[true] )* nls!
+        )?
+        {#interfaceExtends = #(create(EXTENDS_CLAUSE,"EXTENDS_CLAUSE",first,LT(1)),
+                               #interfaceExtends);}
+    ;
+
+// A class can implement several interfaces...
+implementsClause  {Token first = LT(1);}
+    :   (
+            i:"implements"! nls!
+            classOrInterfaceType[true] ( COMMA! nls! classOrInterfaceType[true] )* nls!
+        )?
+        {#implementsClause = #(create(IMPLEMENTS_CLAUSE,"IMPLEMENTS_CLAUSE",first,LT(1)),
+                               #implementsClause);}
+    ;
+
+// Now the various things that can be defined inside a class
+classField!  {Token first = LT(1);}
+    :   // method, constructor, or variable declaration
+        (constructorStart)=>
+        mc:modifiersOpt! ctor:constructorDefinition[#mc]
+        {#classField = #ctor;}
+    |
+        (genericMethodStart)=>
+        dg:genericMethod
+        {#classField = #dg;}
+    |
+        (declarationStart)=>
+        dd:declaration
+        {#classField = #dd;}
+        
+    |
+        //TODO - unify typeDeclaration and typeDefinitionInternal names
+        // type declaration
+        (typeDeclarationStart)=>
+        mods:modifiersOpt!
+        (   td:typeDefinitionInternal[#mods]
+                {#classField = #td;}
+        )
+
+    // "static { ... }" class initializer
+    |   "static" nls! s3:compoundStatement
+        {#classField = #(create(STATIC_INIT,"STATIC_INIT",first,LT(1)), s3);}
+
+    // "{ ... }" instance initializer
+    |   s4:compoundStatement
+        {#classField = #(create(INSTANCE_INIT,"INSTANCE_INIT",first,LT(1)), s4);}
+    ;
+
+// Now the various things that can be defined inside a interface
+interfaceField!
+    :   // method, constructor, or variable declaration
+        (declarationStart)=>
+        d:declaration
+        {#interfaceField = #d;}
+    |
+        //TODO - unify typeDeclaration and typeDefinitionInternal names
+        // type declaration
+        (typeDeclarationStart)=>
+        mods:modifiersOpt
+
+        (   td:typeDefinitionInternal[#mods]
+            {#interfaceField = #td;}
+        )
+    ;
+
+constructorBody
+    :   lc:LCURLY^ nls!         {#lc.setType(SLIST);}
+        (   (explicitConstructorInvocation) =>   // Java compatibility hack
+                explicitConstructorInvocation (sep! blockBody[sepToken])?
+            |   blockBody[EOF]
+        )
+        RCURLY!
+;
+
+
+/** Catch obvious constructor calls, but not the expr.super(...) calls */
+explicitConstructorInvocation
+    :   (typeArguments)?
+        (   "this"! lp1:LPAREN^ argList RPAREN!
+            {#lp1.setType(CTOR_CALL);}
+        |   "super"! lp2:LPAREN^ argList RPAREN!
+            {#lp2.setType(SUPER_CTOR_CALL);}
+        )
+    ;
+
+/** The tail of a declaration.
+  * Either v1, v2, ... (with possible initializers) or else m(args){body}.
+  * The two arguments are the modifier list (if any) and the declaration head (if any).
+  * The declaration head is the variable type, or (for a method) the return type.
+  * If it is missing, then the variable type is taken from its initializer (if there is one).
+  * Otherwise, the variable type defaults to 'any'.
+  * DECIDE:  Method return types default to the type of the method body, as an expression.
+  */
+variableDefinitions[AST mods, AST t]  {Token first = LT(1);}
+    :   variableDeclarator[getASTFactory().dupTree(mods),
+                           getASTFactory().dupTree(t)]
+        (   COMMA! nls!
+            variableDeclarator[getASTFactory().dupTree(mods),
+                               getASTFactory().dupTree(t)]
+        )*
+    |
+        // The parser allows a method definition anywhere a variable definition is accepted.
+
+        (   id:IDENT
+        |   qid:STRING_LITERAL          {#qid.setType(IDENT);}  // use for operator defintions, etc.
+        )
+
+        // parse the formal parameter declarations.
+        LPAREN! param:parameterDeclarationList! RPAREN!
+
+        /*OBS*rt:declaratorBrackets[#t]*/
+
+        // get the list of exceptions that this method is
+        // declared to throw
+        ((nls "throws") =>  tc:throwsClause!  )? 
+
+        // the method body is an open block
+        // but, it may have an optional constructor call (for constructors only)
+        // this constructor clause is only used for constructors using 'def'
+        // which look like method declarations
+        // since the block is optional and nls is part of sep we have to be sure
+        // a newline is followed by a block or ignore the nls too
+        ((nls! LCURLY) =>  (nlsWarn! mb:openBlock!))?
+
+        {   if (#qid != null)  #id = #qid;
+            #variableDefinitions =
+                    #(create(METHOD_DEF,"METHOD_DEF",first,LT(1)),
+                      mods, #(create(TYPE,"TYPE",first,LT(1)),t), id, param, tc, mb);
+        }
+    ;
+
+/** I've split out constructors separately; we could maybe integrate back into variableDefinitions 
+ *  later on if we maybe simplified 'def' to be a type declaration?
+ */
+constructorDefinition[AST mods]  {Token first = LT(1);}
+    :
+        id:IDENT
+
+        // parse the formal parameter declarations.
+        LPAREN! param:parameterDeclarationList! RPAREN!
+
+        /*OBS*rt:declaratorBrackets[#t]*/
+
+        // get the list of exceptions that this method is
+        // declared to throw
+        ((nls "throws") => tc:throwsClause!  )? nlsWarn!
+        // the method body is an open block
+        // but, it may have an optional constructor call (for constructors only)
+
+        // TODO assert that the id matches the class
+        { isConstructorIdent(id); }
+
+        cb:constructorBody!
+        {   #constructorDefinition =  #(create(CTOR_IDENT,"CTOR_IDENT",first,LT(1)),  mods, param, tc, cb);
+        }
+     ;
+
+/** Declaration of a variable. This can be a class/instance variable,
+ *  or a local variable in a method
+ *  It can also include possible initialization.
+ */
+variableDeclarator![AST mods, AST t]  {Token first = LT(1);}
+    :
+        id:variableName
+        /*OBS*d:declaratorBrackets[t]*/
+        (v:varInitializer)?
+        {#variableDeclarator = #(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1)), mods, #(create(TYPE,"TYPE",first,LT(1)),t), id, v);}
+    ;
+
+/** Used in cases where a declaration cannot have commas, or ends with the "in" operator instead of '='. */
+singleVariable![AST mods, AST t]  {Token first = LT(1);}
+    :
+        id:variableName
+        {#singleVariable = #(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1)), mods, #(create(TYPE,"TYPE",first,LT(1)),t), id);}
+    ;
+
+variableName
+    :   IDENT
+    ;
+
+/** After some type names, where zero or more empty bracket pairs are allowed.
+ *  We use ARRAY_DECLARATOR to represent this.
+ *  TODO:  Is there some more Groovy way to view this in terms of the indexed property syntax?
+ */
+declaratorBrackets[AST typ]
+    :   {#declaratorBrackets=typ;}
+        (
+            // A following list constructor might conflict with index brackets; prefer the declarator.
+            options {greedy=true;} :
+            lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!
+        )*
+    ;
+
+/** An assignment operator '=' followed by an expression.  (Never empty.) */
+varInitializer
+    :   ASSIGN^ nls! expression[LC_INIT]
+        // In {T x = y}, the left-context of y is that of an initializer.
+    ;
+
+/*OBS*
+// This is an initializer used to set up an array.
+arrayInitializer
+    :   lc:LCURLY^ {#lc.setType(ARRAY_INIT);}
+        (   initializer
+            (
+                // CONFLICT: does a COMMA after an initializer start a new
+                // initializer or start the option ',' at end?
+                // ANTLR generates proper code by matching
+                // the comma as soon as possible.
+                options {
+                        warnWhenFollowAmbig = false;
+                }
+            :
+                COMMA! initializer
+            )*
+            (COMMA!)?
+        )?
+        RCURLY!
+    ;
+*OBS*/
+
+/*OBS*  // Use [...] for initializing all sorts of sequences, including arrays.
+// The two "things" that can initialize an array element are an expression
+// and another (nested) array initializer.
+initializer
+    :   expression
+    |   arrayInitializer
+    ;
+*OBS*/
+
+/*OBS???
+// This is the header of a method. It includes the name and parameters
+// for the method.
+// This also watches for a list of exception classes in a "throws" clause.
+ctorHead
+    :   IDENT // the name of the method
+
+        // parse the formal parameter declarations.
+        LPAREN! parameterDeclarationList RPAREN!
+
+        // get the list of exceptions that this method is declared to throw
+        (throwsClause)?
+    ;
+*OBS*/
+
+// This is a list of exception classes that the method is declared to throw
+throwsClause
+    :   nls! "throws"^ nls! identifier ( COMMA! nls! identifier )* 
+    ;
+
+/** A list of zero or more formal parameters.
+ *  If a parameter is variable length (e.g. String... myArg) it should be
+ *  to the right of any other parameters of the same kind.
+ *  General form:  (req, ..., opt, ..., [rest], key, ..., [restKeys], [block]
+ *  This must be sorted out after parsing, since the various declaration forms
+ *  are impossible to tell apart without backtracking.
+ */
+parameterDeclarationList  {Token first = LT(1);}
+    :
+        (
+            parameterDeclaration
+            (   COMMA! nls!
+                parameterDeclaration
+            )*
+        )?
+        {#parameterDeclarationList = #(create(PARAMETERS,"PARAMETERS",first,LT(1)),
+                                       #parameterDeclarationList);}
+    ;
+
+/** A formal parameter for a method or closure. */
+parameterDeclaration!
+        { Token first = LT(1);boolean spreadParam = false; }
+    :
+        pm:parameterModifiersOpt
+        (   options {greedy=true;} :
+            t:typeSpec[false]
+        )?
+
+        // TODO:  What do formal parameters for keyword arguments look like?
+
+        // Java-style var args
+        ( TRIPLE_DOT! { spreadParam = true; } )?
+
+        id:IDENT
+
+        // allow an optional default value expression
+        (exp:varInitializer)?
+
+        /*OBS*pd:declaratorBrackets[#t]*/
+        {
+            if (spreadParam) {
+                #parameterDeclaration = #(create(VARIABLE_PARAMETER_DEF,"VARIABLE_PARAMETER_DEF",first,LT(1)),
+                      pm, #(create(TYPE,"TYPE",first,LT(1)),t), id, exp);
+            } else {
+                #parameterDeclaration = #(create(PARAMETER_DEF,"PARAMETER_DEF",first,LT(1)),
+                      pm, #(create(TYPE,"TYPE",first,LT(1)),t), id, exp);
+            }
+        }
+    ;
+
+/*OBS*
+variableLengthParameterDeclaration!  {Token first = LT(1);}
+    :   pm:parameterModifier t:typeSpec[false] TRIPLE_DOT! id:IDENT
+
+        /*OBS* pd:declaratorBrackets[#t]* /
+        {#variableLengthParameterDeclaration = #(create(VARIABLE_PARAMETER_DEF,"VARIABLE_PARAMETER_DEF",first,LT(1)),
+                                                                                            pm, #(create(TYPE,"TYPE",first,LT(1)),t), id);}
+    ;
+*OBS*/
+
+parameterModifiersOpt
+        { Token first = LT(1);int seenDef = 0; }
+        //final and/or def can appear amongst annotations in any order
+    :   (   {seenDef++ == 0}?       // do not allow multiple "def" tokens
+            "def"!  nls!            // redundant, but allowed for symmetry
+        |   "final" nls!
+        |   annotation nls!
+        )*
+        {#parameterModifiersOpt = #(create(MODIFIERS,"MODIFIERS",first,LT(1)), #parameterModifiersOpt);}
+    ;
+
+/** Closure parameters are exactly like method parameters,
+ *  except that they are not enclosed in parentheses, but rather
+ *  are prepended to the front of a block, just after the brace.
+ *  They are separated from the closure body by a CLOSABLE_BLOCK_OP token '->'.
+ */
+// With '|' there would be restrictions on bitwise-or expressions.
+closableBlockParamsOpt[boolean addImplicit]
+    :   (parameterDeclarationList nls CLOSABLE_BLOCK_OP)=>
+        parameterDeclarationList nls! CLOSABLE_BLOCK_OP! nls!
+    |   {addImplicit}?
+        implicitParameters
+    |
+        /* else do not parse any parameters at all */
+    ;
+
+/** Lookahead to check whether a block begins with explicit closure arguments. */
+closableBlockParamsStart!
+    :
+        parameterDeclarationList nls CLOSABLE_BLOCK_OP
+    ;
+
+/** Simple names, as in {x|...}, are completely equivalent to {(def x)|...}.  Build the right AST. */
+closableBlockParam!  {Token first = LT(1);}
+    :   id:IDENT!
+        {#closableBlockParam = #(create(PARAMETER_DEF,"PARAMETER_DEF",first,LT(1)),
+                               #(create(MODIFIERS,"MODIFIERS",first,LT(1))), #(create(TYPE,"TYPE",first,LT(1))),
+                               id);}
+    ;
+
+// Compound statement. This is used in many contexts:
+// Inside a class definition prefixed with "static":
+// it is a class initializer
+// Inside a class definition without "static":
+// it is an instance initializer
+// As the body of a method
+// As a completely indepdent braced block of code inside a method
+// it starts a new scope for variable definitions
+// In Groovy, this is called an "open block".  It cannot have closure arguments.
+
+compoundStatement
+    :   openBlock
+    ;
+
+/** An open block is not allowed to have closure arguments. */
+openBlock
+    :   lc:LCURLY^ nls!     {#lc.setType(SLIST);}
+        // AST type of SLIST means "never gonna be a closure"
+        blockBody[EOF]
+        RCURLY!
+    ;
+
+/** A block body is a parade of zero or more statements or expressions. */
+blockBody[int prevToken]
+    :   
+        (statement[prevToken])? (sep! (statement[sepToken])?)*
+    ;
+
+/** A block which is known to be a closure, even if it has no apparent arguments.
+ *  A block inside an expression or after a method call is always assumed to be a closure.
+ *  Only labeled, unparameterized blocks which occur directly as substatements are kept open.
+ */
+closableBlock
+    :   lc:LCURLY^ nls!     {#lc.setType(CLOSABLE_BLOCK);}
+        closableBlockParamsOpt[true]
+        blockBody[EOF]
+        RCURLY!
+    ;
+
+/** A block known to be a closure, but which omits its arguments, is given this placeholder.
+ *  A subsequent pass is responsible for deciding if there is an implicit 'it' parameter,
+ *  or if the parameter list should be empty.
+ */
+implicitParameters  {Token first = LT(1);}
+    :   {   #implicitParameters = #(create(IMPLICIT_PARAMETERS,"IMPLICIT_PARAMETERS",first,LT(1)));  }
+    ;
+
+/** A sub-block of a block can be either open or closable.
+ *  It is closable if and only if there are explicit closure arguments.
+ *  Compare this to a block which is appended to a method call,
+ *  which is given closure arguments, even if they are not explicit in the code.
+ */
+openOrClosableBlock
+    :   lc:LCURLY^ nls!
+        cp:closableBlockParamsOpt[false]
+        {   if (#cp == null)    #lc.setType(SLIST);
+            else                #lc.setType(CLOSABLE_BLOCK);
+        }
+        blockBody[EOF]
+        RCURLY!
+    ;
+
+/** A statement is an element of a block.
+ *  Typical statements are declarations (which are scoped to the block)
+ *  and expressions.
+ */
+statement[int prevToken]
+    // prevToken is NLS if previous statement is separated only by a newline
+
+    :   (genericMethodStart)=>
+        genericMethod
+        
+
+    // declarations are ambiguous with "ID DOT" relative to expression
+    // statements. Must backtrack to be sure. Could use a semantic
+    // predicate to test symbol table to see what the type was coming
+    // up, but that's pretty hard without a symbol table ;)
+    |   (declarationStart)=>
+        declaration
+
+    // Attach a label to the front of a statement
+    // This block is executed for effect, unless it has an explicit closure argument.
+    |
+        (IDENT COLON)=>
+        pfx:statementLabelPrefix!
+        {#statement = #pfx;}  // nest it all under the label prefix
+        (   (LCURLY) => openOrClosableBlock
+        |   statement[COLON]
+        )
+
+    // An expression statement. This could be a method call,
+    // assignment statement, or any other expression evaluated for
+    // side-effects.
+    // The prevToken is used to check for dumb expressions like +1.
+    |    expressionStatement[prevToken]
+
+    // class definition
+    |    m:modifiersOpt! typeDefinitionInternal[#m]
+
+    // If-else statement
+    |   "if"^ LPAREN! assignmentLessExpression RPAREN! nlsWarn! compatibleBodyStatement
+        (
+            // CONFLICT: the old "dangling-else" problem...
+            //           ANTLR generates proper code matching
+            //                       as soon as possible.  Hush warning.
+            options {
+                    warnWhenFollowAmbig = false;
+            }
+        :   // lookahead to check if we're entering an 'else' clause
+            ( (sep!)? "else"! )=>
+            (sep!)?  // allow SEMI here for compatibility with Java
+            "else"! nlsWarn! compatibleBodyStatement
+        )?
+
+    // For statement
+    |   forStatement
+
+    // While statement
+    |   "while"^ LPAREN! strictContextExpression RPAREN! nlsWarn! compatibleBodyStatement
+
+    /*OBS* no do-while statement in Groovy (too ambiguous)
+    // do-while statement
+    |   "do"^ statement "while"! LPAREN! strictContextExpression RPAREN! SEMI!
+    *OBS*/
+
+    // Import statement.  Can be used in any scope.  Has "import x as y" also.
+    |   importStatement
+
+    // switch/case statement
+    |   "switch"^ LPAREN! strictContextExpression RPAREN! nlsWarn! LCURLY! nls!
+        ( casesGroup )*
+        RCURLY!
+
+    // exception try-catch block
+    |   tryBlock
+
+    // synchronize a statement
+    |   "synchronized"^ LPAREN! strictContextExpression RPAREN! nlsWarn! compoundStatement
+
+
+    /*OBS*
+    // empty statement
+    |   s:SEMI {#s.setType(EMPTY_STAT);}
+    *OBS*/
+
+    |   branchStatement
+    ;
+
+forStatement
+    :   f:"for"^
+        LPAREN!
+        (   (SEMI |(strictContextExpression SEMI))=>closureList
+            // *OBS*
+            // There's no need at all for squeezing in the new Java 5 "for"
+            // syntax, since Groovy's is a suitable alternative.
+            // |   (parameterDeclaration COLON)=> forEachClause
+            // *OBS*
+        |   // the coast is clear; it's a modern Groovy for statement
+            forInClause
+        )
+        RPAREN! nls!
+        compatibleBodyStatement                                  // statement to loop over
+    ;
+
+closureList
+	{Token first = LT(1);}
+    :
+    	
+    	strictContextExpression
+    	(
+    			SEMI! strictContextExpression 
+    	   |	SEMI! {astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT"));}
+    	)+
+		{#closureList = #(create(CLOSURE_LIST,"CLOSURE_LIST",first,LT(1)),#closureList);}
+    ;
+
+/*OBS*
+forEachClause  {Token first = LT(1);}
+    :
+        p:parameterDeclaration COLON! expression
+        {#forEachClause = #(create(FOR_EACH_CLAUSE,"FOR_EACH_CLAUSE",first,LT(1)), #forEachClause);}
+    ;
+*OBS*/
+
+forInClause
+    :   (   (declarationStart)=>
+            decl:singleDeclarationNoInit
+        |   IDENT
+        )
+        (
+            i:"in"^         {#i.setType(FOR_IN_ITERABLE);}
+            shiftExpression[0]
+        |
+            { addWarning(
+              "A colon at this point is legal Java but not recommended in Groovy.",
+              "Use the 'in' keyword."
+              );
+            require(#decl != null,
+                "Java-style for-each statement requires a type declaration."
+                ,
+                "Use the 'in' keyword, as for (x in y) {...}"
+                );
+            }
+            c:COLON^         {#c.setType(FOR_IN_ITERABLE);}
+            expression[0]
+        )
+    ;
+
+/** In Java, "if", "while", and "for" statements can take random, non-braced statements as their bodies.
+ *  Support this practice, even though it isn't very Groovy.
+ */
+compatibleBodyStatement
+    :   (LCURLY)=>
+        compoundStatement
+    |
+        statement[EOF]
+    ;
+
+/** In Groovy, return, break, continue, throw, and assert can be used in a parenthesized expression context.
+ *  Example:  println (x || (return));  println assert x, "won't print a false value!"
+ *  If an optional expression is missing, its value is void (this coerces to null when a value is required).
+ */
+branchStatement
+    :
+    // Return an expression
+        "return"^
+        ( expression[0] )?
+
+    // break:  get out of a loop, or switch, or method call
+    // continue:  do next iteration of a loop, or leave a closure
+    |   ("break"^ | "continue"^)
+        ( IDENT )?
+
+    // throw an exception
+    |   "throw"^ expression[0]
+
+
+    // TODO - decide on definitive 'assert' statement in groovy (1.4 and|or groovy)
+    // asserts
+    // 1.4+ ...
+    //      |   "assert"^ expression[0] ( COLON! expression[0] )?
+
+    // groovy assertion...
+    |   "assert"^ assignmentLessExpression
+        (   options {greedy=true;} :
+            (   COMMA!  // TODO:  gratuitous change caused failures
+            |   COLON!  // standard Java syntax, but looks funny in Groovy
+            )
+            expression[0]
+        )?
+    ;
+
+/** A labeled statement, consisting of a vanilla identifier followed by a colon. */
+// Note:  Always use this lookahead, to keep antlr from panicking: (IDENT COLON)=>
+statementLabelPrefix
+    :   IDENT c:COLON^ {#c.setType(LABELED_STAT);} nls!
+    ;
+
+/** An expression statement can be any general expression.
+ *  <p>
+ *  An expression statement can also be a <em>command</em>,
+ *  which is a simple method call in which the outermost parentheses are omitted.
+ *  <p>
+ *  Certain "suspicious" looking forms are flagged for the user to disambiguate.
+ */
+// DECIDE: A later semantic pass can flag dumb expressions that don't occur in
+//         positions where their value is not used, e.g., <code>{1+1;println}</code>
+expressionStatement[int prevToken]
+        {Token first = LT(1);boolean isPathExpr = false;  }
+    : 
+        (   (suspiciousExpressionStatementStart)=>
+            checkSuspiciousExpressionStatement[prevToken]
+        )?
+        // Checks are now out of the way; here's the real rule:
+        head:expression[LC_STMT]
+        {   isPathExpr = (#head == lastPathExpression);  }
+        (
+            // A path expression (e.g., System.out.print) can take arguments.
+            {isPathExpr}?
+            cmd:commandArguments[#head]!
+            {#expressionStatement = #cmd;}
+        )?
+        {#expressionStatement = #(create(EXPR,"EXPR",first,LT(1)),#expressionStatement);}
+    ;
+        
+/**
+ *  If two statements are separated by newline (not SEMI), the second had
+ *  better not look like the latter half of an expression.  If it does, issue a warning.
+ *  <p>
+ *  Also, if the expression starts with a closure, it needs to
+ *  have an explicit parameter list, in order to avoid the appearance of a
+ *  compound statement.  This is a hard error.
+ *  <p>
+ *  These rules are different from Java's "dumb expression" restriction.
+ *  Unlike Java, Groovy blocks can end with arbitrary (even dumb) expressions,
+ *  as a consequence of optional 'return' and 'continue' tokens.
+ * <p>
+ *  To make the programmer's intention clear, a leading closure must have an
+ *  explicit parameter list, and must not follow a previous statement separated
+ *  only by newlines.
+ */
+checkSuspiciousExpressionStatement[int prevToken]
+    :
+        (~LCURLY | LCURLY closableBlockParamsStart)=>  //FIXME too much lookahead
+        // Either not a block, or a block with an explicit closure parameter list.
+        (   {prevToken == NLS}?
+            {   addWarning(
+                "Expression statement looks like it may continue a previous statement.",
+                "Either remove previous newline, or add an explicit semicolon ';'.");
+            }
+        )?
+    |
+        // Else we have a block without any visible closure parameters.
+        {prevToken == NLS}?
+        // if prevToken is NLS, we have double trouble; issue a double warning
+        // Example:  obj.foo \n {println x}
+        // Might be appended block:  obj.foo {println x}
+        // Might be closure expression:  obj.foo ; {x->println x}
+        // Might be open block:  obj.foo ; L:{println x}
+        {   require(false,
+            "Closure expression looks like it may be an isolated open block, "+
+            "or it may continue a previous statement."
+            ,
+            "Add an explicit parameter list, as in {it -> ...}, or label it as L:{...}, "+
+            "and also either remove previous newline, or add an explicit semicolon ';'."
+            );
+        }
+    |
+        {prevToken != NLS}?
+        // If prevToken is SEMI or something else, issue a single warning:
+        // Example:  obj.foo ; {println x}
+        // Might be closure expression:  obj.foo ; {x->println x}
+        // Might be open block:  obj.foo ; L:{println x}
+        {   require(false,
+            "Closure expression looks like it may be an isolated open block.",
+            "Add an explicit parameter list, as in {it -> ...}, or label it as L:{...}.");
+        }
+    ;
+
+/** Lookahead for suspicious statement warnings and errors. */
+suspiciousExpressionStatementStart
+    :
+        (   (PLUS | MINUS)
+        |   (LBRACK | LPAREN | LCURLY)
+        )
+        // TODO:  Expand this set?
+    ;
+
+// Support for switch/case:
+casesGroup  {Token first = LT(1);}
+    :   (   // CONFLICT: to which case group do the statements bind?
+            // ANTLR generates proper code: it groups the
+            // many "case"/"default" labels together then
+            // follows them with the statements
+            options {
+                greedy = true;
+            }
+            :
+            aCase
+        )+
+        caseSList
+        {#casesGroup = #(create(CASE_GROUP, "CASE_GROUP",first,LT(1)), #casesGroup);}
+    ;
+
+aCase
+    :   ("case"^ expression[0] | "default") COLON! nls!
+    ;
+
+caseSList  {Token first = LT(1);}
+    :   statement[COLON] (sep! (statement[sepToken])?)*
+        {#caseSList = #(create(SLIST,"SLIST",first,LT(1)),#caseSList);}
+    ;
+
+// The initializer for a for loop
+forInit  {Token first = LT(1);}
+    :   // if it looks like a declaration, it is
+        (declarationStart)=> declaration
+    |   // else it's a comma-separated list of expressions
+        (controlExpressionList)?
+        {#forInit = #(create(FOR_INIT,"FOR_INIT",first,LT(1)),#forInit);}
+    ;
+
+forCond  {Token first = LT(1);}
+    :   (strictContextExpression)?
+        {#forCond = #(create(FOR_CONDITION,"FOR_CONDITION",first,LT(1)),#forCond);}
+    ;
+
+forIter  {Token first = LT(1);}
+    :   (controlExpressionList)?
+        {#forIter = #(create(FOR_ITERATOR,"FOR_ITERATOR",first,LT(1)),#forIter);}
+    ;
+
+// an exception handler try/catch block
+tryBlock
+    :   "try"^ nlsWarn! compoundStatement
+            ( options {greedy=true;} :  nls! handler)*
+            ( options {greedy=true;} :  nls! finallyClause)?
+    ;
+
+finallyClause
+    :   "finally"^ nlsWarn! compoundStatement
+    ;
+
+// an exception handler
+handler
+    :   "catch"^ LPAREN! parameterDeclaration RPAREN! nlsWarn! compoundStatement
+    ;
+
+/** A member name (x.y) or element name (x[y]) can serve as a command name,
+ *  which may be followed by a list of arguments.
+ *  Unlike parenthesized arguments, these must be plain expressions,
+ *  without labels or spread operators.
+ */
+commandArguments[AST head]  
+  {
+  	Token first = LT(1);
+  	int hls=0;
+  }
+    :
+        commandArgument ( COMMA! nls! commandArgument )*
+        // println 2+2 //OK
+        // println(2+2) //OK
+        // println (2)+2 //BAD
+        // println((2)+2) //OK
+        // (println(2)+2) //OK
+        // compare (2), 2 //BAD
+        // compare( (2), 2 ) //OK
+        // foo.bar baz{bat}, bang{boz} //OK
+        {
+            AST elist = #(create(ELIST,"ELIST",first,LT(1)), #commandArguments);
+            AST headid = getASTFactory().dup(#head);
+            headid.setType(METHOD_CALL);
+            headid.setText("<command>");
+            #commandArguments = #(headid, head, elist);
+        }
+    ;
+
+commandArgument
+    :
+        (argumentLabel COLON) => (
+            argumentLabel c:COLON^  expression[0]  {#c.setType(LABELED_ARG);}
+        )
+        |  expression[0]
+    ;
+
+// expressions
+// Note that most of these expressions follow the pattern
+//   thisLevelExpression :
+//         nextHigherPrecedenceExpression
+//                 (OPERATOR nextHigherPrecedenceExpression)*
+// which is a standard recursive definition for a parsing an expression.
+// The operators in java have the following precedences:
+//      lowest  (15)  = **= *= /= %= += -= <<= >>= >>>= &= ^= |=
+//                      (14)  ?:
+//                      (13)  ||
+//                      (12)  &&
+//                      (11)  |
+//                      (10)  ^
+//                      ( 9)  &
+//                      ( 8)  == != <=>
+//                      ( 7)  < <= > >= instanceof as
+//                      ( 6)  << >> .. ...
+//                      ( 5)  +(binary) -(binary)
+//                      ( 4)  * / %
+//                      ( 3)  ++(pre/post) --(pre/post) +(unary) -(unary)
+//                      ( 2)  **(power)
+//                      ( 1)  ~  ! $ (type)
+//                            . ?. *. (dot -- identifier qualification)
+//                            []   () (method call)  {} (closableBlock)  [] (list/map)
+//                            new  () (explicit parenthesis)
+//                            $x (scope escape)
+//
+// the last two are not usually on a precedence chart; I put them in
+// to point out that new has a higher precedence than '.', so you
+// can validy use
+//       new Frame().show()
+//
+// Note that the above precedence levels map to the rules below...
+// Once you have a precedence chart, writing the appropriate rules as below
+//   is usually very straightfoward
+
+
+// the mother of all expressions
+// This nonterminal is not used for expression statements, which have a more restricted syntax
+// due to possible ambiguities with other kinds of statements.  This nonterminal is used only
+// in contexts where we know we have an expression.  It allows general Java-type expressions.
+expression[int lc_stmt]
+    :   assignmentExpression[lc_stmt]
+    ;
+
+// This is a list of expressions.
+// Used for backward compatibility, in a few places where
+// comma-separated lists of Java expression statements and declarations are required.
+controlExpressionList  {Token first = LT(1);}
+    :   strictContextExpression (COMMA! nls! strictContextExpression)*
+        {#controlExpressionList = #(create(ELIST,"ELIST",first,LT(1)), controlExpressionList);}
+    ;
+
+/** A "path expression" is a name or other primary, possibly qualified by various
+ *  forms of dot, and/or followed by various kinds of brackets.
+ *  It can be used for value or assigned to, or else further qualified, indexed, or called.
+ *  It is called a "path" because it looks like a linear path through a data structure.
+ *  Examples:  x.y, x?.y, x*.y, x.@y; x[], x[y], x[y,z]; x(), x(y), x(y,z); x{s}; a.b[n].c(x).d{s}
+ *  (Compare to a C lvalue, or LeftHandSide in the JLS section 15.26.)
+ *  General expressions are built up from path expressions, using operators like '+' and '='.
+ */
+pathExpression[int lc_stmt]
+        { AST prefix = null; }
+    :	
+        pre:primaryExpression!
+        { prefix = #pre; }
+
+        (
+            options {
+                // \n{foo} could match here or could begin a new statement
+                // We do want to match here. Turn off warning.
+                greedy=true;
+                // This turns the ambiguity warning of the second alternative
+                // off. See below. (The "ANTLR_LOOP_EXIT" predicate makes it non-issue)
+                //@@ warnWhenFollowAmbig=false;
+            }
+            // Parsing of this chain is greedy.  For example, a pathExpression may be a command name
+            // followed by a command argument, but that command argument cannot begin with an LPAREN,
+            // since a parenthesized expression is greedily attached to the pathExpression as a method argument.
+            // The lookahead is also necessary to reach across newline in foo \n {bar}.
+            // (Apparently antlr's basic approximate LL(k) lookahead is too weak for this.)
+        :   (pathElementStart)=>
+            nls!
+            pe:pathElement[prefix]!
+            { prefix = #pe; }
+        |
+            {lc_stmt == LC_STMT || lc_stmt == LC_INIT}?
+            (nls LCURLY)=>
+            nlsWarn!
+            apb:appendedBlock[prefix]!
+            { prefix = #apb; }
+        )*
+
+        {
+            #pathExpression = prefix;
+            lastPathExpression = #pathExpression;
+        }
+    ;
+
+pathElement[AST prefix]
+        // The primary can then be followed by a chain of .id, (a), [a], and {...}
+    :
+        {   #pathElement = prefix;  }
+        (   // Spread operator:  x*.y  ===  x?.collect{it.y}
+            SPREAD_DOT^
+        |   // Optional-null operator:  x?.y  === (x==null)?null:x.y
+            OPTIONAL_DOT^
+        |   // Member pointer operator: foo.&y == foo.metaClass.getMethodPointer(foo, "y")
+            MEMBER_POINTER^
+        |   // The all-powerful dot.
+            (nls! DOT^)
+        ) nls!
+        (typeArguments)?   // TODO: Java 5 type argument application via prefix x.<Integer>y
+        namePart
+    |
+        mca:methodCallArgs[prefix]!
+        {   #pathElement = #mca;  }
+    |
+        // Can always append a block, as foo{bar}
+        apb:appendedBlock[prefix]!
+        {   #pathElement = #apb;  }
+    |
+        // Element selection is always an option, too.
+        // In Groovy, the stuff between brackets is a general argument list,
+        // since the bracket operator is transformed into a method call.
+        ipa:indexPropertyArgs[prefix]!
+        {   #pathElement = #ipa;  }
+
+/*NYI*
+    |   DOT^ nls! "this"
+
+    |   DOT^ nls! "super"
+        (   // (new Outer()).super()  (create enclosing instance)
+            lp3:LPAREN^ argList RPAREN!
+            {#lp3.setType(SUPER_CTOR_CALL);}
+        |   DOT^ IDENT
+            (   lps:LPAREN^ {#lps.setType(METHOD_CALL);}
+                argList
+                RPAREN!
+            )?
+        )
+    |   DOT^ nls! newExpression
+*NYI*/
+    ;
+
+pathElementStart!
+    :   (nls! DOT)
+    |   SPREAD_DOT
+    |   OPTIONAL_DOT
+//todo - nondeterminisms    |   MEMBER_POINTER_DEFAULT
+    |   MEMBER_POINTER
+    |   LBRACK
+    |   LPAREN
+    |   LCURLY
+    ;
+
+/** This is the grammar for what can follow a dot:  x.a, x.@a, x.&a, x.'a', etc.
+ *  Note: <code>typeArguments</code> is handled by the caller of <code>namePart</code>.
+ */
+namePart  {Token first = LT(1);}
+    :
+        (   ats:AT^     {#ats.setType(SELECT_SLOT);}  )?
+        // foo.@bar selects the field (or attribute), not property
+
+        (   IDENT
+        |   sl:STRING_LITERAL {#sl.setType(IDENT);}
+            // foo.'bar' is in all ways same as foo.bar, except that bar can have an arbitrary spelling
+        |   dynamicMemberName
+        |
+            openBlock
+            // PROPOSAL, DECIDE:  Is this inline form of the 'with' statement useful?
+            // Definition:  a.{foo} === {with(a) {foo}}
+            // May cover some path expression use-cases previously handled by dynamic scoping (closure delegates).
+
+                                                                    // lets allow common keywords as property names
+        |   keywordPropertyNames
+
+/* lets allow some common keywords for properties like 'in', 'class', 'def' etc
+ * TODO: Reinstate this logic if we change or remove keywordPropertyNames.
+ * See also LITERAL_in logic in the lexer.
+        // Recover with a good diagnostic from a common error:
+        |   "in"  // poster child; the lexer makes all keywords after dot look like "in"
+            {   String kwd = LT(1).getText();
+                require(false,
+                    "illegal keyword after dot in x."+kwd,
+                    "put the keyword in quotes, as in x.'"+kwd+"'");
+                // This helps the user recover from ruined Java identifiers, as in System.'in'.
+                // DECIDE: Shall we just define foo.in to DTRT automagically, or do we want the syntax check?
+            }
+*/                        
+        )
+
+        // (No, x.&@y is not needed; just say x.&y as Slot or some such.)
+    ;
+
+/** Allowed keywords after dot (as a member name) and before colon (as a label).
+ *  TODO: What's the rationale for these?
+ */
+keywordPropertyNames
+    :   (   "class" | "in" | "as" | "def" | "default" | "static" | "goto"
+        |   "if" | "else" | "for" | "while" | "do" | "switch" | "try" | "catch" | "finally"
+        |   builtInType
+        )
+        { #keywordPropertyNames.setType(IDENT); }
+    ;
+                                                
+/** If a dot is followed by a parenthesized or quoted expression, the member is computed dynamically,
+ *  and the member selection is done only at runtime.  This forces a statically unchecked member access.
+ */
+dynamicMemberName  {Token first = LT(1);}
+    :   (   parenthesizedExpression
+        |   stringConstructorExpression
+        )
+        { #dynamicMemberName = #(create(DYNAMIC_MEMBER, "DYNAMIC_MEMBER",first,LT(1)), #dynamicMemberName); }
+    ;
+
+/** An expression may be followed by one or both of (...) and {...}.
+ *  Note: If either is (...) or {...} present, it is a method call.
+ *  The {...} is appended to the argument list, and matches a formal of type Closure.
+ *  If there is no method member, a property (or field) is used instead, and must itself be callable.
+ *  <p>
+ *  If the methodCallArgs are absent, it is a property reference.
+ *  If there is no property, it is treated as a field reference, but never a method reference.
+ *  <p>
+ *  Arguments in the (...) can be labeled, and the appended block can be labeled also.
+ *  If there is a mix of unlabeled and labeled arguments,
+ *  all the labeled arguments must follow the unlabeled arguments,
+ *  except that the closure (labeled or not) is always a separate final argument.
+ *  Labeled arguments are collected up and passed as a single argument to a formal of type Map.
+ *  <p>
+ *  Therefore, f(x,y, a:p, b:q) {s} is equivalent in all ways to f(x,y, [a:p,b:q], {s}).
+ *  Spread arguments of sequence type count as unlabeled arguments,
+ *  while spread arguments of map type count as labeled arguments.
+ *  (This distinction must sometimes be checked dynamically.)
+ *
+ *  A plain unlabeled argument is allowed to match a trailing Map or Closure argument:
+ *  f(x, a:p) {s}  ===  f(*[ x, [a:p], {s} ])
+ */
+// AST is [METHOD_CALL, callee, ELIST? CLOSABLE_BLOCK?].
+// Note that callee is often of the form x.y but not always.
+// If the callee is not of the form x.y, then an implicit .call is needed.
+methodCallArgs[AST callee]
+    :
+        {#methodCallArgs = callee;}
+        lp:LPAREN^ {#lp.setType(METHOD_CALL);}
+        argList
+        RPAREN!
+    ;
+
+/** An appended block follows any expression.
+ *  If the expression is not a method call, it is given an empty argument list.
+ */
+appendedBlock[AST callee]
+    :
+        {
+            // If the callee is itself a call, flatten the AST.
+            if (callee != null && callee.getType() == METHOD_CALL) {
+                #appendedBlock = callee;
+            } else {
+                AST lbrace = getASTFactory().create(LT(1));
+                lbrace.setType(METHOD_CALL);
+                if (callee != null)  lbrace.addChild(callee);
+                #appendedBlock = lbrace;
+            }
+        }
+        /*  FIXME DECIDE: should appended blocks accept labels?
+        (   (IDENT COLON nls LCURLY)=>
+            IDENT c:COLON^ {#c.setType(LABELED_ARG);} nls!
+        )? */
+        closableBlock
+    ;
+
+/** An expression may be followed by [...].
+ *  Unlike Java, these brackets may contain a general argument list,
+ *  which is passed to the array element operator, which can make of it what it wants.
+ *  The brackets may also be empty, as in T[].  This is how Groovy names array types.
+ *  <p>Returned AST is [INDEX_OP, indexee, ELIST].
+ */
+indexPropertyArgs[AST indexee]
+    :
+        {#indexPropertyArgs = indexee;}
+        lb:LBRACK^ {#lb.setType(INDEX_OP);}
+        argList
+        RBRACK!
+    ;
+
+// assignment expression (level 15)
+assignmentExpression[int lc_stmt]
+    :   conditionalExpression[lc_stmt]
+        (
+            (   ASSIGN^
+            |   PLUS_ASSIGN^
+            |   MINUS_ASSIGN^
+            |   STAR_ASSIGN^
+            |   DIV_ASSIGN^
+            |   MOD_ASSIGN^
+            |   SR_ASSIGN^
+            |   BSR_ASSIGN^
+            |   SL_ASSIGN^
+            |   BAND_ASSIGN^
+            |   BXOR_ASSIGN^
+            |   BOR_ASSIGN^
+            |   STAR_STAR_ASSIGN^
+            //|   USEROP_13^  //DECIDE: This is how user-define ops would show up.
+            )
+            nls!
+            assignmentExpression[lc_stmt == LC_STMT? LC_INIT: 0]
+            // If left-context of {x = y} is a statement boundary,
+            // define the left-context of y as an initializer.
+        )?
+    ;
+
+// conditional test (level 14)
+conditionalExpression[int lc_stmt]
+    :   logicalOrExpression[lc_stmt] 
+        ( 
+          (ELVIS_OPERATOR)=> ELVIS_OPERATOR^ nls! conditionalExpression[0] 
+          | QUESTION^ nls! assignmentExpression[0] COLON! nls! conditionalExpression[0] 
+        )?
+    ;
+
+
+// logical or (||)  (level 13)
+logicalOrExpression[int lc_stmt]
+    :   logicalAndExpression[lc_stmt] (LOR^ nls! logicalAndExpression[0])*
+    ;
+
+
+// logical and (&&)  (level 12)
+logicalAndExpression[int lc_stmt]
+    :   inclusiveOrExpression[lc_stmt] (LAND^ nls! inclusiveOrExpression[0])*
+    ;
+
+// bitwise or non-short-circuiting or (|)  (level 11)
+inclusiveOrExpression[int lc_stmt]
+    :   exclusiveOrExpression[lc_stmt] (BOR^ nls! exclusiveOrExpression[0])*
+    ;
+
+
+// exclusive or (^)  (level 10)
+exclusiveOrExpression[int lc_stmt]
+    :   andExpression[lc_stmt] (BXOR^ nls! andExpression[0])*
+    ;
+
+
+// bitwise or non-short-circuiting and (&)  (level 9)
+andExpression[int lc_stmt]
+    :   regexExpression[lc_stmt] (BAND^ nls! regexExpression[0])*
+    ;
+
+// regex find and match (=~ and ==~) (level 8.5)
+// jez: moved =~ closer to precedence of == etc, as...
+// 'if (foo =~ "a.c")' is very close in intent to 'if (foo == "abc")'
+regexExpression[int lc_stmt]
+    :   equalityExpression[lc_stmt] ((REGEX_FIND^ | REGEX_MATCH^) nls! equalityExpression[0])*
+    ;
+
+// equality/inequality (==/!=) (level 8)
+equalityExpression[int lc_stmt]
+    :   relationalExpression[lc_stmt] ((NOT_EQUAL^ | EQUAL^ | COMPARE_TO^) nls! relationalExpression[0])*
+    ;
+
+// boolean relational expressions (level 7)
+relationalExpression[int lc_stmt]
+    :   shiftExpression[lc_stmt]
+        (	options {greedy=true;} : (	
+        		(   LT^
+                |   GT^
+                |   LE^
+                |   GE^
+                |   "in"^
+                )
+                nls!
+                shiftExpression[0]
+            
+        	)
+        	|   "instanceof"^ nls! typeSpec[true]
+        	|   "as"^         nls! typeSpec[true] //TODO: Rework to allow type expression?
+        )?
+    ;
+
+
+
+// bit shift expressions (level 6)
+shiftExpression[int lc_stmt]
+    :   additiveExpression[lc_stmt]
+        (
+            ((SL^ | SR^ | BSR^)
+            |   RANGE_INCLUSIVE^
+            |   RANGE_EXCLUSIVE^
+            )
+            nls!
+            additiveExpression[0]
+        )*
+    ;
+
+
+// binary addition/subtraction (level 5)
+additiveExpression[int lc_stmt]
+    :   multiplicativeExpression[lc_stmt]
+        (
+            options {greedy=true;} :
+            // Be greedy here, to favor {x+y} instead of {print +value}
+            (PLUS^ | MINUS^) nls!
+            multiplicativeExpression[0]
+        )*
+    ;
+
+
+// multiplication/division/modulo (level 4)
+multiplicativeExpression[int lc_stmt]
+    :    ( INC^ nls!  powerExpressionNotPlusMinus[0] ((STAR^ | DIV^ | MOD^ )  nls!  powerExpression[0])* )
+    |    ( DEC^ nls!  powerExpressionNotPlusMinus[0] ((STAR^ | DIV^ | MOD^ )  nls!  powerExpression[0])* )
+    |    ( MINUS^ {#MINUS.setType(UNARY_MINUS);} nls!   powerExpressionNotPlusMinus[0] ((STAR^ | DIV^ | MOD^ )  nls!  powerExpression[0])* )
+    |    ( PLUS^ {#PLUS.setType(UNARY_PLUS);} nls!   powerExpressionNotPlusMinus[0] ((STAR^ | DIV^ | MOD^ )  nls!  powerExpression[0])* )
+    |    (  powerExpressionNotPlusMinus[lc_stmt] ((STAR^ | DIV^ | MOD^ )  nls!  powerExpression[0])* )
+    ;
+    
+// math power operator (**) (level 3)
+powerExpression[int lc_stmt]
+    :   unaryExpression[lc_stmt] (STAR_STAR^ nls! unaryExpression[0])*
+    ;
+    
+// math power operator (**) (level 3) 
+// (without ++(prefix)/--(prefix)/+(unary)/-(unary))
+// The different rules are needed to avoid ambigous selection
+// of alternatives. 
+powerExpressionNotPlusMinus[int lc_stmt]
+    :   unaryExpressionNotPlusMinus[lc_stmt] (STAR_STAR^ nls! unaryExpression[0])*
+    ;
+
+// ++(prefix)/--(prefix)/+(unary)/-(unary) (level 2)
+unaryExpression[int lc_stmt]
+    :   INC^ nls! unaryExpression[0]
+    |   DEC^ nls! unaryExpression[0]
+    |   MINUS^   {#MINUS.setType(UNARY_MINUS);}   nls! unaryExpression[0] 
+    |   PLUS^    {#PLUS.setType(UNARY_PLUS);}     nls! unaryExpression[0] 
+    |   unaryExpressionNotPlusMinus[lc_stmt]
+    ;
+
+// ~(BNOT)/!(LNOT)/(type casting) (level 1)
+unaryExpressionNotPlusMinus[int lc_stmt]
+    :   //BAND^    {#BAND.setType(MEMBER_POINTER_DEFAULT);}   nls!  namePart
+    //|
+        BNOT^ nls! unaryExpression[0]
+    |   LNOT^ nls! unaryExpression[0]
+    |   (   // subrule allows option to shut off warnings
+            options {
+                    // "(int" ambig with postfixExpr due to lack of sequence
+                    // info in linear approximate LL(k). It's ok. Shut up.
+                    generateAmbigWarnings=false;
+            }
+        :   // If typecast is built in type, must be numeric operand
+            // Have to backtrack to see if operator follows
+            // FIXME: DECIDE: This syntax is wormy.  Can we deprecate or remove?
+            (LPAREN builtInTypeSpec[true] RPAREN unaryExpression[0])=>
+            lpb:LPAREN^ {#lpb.setType(TYPECAST);} builtInTypeSpec[true] RPAREN!
+            unaryExpression[0]
+
+            // Have to backtrack to see if operator follows. If no operator
+            // follows, it's a typecast. No semantic checking needed to parse.
+            // if it _looks_ like a cast, it _is_ a cast; else it's a "(expr)"
+            // FIXME: DECIDE: This syntax is wormy.  Can we deprecate or remove?
+            // TODO:  Rework this mess for Groovy.
+        |   (LPAREN classTypeSpec[true] RPAREN unaryExpressionNotPlusMinus[0])=>
+            lp:LPAREN^ {#lp.setType(TYPECAST);} classTypeSpec[true] RPAREN!
+            unaryExpressionNotPlusMinus[0]
+
+        |   postfixExpression[lc_stmt]
+        )
+    ;
+
+// qualified names, array expressions, method invocation, post inc/dec
+postfixExpression[int lc_stmt]
+    :
+        pathExpression[lc_stmt]
+        (
+            options {greedy=true;} :
+            // possibly add on a post-increment or post-decrement.
+            // allows INC/DEC on too much, but semantics can check
+            in:INC^ {#in.setType(POST_INC);}
+        |   de:DEC^ {#de.setType(POST_DEC);}
+        )?
+    ;
+    
+// TODO:  Move pathExpression to this point in the file.
+
+// the basic element of an expression
+primaryExpression
+    :   IDENT
+        /*OBS*  //keywords can follow dot in Groovy; no need for this special case
+        ( options {greedy=true;} : DOT^ "class" )?
+        *OBS*/
+    |   constant
+    |   newExpression
+    |   "this"
+    |   "super"
+    |   parenthesizedExpression             // (general stuff...)
+    |   closableBlockConstructorExpression
+    |   listOrMapConstructorExpression
+    |   stringConstructorExpression         // "foo $bar baz"; presented as multiple tokens
+//deprecated    |   scopeEscapeExpression               // $x
+    |   builtInType
+    /*OBS*  //class names work fine as expressions
+            // look for int.class and int[].class
+    |   bt:builtInType!
+        declaratorBrackets[bt]
+        DOT^ nls! "class"
+    *OBS*/
+    ;
+
+// Note:  This is guaranteed to be an EXPR AST.
+// That is, parentheses are preserved, in case the walker cares about them.
+// They are significant sometimes, as in (f(x)){y} vs. f(x){y}.
+parenthesizedExpression { Token first = LT(1); boolean hasClosureList=false; }
+    :   LPAREN! 
+           strictContextExpression
+           (SEMI!  
+             {hasClosureList=true;}
+             (strictContextExpression | { astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT")); })  
+           )*
+        RPAREN!
+        {
+        	if (hasClosureList) {
+        		#parenthesizedExpression = #(create(CLOSURE_LIST,"CLOSURE_LIST",first,LT(1)),#parenthesizedExpression);
+        	}
+        }
+    ;
+
+/** Things that can show up as expressions, but only in strict
+ *  contexts like inside parentheses, argument lists, and list constructors.
+ */
+strictContextExpression  {Token first = LT(1);}
+    :
+        (   (declarationStart)=>
+            singleDeclaration  // used for both binding and value, as: while (String xx = nextln()) { println xx }
+        |   expression[0]
+        |   branchStatement // useful to embed inside expressions (cf. C++ throw)
+        |   annotation      // creates an annotation value
+        )
+        // For the sake of the AST walker, mark nodes like this very clearly.
+        {#strictContextExpression = #(create(EXPR,"EXPR",first,LT(1)),#strictContextExpression);}
+    ;
+    
+assignmentLessExpression  {Token first = LT(1);}
+    :
+        (   conditionalExpression[0]
+        )
+        // For the sake of the AST walker, mark nodes like this very clearly.
+        {#assignmentLessExpression = #(create(EXPR,"EXPR",first,LT(1)),#assignmentLessExpression);}
+    ;
+
+
+closableBlockConstructorExpression
+    :   closableBlock
+    ;
+
+// Groovy syntax for "$x $y" or /$x $y/.
+stringConstructorExpression  {Token first = LT(1);}
+    :   cs:STRING_CTOR_START
+        { #cs.setType(STRING_LITERAL); }
+
+        stringConstructorValuePart
+
+        (   cm:STRING_CTOR_MIDDLE
+            { #cm.setType(STRING_LITERAL); }
+            stringConstructorValuePart
+        )*
+
+        ce:STRING_CTOR_END
+        { #ce.setType(STRING_LITERAL);
+          #stringConstructorExpression =
+            #(create(STRING_CONSTRUCTOR,"STRING_CONSTRUCTOR",first,LT(1)), stringConstructorExpression);
+        }
+    ;
+
+stringConstructorValuePart
+    :
+    (   identifier
+    |   openOrClosableBlock
+    )
+    ;
+
+/**
+ * A list constructor is a argument list enclosed in square brackets, without labels.
+ * Any argument can be decorated with a spread operator (*x), but not a label (a:x).
+ * Examples:  [], [1], [1,2], [1,*l1,2], [*l1,*l2].
+ * (The l1, l2 must be a sequence or null.)
+ * <p>
+ * A map constructor is an argument list enclosed in square brackets, with labels everywhere,
+ * except on spread arguments, which stand for whole maps spliced in.
+ * A colon alone between the brackets also forces the expression to be an empty map constructor.
+ * Examples: [:], [a:1], [a:1,b:2], [a:1,*:m1,b:2], [*:m1,*:m2]
+ * (The m1, m2 must be a map or null.)
+ * Values associated with identical keys overwrite from left to right:
+ * [a:1,a:2]  ===  [a:2]
+ * <p>
+ * Some malformed constructor expressions are not detected in the parser, but in a post-pass.
+ * Bad examples: [1,b:2], [a:1,2], [:1].
+ * (Note that method call arguments, by contrast, can be a mix of keyworded and non-keyworded arguments.)
+ */
+// The parser allows a mix of labeled and unlabeled arguments, but there must be a semantic check that
+// the arguments are all labeled (or SPREAD_MAP_ARG) or all unlabeled (and not SPREAD_MAP_ARG).
+listOrMapConstructorExpression
+        { boolean hasLabels = false; }
+    :   lcon:LBRACK^
+        argList                 { hasLabels |= argListHasLabels;  }  // any argument label implies a map
+        RBRACK!
+        { #lcon.setType(hasLabels ? MAP_CONSTRUCTOR : LIST_CONSTRUCTOR); }
+    |
+        /* Special case:  [:] is an empty map constructor. */
+        emcon:LBRACK^ COLON! RBRACK!   {#emcon.setType(MAP_CONSTRUCTOR);}
+    ;
+
+/*OBS*
+/** Match a, a.b.c refs, a.b.c(...) refs, a.b.c[], a.b.c[].class,
+ *  and a.b.c.class refs. Also this(...) and super(...). Match
+ *  this or super.
+ */
+/*OBS*
+identPrimary
+    :   (ta1:typeArguments!)?
+        IDENT
+        // Syntax for method invocation with type arguments is
+        // <String>foo("blah")
+        (
+            options {
+                // .ident could match here or in postfixExpression.
+                // We do want to match here. Turn off warning.
+                greedy=true;
+                // This turns the ambiguity warning of the second alternative
+                // off. See below. (The "ANTLR_LOOP_EXIT" predicate makes it non-issue)
+                warnWhenFollowAmbig=false;
+            }
+            // we have a new nondeterminism because of
+            // typeArguments... only a syntactic predicate will help...
+            // The problem is that this loop here conflicts with
+            // DOT typeArguments "super" in postfixExpression (k=2)
+            // A proper solution would require a lot of refactoring...
+        :   (DOT (typeArguments)? IDENT) =>
+            DOT^ (ta2:typeArguments!)? IDENT
+        |   {ANTLR_LOOP_EXIT}?  //(see documentation above)
+        )*
+        (
+            options {
+                // ARRAY_DECLARATOR here conflicts with INDEX_OP in
+                // postfixExpression on LBRACK RBRACK.
+                // We want to match [] here, so greedy. This overcomes
+                // limitation of linear approximate lookahead.
+                greedy=true;
+            }
+        :   (   lp:LPAREN^ {#lp.setType(METHOD_CALL);}
+                // if the input is valid, only the last IDENT may
+                // have preceding typeArguments... rather hacky, this is...
+                {if (#ta2 != null) astFactory.addASTChild(currentAST, #ta2);}
+                {if (#ta2 == null) astFactory.addASTChild(currentAST, #ta1);}
+                argList RPAREN!
+            )
+        |   (    options {greedy=true;} :
+                lbc:LBRACK^ {#lbc.setType(ARRAY_DECLARATOR);} RBRACK!
+            )+
+        )?
+    ;
+*OBS*/
+
+/** object instantiation.
+ *  Trees are built as illustrated by the following input/tree pairs:
+ *
+ *  new T()
+ *
+ *  new
+ *   |
+ *   T --  ELIST
+ *                 |
+ *                arg1 -- arg2 -- .. -- argn
+ *
+ *  new int[]
+ *
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR
+ *
+ *  new int[] {1,2}
+ *
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR -- ARRAY_INIT
+ *                                                                |
+ *                                                              EXPR -- EXPR
+ *                                                                |   |
+ *                                                                1       2
+ *
+ *  new int[3]
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR
+ *                              |
+ *                        EXPR
+ *                              |
+ *                              3
+ *
+ *  new int[1][2]
+ *
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR
+ *                         |
+ *               ARRAY_DECLARATOR -- EXPR
+ *                         |                  |
+ *                       EXPR                    1
+ *                         |
+ *                         2
+ *
+ */
+newExpression
+    :   "new"^ nls! (typeArguments)? type
+        (   nls!
+            mca:methodCallArgs[null]!
+
+            (
+                options { greedy=true; }:
+                apb1:appendedBlock[#mca]!
+                { #mca = #apb1; }
+            )?
+
+            {#newExpression.addChild(#mca.getFirstChild());}
+
+        //|  
+        //from blackrag: new Object.f{} matches this part here
+        //and that shouldn't happen unless we decide to support
+        //this kind of Object initialization        
+            //apb:appendedBlock[null]!
+            // FIXME:  This node gets dropped, somehow.
+
+            //{#newExpression.addChild(#apb.getFirstChild());}
+
+            //TODO - NYI* (anonymousInnerClassBlock)? *NYI
+
+            //java 1.1
+            // Note: This will allow bad constructs like
+            //      new int[4][][3] {exp,exp}.
+            //      There needs to be a semantic check here...
+            // to make sure:
+            //   a) [ expr ] and [ ] are not mixed
+            //   b) [ expr ] and an init are not used together
+        |   newArrayDeclarator //(arrayInitializer)?
+            // Groovy does not support Java syntax for initialized new arrays.
+            // Use sequence constructors instead.
+
+        )
+        // DECIDE:  Keep 'new x()' syntax?
+    ;
+
+/*NYI*
+anonymousInnerClassBlock
+    :   classBlock
+    ;
+*NYI*/
+
+argList
+    {
+    	Token first = LT(1); 
+    	Token lastComma = null;
+    	int hls=0, hls2=0; 
+    	boolean hasClosureList=false;
+    	boolean trailingComma=false; 
+   	}
+    :	
+        // Note:  nls not needed, since we are inside parens,
+        // and those insignificant newlines are suppressed by the lexer.
+    	(hls=argument
+    	(( 
+    		(
+    			SEMI! {hasClosureList=true;}
+    			(
+    				strictContextExpression
+    				| { astFactory.addASTChild(currentAST,astFactory.create(EMPTY_STAT, "EMPTY_STAT")); }
+    			)
+    		)+
+    		{#argList = #(create(CLOSURE_LIST,"CLOSURE_LIST",first,LT(1)),#argList);}
+    	) | (	
+    			(   {lastComma = LT(1);}
+        	  		COMMA! 
+        	  		(
+        	  			(hls2=argument {hls |= hls2;})
+        	  			|
+        	  			(
+        	  			 {  if (trailingComma) throw new NoViableAltException(lastComma, getFilename());
+        	  			 	trailingComma=true;
+        	  			 }
+        	  			)
+        	  		)	 
+        	  			
+        		)*
+    	        {#argList = #(create(ELIST,"ELIST",first,LT(1)), argList);}
+	        )
+    	) | (
+			{#argList = create(ELIST,"ELIST",first,LT(1));}
+    	)
+    	)	
+    	{argListHasLabels = (hls&1)!=0; }
+    ;
+
+/** A single argument in (...) or [...].  Corresponds to to a method or closure parameter.
+ *  May be labeled.  May be modified by the spread operator '*' ('*:' for keywords).
+ */
+argument
+returns [byte hasLabelOrSpread = 0]
+    :
+        // Optional argument label.
+        // Usage:  Specifies a map key, or a keyworded argument.
+        (   (argumentLabelStart) =>
+            argumentLabel c:COLON^          {#c.setType(LABELED_ARG);}
+
+            {   hasLabelOrSpread |= 1;  }  // signal to caller the presence of a label
+
+        |   // Spread operator:  f(*[a,b,c])  ===  f(a,b,c);  f(1,*null,2)  ===  f(1,2).
+            sp:STAR^                        {#sp.setType(SPREAD_ARG);}
+            {   hasLabelOrSpread |= 2;  }  // signal to caller the presence of a spread operator
+            // spread maps are marked, as f(*:m) for f(a:x, b:y) if m==[a:x, b:y]
+            (
+                COLON!                      {#sp.setType(SPREAD_MAP_ARG);}
+                { hasLabelOrSpread |= 1; }  // signal to caller the presence of a label
+            )?
+        )?
+
+        strictContextExpression
+        {
+            require(LA(1) != COLON,
+                "illegal colon after argument expression",
+                "a complex label expression before a colon must be parenthesized");
+        }
+    ;
+
+/** A label for an argument is of the form a:b, 'a':b, "a":b, (a):b, etc..
+ *      The labels in (a:b), ('a':b), and ("a":b) are in all ways equivalent,
+ *      except that the quotes allow more spellings.
+ *  Equivalent dynamically computed labels are (('a'):b) and ("${'a'}":b)
+ *  but not ((a):b) or "$a":b, since the latter cases evaluate (a) as a normal identifier.
+ *      Bottom line:  If you want a truly variable label, use parens and say ((a):b).
+ */
+argumentLabel
+    :   (IDENT) =>
+        id:IDENT                  {#id.setType(STRING_LITERAL);}  // identifiers are self-quoting in this context
+    |   (keywordPropertyNames) =>
+        kw:keywordPropertyNames   {#kw.setType(STRING_LITERAL);}  // identifiers are self-quoting in this context
+    |   primaryExpression                                         // dynamic expression
+    ;
+
+/** For lookahead only.  Fast approximate parse of an argumentLabel followed by a colon. */
+argumentLabelStart!
+        // allow number and string literals as labels for maps
+    :   (
+            IDENT | keywordPropertyNames
+        |   constantNumber | STRING_LITERAL
+        |   (LPAREN | STRING_CTOR_START)=> balancedBrackets
+        )
+        COLON
+    ;
+
+newArrayDeclarator
+    :   (
+            // CONFLICT:
+            // newExpression is a primaryExpression which can be
+            // followed by an array index reference. This is ok,
+            // as the generated code will stay in this loop as
+            // long as it sees an LBRACK (proper behavior)
+            options {
+                warnWhenFollowAmbig = false;
+            }
+        :
+            lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);}
+                (expression[0])?
+            RBRACK!
+        )+
+    ;
+
+/** Numeric, string, regexp, boolean, or null constant. */
+constant
+    :   constantNumber
+    |   STRING_LITERAL
+    |   "true"
+    |   "false"
+    |   "null"
+    ;
+
+/** Numeric constant. */
+constantNumber
+    :   NUM_INT
+    |   NUM_FLOAT
+    |   NUM_LONG
+    |   NUM_DOUBLE
+    |   NUM_BIG_INT
+    |   NUM_BIG_DECIMAL
+    ;
+
+/** Fast lookahead across balanced brackets of all sorts. */
+balancedBrackets!
+    :   LPAREN balancedTokens RPAREN
+    |   LBRACK balancedTokens RBRACK
+    |   LCURLY balancedTokens RCURLY
+    |   STRING_CTOR_START balancedTokens STRING_CTOR_END
+    ;
+
+balancedTokens!
+    :   (   balancedBrackets
+        |   ~(LPAREN|LBRACK|LCURLY | STRING_CTOR_START
+             |RPAREN|RBRACK|RCURLY | STRING_CTOR_END)
+        )*
+    ;
+
+/** A statement separator is either a semicolon or a significant newline. 
+ *  Any number of additional (insignificant) newlines may accompany it.
+ */
+//  (All the '!' signs simply suppress the default AST building.)
+//  Returns the type of the separator in this.sepToken, in case it matters.
+sep!
+    :   SEMI!
+        (options { greedy=true; }: NLS!)*
+        { sepToken = SEMI; }
+    |   NLS!                // this newline is significant!
+        { sepToken = NLS; }
+        (
+            options { greedy=true; }:
+            SEMI!           // this superfluous semicolon is gobbled
+            (options { greedy=true; }: NLS!)*
+            { sepToken = SEMI; }
+        )*
+    ;
+
+/** Zero or more insignificant newlines, all gobbled up and thrown away. */
+nls!
+    :
+        (options { greedy=true; }: NLS!)?
+        // Note:  Use '?' rather than '*', relying on the fact that the lexer collapses
+        // adjacent NLS tokens, always.  This lets the parser use its LL(3) lookahead
+        // to "see through" sequences of newlines.  If there were a '*' here, the lookahead
+        // would be weaker, since the parser would have to be prepared for long sequences
+        // of NLS tokens.
+    ;
+
+/** Zero or more insignificant newlines, all gobbled up and thrown away,
+ *  but a warning message is left for the user, if there was a newline.
+ */
+nlsWarn!
+    :
+        (   (NLS)=>
+            { addWarning(
+              "A newline at this point does not follow the Groovy Coding Conventions.",
+              "Keep this statement on one line, or use curly braces to break across multiple lines."
+            ); }
+        )?
+        nls!
+    ;
+
+
+//----------------------------------------------------------------------------
+// The Groovy scanner
+//----------------------------------------------------------------------------
+class GroovyLexer extends Lexer;
+
+options {
+    exportVocab=Groovy;             // call the vocabulary "Groovy"
+    testLiterals=false;             // don't automatically test for literals
+    k=4;                                    // four characters of lookahead
+    charVocabulary='\u0003'..'\uFFFF';
+    // without inlining some bitset tests, couldn't do unicode;
+    // I need to make ANTLR generate smaller bitsets; see
+    // bottom of GroovyLexer.java
+    codeGenBitsetTestThreshold=20;
+}
+
+{
+    /** flag for enabling the "assert" keyword */
+    private boolean assertEnabled = true;
+    /** flag for enabling the "enum" keyword */
+    private boolean enumEnabled = true;
+    /** flag for including whitespace tokens (for IDE preparsing) */
+    private boolean whitespaceIncluded = false;
+
+    /** Enable the "assert" keyword */
+    public void enableAssert(boolean shouldEnable) { assertEnabled = shouldEnable; }
+    /** Query the "assert" keyword state */
+    public boolean isAssertEnabled() { return assertEnabled; }
+    /** Enable the "enum" keyword */
+    public void enableEnum(boolean shouldEnable) { enumEnabled = shouldEnable; }
+    /** Query the "enum" keyword state */
+    public boolean isEnumEnabled() { return enumEnabled; }
+
+    /** Include whitespace tokens.  Note that this breaks the parser.   */
+    public void setWhitespaceIncluded(boolean z) { whitespaceIncluded = z; }
+    /** Are whitespace tokens included? */
+    public boolean isWhitespaceIncluded() { return whitespaceIncluded; }
+
+    {
+        // Initialization actions performed on construction.
+        setTabSize(1);  // get rid of special tab interpretation, for IDEs and general clarity
+    }
+
+    /** Bumped when inside '[x]' or '(x)', reset inside '{x}'.  See ONE_NL.  */
+    protected int parenLevel = 0;
+    protected int suppressNewline = 0;  // be really mean to newlines inside strings
+    protected static final int SCS_TYPE = 3, SCS_VAL = 4, SCS_LIT = 8, SCS_LIMIT = 16;
+    protected static final int SCS_SQ_TYPE = 0, SCS_TQ_TYPE = 1, SCS_RE_TYPE = 2;
+    protected int stringCtorState = 0;  // hack string and regexp constructor boundaries
+    /** Push parenLevel here and reset whenever inside '{x}'. */
+    protected ArrayList parenLevelStack = new ArrayList();
+    protected int lastSigTokenType = EOF;  // last returned non-whitespace token
+
+    protected void pushParenLevel() {
+        parenLevelStack.add(new Integer(parenLevel*SCS_LIMIT + stringCtorState));
+        parenLevel = 0;
+        stringCtorState = 0;
+    }
+    protected void popParenLevel() {
+        int npl = parenLevelStack.size();
+        if (npl == 0)  return;
+        int i = ((Integer) parenLevelStack.remove(--npl)).intValue();
+        parenLevel      = i / SCS_LIMIT;
+        stringCtorState = i % SCS_LIMIT;
+    }
+
+    protected void restartStringCtor(boolean expectLiteral) {
+        if (stringCtorState != 0) {
+            stringCtorState = (expectLiteral? SCS_LIT: SCS_VAL) + (stringCtorState & SCS_TYPE);
+        }
+    }
+    
+    protected boolean allowRegexpLiteral() {
+        return !isExpressionEndingToken(lastSigTokenType);
+    }
+
+    /** Return true for an operator or punctuation which can end an expression.
+     *  Return true for keywords, identifiers, and literals.
+     *  Return true for tokens which can end expressions (right brackets, ++, --).
+     *  Return false for EOF and all other operator and punctuation tokens.
+     *  Used to suppress the recognition of /foo/ as opposed to the simple division operator '/'.
+     */
+    // Cf. 'constant' and 'balancedBrackets' rules in the grammar.)
+    protected static boolean isExpressionEndingToken(int ttype) {
+        switch (ttype) {
+        case INC:               // x++ / y
+        case DEC:               // x-- / y
+        case RPAREN:            // (x) / y
+        case RBRACK:            // f[x] / y
+        case RCURLY:            // f{x} / y
+        case STRING_LITERAL:    // "x" / y
+        case STRING_CTOR_END:   // "$x" / y
+        case NUM_INT:           // 0 / y
+        case NUM_FLOAT:         // 0f / y
+        case NUM_LONG:          // 0l / y
+        case NUM_DOUBLE:        // 0.0 / y
+        case NUM_BIG_INT:       // 0g / y
+        case NUM_BIG_DECIMAL:   // 0.0g / y
+        case IDENT:             // x / y
+        // and a bunch of keywords (all of them; no sense picking and choosing):
+        case LITERAL_as:
+        case LITERAL_assert:
+        case LITERAL_boolean:
+        case LITERAL_break:
+        case LITERAL_byte:
+        case LITERAL_case:
+        case LITERAL_catch:
+        case LITERAL_char:
+        case LITERAL_class:
+        case LITERAL_continue:
+        case LITERAL_def:
+        case LITERAL_default:
+        case LITERAL_double:
+        case LITERAL_else:
+        case LITERAL_enum:
+        case LITERAL_extends:
+        case LITERAL_false:
+        case LITERAL_finally:
+        case LITERAL_float:
+        case LITERAL_for:
+        case LITERAL_if:
+        case LITERAL_implements:
+        case LITERAL_import:
+        case LITERAL_in:
+        case LITERAL_instanceof:
+        case LITERAL_int:
+        case LITERAL_interface:
+        case LITERAL_long:
+        case LITERAL_native:
+        case LITERAL_new:
+        case LITERAL_null:
+        case LITERAL_package:
+        case LITERAL_private:
+        case LITERAL_protected:
+        case LITERAL_public:
+        case LITERAL_return:
+        case LITERAL_short:
+        case LITERAL_static:
+        case LITERAL_super:
+        case LITERAL_switch:
+        case LITERAL_synchronized:
+        case LITERAL_this:
+        case LITERAL_threadsafe:
+        case LITERAL_throw:
+        case LITERAL_throws:
+        case LITERAL_transient:
+        case LITERAL_true:
+        case LITERAL_try:
+        case LITERAL_void:
+        case LITERAL_volatile:
+        case LITERAL_while:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    protected void newlineCheck(boolean check) throws RecognitionException {
+        if (check && suppressNewline > 0) {
+            require(suppressNewline == 0,
+                "end of line reached within a simple string 'x' or \"x\" or /x/",
+                "for multi-line literals, use triple quotes '''x''' or \"\"\"x\"\"\"");
+            suppressNewline = 0;  // shut down any flood of errors
+        }
+        newline();
+    }
+    
+    protected boolean atValidDollarEscape() throws CharStreamException {
+        // '$' (('*')? ('{' | LETTER)) =>
+        int k = 1;
+        char lc = LA(k++);
+        if (lc != '$')  return false;
+        lc = LA(k++);
+        if (lc == '*')  lc = LA(k++);
+        return (lc == '{' || (lc != '$' && Character.isJavaIdentifierStart(lc)));
+    }
+
+    /** This is a bit of plumbing which resumes collection of string constructor bodies,
+     *  after an embedded expression has been parsed.
+     *  Usage:  new GroovyRecognizer(new GroovyLexer(in).plumb()).
+     */
+    public TokenStream plumb() {
+        return new TokenStream() {
+            public Token nextToken() throws TokenStreamException {
+                if (stringCtorState >= SCS_LIT) {
+                    // This goo is modeled upon the ANTLR code for nextToken:
+                    int quoteType = (stringCtorState & SCS_TYPE);
+                    stringCtorState = 0;  // get out of this mode, now
+                    resetText();
+                    try {
+                        switch (quoteType) {
+                        case SCS_SQ_TYPE:
+                            mSTRING_CTOR_END(true, /*fromStart:*/false, false); break;
+                        case SCS_TQ_TYPE:
+                            mSTRING_CTOR_END(true, /*fromStart:*/false, true); break;
+                        case SCS_RE_TYPE:
+                            mREGEXP_CTOR_END(true, /*fromStart:*/false); break;
+                        default:  throw new AssertionError(false);
+                        }
+                        lastSigTokenType = _returnToken.getType();
+                        return _returnToken;
+                    } catch (RecognitionException e) {
+                        throw new TokenStreamRecognitionException(e);
+                    } catch (CharStreamException cse) {
+                        if ( cse instanceof CharStreamIOException ) {
+                            throw new TokenStreamIOException(((CharStreamIOException)cse).io);
+                        }
+                        else {
+                            throw new TokenStreamException(cse.getMessage());
+                        }
+                    }
+                }
+                Token token = GroovyLexer.this.nextToken();
+                int lasttype = token.getType();
+                if (whitespaceIncluded) {
+                    switch (lasttype) {  // filter out insignificant types
+                    case WS:
+                    case ONE_NL:
+                    case SL_COMMENT:
+                    case ML_COMMENT:
+                        lasttype = lastSigTokenType;  // back up!
+                    }
+                }
+                lastSigTokenType = lasttype;
+                return token;
+            }
+        };
+    }
+
+        // stuff to adjust ANTLR's tracing machinery
+    public static boolean tracing = false;  // only effective if antlr.Tool is run with -traceLexer
+    public void traceIn(String rname) throws CharStreamException {
+        if (!GroovyLexer.tracing)  return;
+        super.traceIn(rname);
+    }
+    public void traceOut(String rname) throws CharStreamException {
+        if (!GroovyLexer.tracing)  return;
+        if (_returnToken != null)  rname += tokenStringOf(_returnToken);
+        super.traceOut(rname);
+    }
+    private static java.util.HashMap ttypes;
+    private static String tokenStringOf(Token t) {
+        if (ttypes == null) {
+            java.util.HashMap map = new java.util.HashMap();
+            java.lang.reflect.Field[] fields = GroovyTokenTypes.class.getDeclaredFields();
+            for (int i = 0; i < fields.length; i++) {
+                if (fields[i].getType() != int.class)  continue;
+                try {
+                    map.put(fields[i].get(null), fields[i].getName());
+                } catch (IllegalAccessException ee) {
+                }
+            }
+            ttypes = map;
+        }
+        Integer tt = new Integer(t.getType());
+        Object ttn = ttypes.get(tt);
+        if (ttn == null)  ttn = "<"+tt+">";
+        return "["+ttn+",\""+t.getText()+"\"]";
+    }
+
+    protected GroovyRecognizer parser;  // little-used link; TODO: get rid of
+    private void require(boolean z, String problem, String solution) throws SemanticException {
+        // TODO: Direct to a common error handler, rather than through the parser.
+        if (!z)  parser.requireFailed(problem, solution);
+    }    
+}
+
+// TODO:  Borneo-style ops.
+
+// OPERATORS
+QUESTION          options {paraphrase="'?'";}           :   '?'             ;
+LPAREN            options {paraphrase="'('";}           :   '('             {++parenLevel;};
+RPAREN            options {paraphrase="')'";}           :   ')'             {--parenLevel;};
+LBRACK            options {paraphrase="'['";}           :   '['             {++parenLevel;};
+RBRACK            options {paraphrase="']'";}           :   ']'             {--parenLevel;};
+LCURLY            options {paraphrase="'{'";}           :   '{'             {pushParenLevel();};
+RCURLY            options {paraphrase="'}'";}           :   '}'             {popParenLevel(); if(stringCtorState!=0) restartStringCtor(true);};
+COLON             options {paraphrase="':'";}           :   ':'             ;
+COMMA             options {paraphrase="','";}           :   ','             ;
+DOT               options {paraphrase="'.'";}           :   '.'             ;
+ASSIGN            options {paraphrase="'='";}           :   '='             ;
+COMPARE_TO        options {paraphrase="'<=>'";}         :   "<=>"           ;
+EQUAL             options {paraphrase="'=='";}          :   "=="            ;
+LNOT              options {paraphrase="'!'";}           :   '!'             ;
+BNOT              options {paraphrase="'~'";}           :   '~'             ;
+NOT_EQUAL         options {paraphrase="'!='";}          :   "!="            ;
+protected  //switched from combined rule
+DIV               options {paraphrase="'/'";}           :   '/'             ;
+protected  //switched from combined rule
+DIV_ASSIGN        options {paraphrase="'/='";}          :   "/="            ;
+PLUS              options {paraphrase="'+'";}           :   '+'             ;
+PLUS_ASSIGN       options {paraphrase="'+='";}          :   "+="            ;
+INC               options {paraphrase="'++'";}          :   "++"            ;
+MINUS             options {paraphrase="'-'";}           :   '-'             ;
+MINUS_ASSIGN      options {paraphrase="'-='";}          :   "-="            ;
+DEC               options {paraphrase="'--'";}          :   "--"            ;
+STAR              options {paraphrase="'*'";}           :   '*'             ;
+STAR_ASSIGN       options {paraphrase="'*='";}          :   "*="            ;
+MOD               options {paraphrase="'%'";}           :   '%'             ;
+MOD_ASSIGN        options {paraphrase="'%='";}          :   "%="            ;
+SR                options {paraphrase="'>>'";}          :   ">>"            ;
+SR_ASSIGN         options {paraphrase="'>>='";}         :   ">>="           ;
+BSR               options {paraphrase="'>>>'";}         :   ">>>"           ;
+BSR_ASSIGN        options {paraphrase="'>>>='";}        :   ">>>="          ;
+GE                options {paraphrase="'>='";}          :   ">="            ;
+GT                options {paraphrase="'>'";}           :   ">"             ;
+SL                options {paraphrase="'<<'";}          :   "<<"            ;
+SL_ASSIGN         options {paraphrase="'<<='";}         :   "<<="           ;
+LE                options {paraphrase="'<='";}          :   "<="            ;
+LT                options {paraphrase="'<'";}           :   '<'             ;
+BXOR              options {paraphrase="'^'";}           :   '^'             ;
+BXOR_ASSIGN       options {paraphrase="'^='";}          :   "^="            ;
+BOR               options {paraphrase="'|'";}           :   '|'             ;
+BOR_ASSIGN        options {paraphrase="'|='";}          :   "|="            ;
+LOR               options {paraphrase="'||'";}          :   "||"            ;
+BAND              options {paraphrase="'&'";}           :   '&'             ;
+BAND_ASSIGN       options {paraphrase="'&='";}          :   "&="            ;
+LAND              options {paraphrase="'&&'";}          :   "&&"            ;
+SEMI              options {paraphrase="';'";}           :   ';'             ;
+DOLLAR            options {paraphrase="'$'";}           :   '$'             ;
+RANGE_INCLUSIVE   options {paraphrase="'..'";}          :   ".."            ;
+RANGE_EXCLUSIVE   options {paraphrase="'..<'";}         :   "..<"           ;
+TRIPLE_DOT        options {paraphrase="'...'";}         :   "..."           ;
+SPREAD_DOT        options {paraphrase="'*.'";}          :   "*."            ;
+OPTIONAL_DOT      options {paraphrase="'?.'";}          :   "?."			;
+ELVIS_OPERATOR    options {paraphrase="'?:'";}          :   "?:"			;
+MEMBER_POINTER    options {paraphrase="'.&'";}          :   ".&"            ;
+REGEX_FIND        options {paraphrase="'=~'";}          :   "=~"            ;
+REGEX_MATCH       options {paraphrase="'==~'";}         :   "==~"           ;
+STAR_STAR         options {paraphrase="'**'";}          :   "**"            ;
+STAR_STAR_ASSIGN  options {paraphrase="'**='";}         :   "**="           ;
+CLOSABLE_BLOCK_OP options {paraphrase="'->'";}          :   "->"            ;
+
+// Whitespace -- ignored
+WS
+options {
+    paraphrase="whitespace";
+}
+    :
+        (
+            options { greedy=true; }:
+            ' '
+        |   '\t'
+        |   '\f'
+        |   '\\' ONE_NL[false]
+        )+
+        { if (!whitespaceIncluded)  _ttype = Token.SKIP; }
+    ;
+
+protected
+ONE_NL![boolean check]
+options {
+    paraphrase="a newline";
+}
+ :   // handle newlines, which are significant in Groovy
+        (   options {generateAmbigWarnings=false;}
+        :   "\r\n"  // Evil DOS
+        |   '\r'    // Macintosh
+        |   '\n'    // Unix (the right way)
+        )
+        {
+            // update current line number for error reporting
+            newlineCheck(check);
+        }
+    ;
+        
+// Group any number of newlines (with comments and whitespace) into a single token.
+// This reduces the amount of parser lookahead required to parse around newlines.
+// It is an invariant that the parser never sees NLS tokens back-to-back.
+NLS
+options {
+    paraphrase="some newlines, whitespace or comments";
+}
+    :   ONE_NL[true]
+        (   {!whitespaceIncluded}?
+            (ONE_NL[true] | WS | SL_COMMENT | ML_COMMENT)+
+            // (gobble, gobble)*
+        )?
+        // Inside (...) and [...] but not {...}, ignore newlines.
+        {   if (whitespaceIncluded) {
+                // keep the token as-is
+            } else if (parenLevel != 0) {
+                // when directly inside parens, all newlines are ignored here
+                $setType(Token.SKIP);
+            } else {
+                // inside {...}, newlines must be explicitly matched as 'nls!'
+                $setText("<newline>");
+            }
+        }
+    ;
+
+// Single-line comments
+SL_COMMENT
+options {
+    paraphrase="a single line comment";
+}
+    :   "//"
+        (
+            options {  greedy = true;  }:
+            // '\uffff' means the EOF character.
+            // This will fix the issue GROOVY-766 (infinite loop).
+            ~('\n'|'\r'|'\uffff')
+        )*
+        { if (!whitespaceIncluded)  $setType(Token.SKIP); }
+        //This might be significant, so don't swallow it inside the comment:
+        //ONE_NL
+    ;
+
+// Script-header comments
+SH_COMMENT
+options {
+    paraphrase="a script header";
+}
+    :   {getLine() == 1 && getColumn() == 1}?  "#!"
+        (
+            options {  greedy = true;  }:
+            // '\uffff' means the EOF character.
+            // This will fix the issue GROOVY-766 (infinite loop).
+            ~('\n'|'\r'|'\uffff')
+        )*
+        { if (!whitespaceIncluded)  $setType(Token.SKIP); }
+        //ONE_NL  //Never a significant newline, but might as well separate it.
+    ;
+
+// multiple-line comments
+ML_COMMENT
+options {
+    paraphrase="a comment";
+}
+    :   "/*"
+        (   /*  '\r' '\n' can be matched in one alternative or by matching
+                '\r' in one iteration and '\n' in another. I am trying to
+                handle any flavor of newline that comes in, but the language
+                that allows both "\r\n" and "\r" and "\n" to all be valid
+                newline is ambiguous. Consequently, the resulting grammar
+                must be ambiguous. I'm shutting this warning off.
+             */
+            options {
+                    generateAmbigWarnings=false;
+            }
+        :
+            ( '*' ~'/' ) => '*'
+        |   ONE_NL[true]
+        |   ~('*'|'\n'|'\r'|'\uffff')
+        )*
+        "*/"
+        { if (!whitespaceIncluded)  $setType(Token.SKIP); }
+    ;
+
+
+// string literals
+STRING_LITERAL
+options {
+    paraphrase="a string literal";
+}
+        {int tt=0;}
+    :   ("'''") =>  //...shut off ambiguity warning
+        "'''"!
+        (   STRING_CH | ESC | '"' | '$' | STRING_NL[true]
+        |   ('\'' (~'\'' | '\'' ~'\'')) => '\''  // allow 1 or 2 close quotes
+        )*
+        "'''"!
+    |   '\''!
+                                {++suppressNewline;}
+        (   STRING_CH | ESC | '"' | '$'  )*
+                                {--suppressNewline;}
+        '\''!
+    |   ("\"\"\"") =>  //...shut off ambiguity warning
+        "\"\"\""!
+        tt=STRING_CTOR_END[true, /*tripleQuote:*/ true]
+        {$setType(tt);}
+    |   '"'!
+                                {++suppressNewline;}
+        tt=STRING_CTOR_END[true, /*tripleQuote:*/ false]
+        {$setType(tt);}
+    ;
+
+protected
+STRING_CTOR_END[boolean fromStart, boolean tripleQuote]
+returns [int tt=STRING_CTOR_END]
+options {
+    paraphrase="a string literal end";
+}
+        { boolean dollarOK = false; }
+    :
+        (
+            options {  greedy = true;  }:
+            STRING_CH | ESC | '\'' | STRING_NL[tripleQuote]
+        |   ('"' (~'"' | '"' ~'"'))=> {tripleQuote}? '"'  // allow 1 or 2 close quotes
+        )*
+        (   (   { !tripleQuote }? "\""!
+            |   {  tripleQuote }? "\"\"\""!
+            )
+            {
+                if (fromStart)      tt = STRING_LITERAL;  // plain string literal!
+                if (!tripleQuote)   {--suppressNewline;}
+                // done with string constructor!
+                //assert(stringCtorState == 0);
+            }
+        |   {dollarOK = atValidDollarEscape();}
+            '$'!
+            {
+                require(dollarOK,
+                    "illegal string body character after dollar sign",
+                    "either escape a literal dollar sign \"\\$5\" or bracket the value expression \"${5}\"");
+                // Yes, it's a string constructor, and we've got a value part.
+                tt = (fromStart ? STRING_CTOR_START : STRING_CTOR_MIDDLE);
+                stringCtorState = SCS_VAL + (tripleQuote? SCS_TQ_TYPE: SCS_SQ_TYPE);
+            }
+        )
+        {   $setType(tt);  }
+    ;
+
+protected
+STRING_CH
+options {
+    paraphrase="a string character";
+}
+    :   ~('"'|'\''|'\\'|'$'|'\n'|'\r'|'\uffff')
+    ;
+
+REGEXP_LITERAL
+options {
+    paraphrase="a regular expression literal";
+}
+        {int tt=0;}
+    :   {allowRegexpLiteral()}?
+        '/'!
+        {++suppressNewline;}
+        //Do this, but require it to be non-trivial:  REGEXP_CTOR_END[true]
+        // There must be at least one symbol or $ escape, lest the regexp collapse to '//'.
+        // (This should be simpler, but I don't know how to do it w/o ANTLR warnings vs. '//' comments.)
+        (
+            REGEXP_SYMBOL
+            tt=REGEXP_CTOR_END[true]
+        |   {!atValidDollarEscape()}? '$'
+            tt=REGEXP_CTOR_END[true]
+        |   '$'!
+            {
+                // Yes, it's a regexp constructor, and we've got a value part.
+                tt = STRING_CTOR_START;
+                stringCtorState = SCS_VAL + SCS_RE_TYPE;
+            }
+        )
+        {$setType(tt);}
+
+    |   DIV                 {$setType(DIV);}
+    |   DIV_ASSIGN          {$setType(DIV_ASSIGN);}
+    ;
+
+protected
+REGEXP_CTOR_END[boolean fromStart]
+returns [int tt=STRING_CTOR_END]
+options {
+    paraphrase="a regular expression literal end";
+}
+    :
+        (
+            options {  greedy = true;  }:
+            REGEXP_SYMBOL
+        |
+            {!atValidDollarEscape()}? '$'
+        )*
+        (   '/'!
+            {
+                if (fromStart)      tt = STRING_LITERAL;  // plain regexp literal!
+                {--suppressNewline;}
+                // done with regexp constructor!
+                //assert(stringCtorState == 0);
+            }
+        |   '$'!
+            {
+                // Yes, it's a regexp constructor, and we've got a value part.
+                tt = (fromStart ? STRING_CTOR_START : STRING_CTOR_MIDDLE);
+                stringCtorState = SCS_VAL + SCS_RE_TYPE;
+            }
+        )
+        {   $setType(tt);  }
+    ;
+
+protected
+REGEXP_SYMBOL
+options {
+    paraphrase="a regular expression character";
+}
+    :
+        (
+            ~('*'|'/'|'$'|'\\'|'\n'|'\r'|'\uffff')
+        |   { LA(2)!='/' && LA(2)!='\n' && LA(2)!='\r' }? '\\' // backslash only escapes '/' and EOL
+        |   '\\' '/'                   { $setText('/'); }
+        |!  '\\' ONE_NL[false]         { $setText('\n'); }     // always normalize to newline
+        )
+        ('*')*      // stars handled specially to avoid ambig. on /**/
+    ;
+
+// escape sequence -- note that this is protected; it can only be called
+// from another lexer rule -- it will not ever directly return a token to
+// the parser
+// There are various ambiguities hushed in this rule. The optional
+// '0'...'9' digit matches should be matched here rather than letting
+// them go back to STRING_LITERAL to be matched. ANTLR does the
+// right thing by matching immediately; hence, it's ok to shut off
+// the FOLLOW ambig warnings.
+protected
+ESC
+options {
+    paraphrase="an escape sequence";
+}
+    :   '\\'!
+        (   'n'     {$setText("\n");}
+        |   'r'     {$setText("\r");}
+        |   't'     {$setText("\t");}
+        |   'b'     {$setText("\b");}
+        |   'f'     {$setText("\f");}
+        |   '"'
+        |   '\''
+        |   '\\'
+        |   '$'     //escape Groovy $ operator uniformly also
+        |   ('u')+ {$setText("");}
+            HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
+            {char ch = (char)Integer.parseInt($getText,16); $setText(ch);}
+        |   '0'..'3'
+            (
+                options {
+                    warnWhenFollowAmbig = false;
+                }
+            :   '0'..'7'
+                (
+                    options {
+                        warnWhenFollowAmbig = false;
+                    }
+                :   '0'..'7'
+                )?
+            )?
+            {char ch = (char)Integer.parseInt($getText,8); $setText(ch);}
+        |   '4'..'7'
+            (
+                options {
+                    warnWhenFollowAmbig = false;
+                }
+            :   '0'..'7'
+            )?
+            {char ch = (char)Integer.parseInt($getText,8); $setText(ch);}
+        )
+    |!  '\\' ONE_NL[false]
+    //|!  ONE_NL[true]          { $setText('\n'); }             // always normalize to newline
+    ;
+
+protected 
+STRING_NL[boolean allowNewline]
+options {
+    paraphrase="a newline inside a string";
+}
+    :  {if (!allowNewline) throw new MismatchedCharException('\n', '\n', true, this); } 
+       ONE_NL[false] { $setText('\n'); }
+    ;
+
+
+// hexadecimal digit (again, note it's protected!)
+protected
+HEX_DIGIT
+options {
+    paraphrase="a hexadecimal digit";
+}
+    :   ('0'..'9'|'A'..'F'|'a'..'f')
+    ;
+
+
+// a dummy rule to force vocabulary to be all characters (except special
+// ones that ANTLR uses internally (0 to 2)
+protected
+VOCAB
+options {
+    paraphrase="a character";
+}
+    :   '\3'..'\377'
+    ;
+
+
+// an identifier. Note that testLiterals is set to true! This means
+// that after we match the rule, we look in the literals table to see
+// if it's a literal or really an identifer
+IDENT
+options {
+    paraphrase="an identifier";
+}
+    //options {testLiterals=true;}  // Actually, this is done manually in the actions below.
+    :   LETTER(LETTER|DIGIT)*
+        {
+            if (stringCtorState != 0) {
+                if (LA(1) == '.' && LA(2) != '$' &&
+                        Character.isJavaIdentifierStart(LA(2))) {
+                    // pick up another name component before going literal again:
+                    restartStringCtor(false);
+                } else {
+                    // go back to the string
+                    restartStringCtor(true);
+                }
+            }
+            int ttype = testLiteralsTable(IDENT);
+        /* The grammar allows a few keywords to follow dot.
+         * TODO: Reinstate this logic if we change or remove keywordPropertyNames.
+            if (ttype != IDENT && lastSigTokenType == DOT) {
+                // A few keywords can follow a dot:
+                switch (ttype) {
+                case LITERAL_this: case LITERAL_super: case LITERAL_class:
+                    break;
+                default:
+                    ttype = LITERAL_in;  // the poster child for bad dotted names
+                }
+            }
+        */
+            $setType(ttype);
+
+            // check if "assert" keyword is enabled
+            if (assertEnabled && "assert".equals($getText)) {
+                $setType(LITERAL_assert); // set token type for the rule in the parser
+            }
+            // check if "enum" keyword is enabled
+            if (enumEnabled && "enum".equals($getText)) {
+                $setType(LITERAL_enum); // set token type for the rule in the parser
+            }
+        }
+    ;
+
+protected
+LETTER
+options {
+    paraphrase="a letter";
+}
+    :   'a'..'z'|'A'..'Z'|'\u00C0'..'\u00D6'|'\u00D8'..'\u00F6'|'\u00F8'..'\u00FF'|'\u0100'..'\uFFFE'|'_'
+    // TODO:  Recognize all the Java identifier starts here (except '$').
+    ;
+
+protected
+DIGIT
+options {
+    paraphrase="a digit";
+}
+    :   '0'..'9'
+    // TODO:  Recognize all the Java identifier parts here (except '$').
+    ;
+
+// a numeric literal
+NUM_INT
+options {
+    paraphrase="a numeric literal";
+}
+    {boolean isDecimal=false; Token t=null;}
+    :
+/*OBS*
+        '.' {_ttype = DOT;}
+        (
+            (('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})?
+            {
+                if (t != null && t.getText().toUpperCase().indexOf('F')>=0) {
+                    _ttype = NUM_FLOAT;
+                }
+                else {
+                    _ttype = NUM_DOUBLE; // assume double
+                }
+            })
+        |
+            // JDK 1.5 token for variable length arguments
+            (".." {_ttype = TRIPLE_DOT;})
+        )?
+    |
+*OBS*/
+        // TODO:  This complex pattern seems wrong.  Verify or fix.
+        (   '0' {isDecimal = true;} // special case for just '0'
+            (   ('x'|'X')
+                {isDecimal = false;}
+                (                                                                                   // hex
+                    // the 'e'|'E' and float suffix stuff look
+                    // like hex digits, hence the (...)+ doesn't
+                    // know when to stop: ambig. ANTLR resolves
+                    // it correctly by matching immediately. It
+                    // is therefor ok to hush warning.
+                    options {
+                        warnWhenFollowAmbig=false;
+                    }
+                :   HEX_DIGIT
+                )+
+
+            |   //float or double with leading zero
+                (('0'..'9')+ ('.'('0'..'9')|EXPONENT|FLOAT_SUFFIX)) => ('0'..'9')+
+
+            |   ('0'..'7')+                                                                     // octal
+                {isDecimal = false;}
+            )?
+        |   ('1'..'9') ('0'..'9')*  {isDecimal=true;}               // non-zero decimal
+        )
+        (   ('l'|'L') { _ttype = NUM_LONG; }
+        |   ('i'|'I') { _ttype = NUM_INT; }
+        |   BIG_SUFFIX { _ttype = NUM_BIG_INT; }
+
+        // only check to see if it's a float if looks like decimal so far
+        |
+            (~'.' | '.' ('0'..'9')) =>
+            {isDecimal}?
+            (   '.' ('0'..'9')+ (EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;} | g2:BIG_SUFFIX {t=g2;})?
+            |   EXPONENT (f3:FLOAT_SUFFIX {t=f3;} | g3:BIG_SUFFIX {t=g3;})?
+            |   f4:FLOAT_SUFFIX {t=f4;}
+            )
+            {
+                String txt = (t == null ? "" : t.getText().toUpperCase());
+                if (txt.indexOf('F') >= 0) {
+                    _ttype = NUM_FLOAT;
+                } else if (txt.indexOf('G') >= 0) {
+                    _ttype = NUM_BIG_DECIMAL;
+                } else {
+                    _ttype = NUM_DOUBLE; // assume double
+                }
+            }
+        )?
+    ;
+
+// JDK 1.5 token for annotations and their declarations
+// also a groovy operator for actual field access e.g. 'telson.@age' 
+AT
+options {
+    paraphrase="'@'";
+}
+    :   '@'
+    ;
+
+// a couple protected methods to assist in matching floating point numbers
+protected
+EXPONENT
+options {
+    paraphrase="an exponent";
+}
+    :   ('e'|'E') ('+'|'-')? ('0'..'9')+
+    ;
+
+
+protected
+FLOAT_SUFFIX
+options {
+    paraphrase="a float or double suffix";
+}
+    :   'f'|'F'|'d'|'D'
+    ;
+
+protected
+BIG_SUFFIX
+options {
+    paraphrase="a big decimal suffix";
+}
+    :   'g'|'G'
+    ;
+
+// Note: Please don't use physical tabs.  Logical tabs for indent are width 4.
+// Here's a little hint for you, Emacs:
+// Local Variables:
+// tab-width: 4
+// mode: antlr-mode
+// indent-tabs-mode: nil
+// End:
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/java/Groovifier.java b/groovy/src/main/org/codehaus/groovy/antlr/java/Groovifier.java
new file mode 100644
index 0000000..656ef69
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/java/Groovifier.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.antlr.java;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+import org.codehaus.groovy.antlr.treewalker.VisitorAdapter;
+
+public class Groovifier extends VisitorAdapter implements GroovyTokenTypes {
+    private String[] tokenNames;
+    
+	public Groovifier(String[] tokenNames) {
+		this.tokenNames = tokenNames;
+	}
+	
+    public void visitDefault(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            // only want to do this once per node...
+
+        	// remove 'public' when implied already
+        	if (t.getType() == LITERAL_public) {
+        		t.setType(EXPR);
+        	}
+        	
+        	// constructors are not distinguished from methods in java ast
+        	if (t.getType() == METHOD_DEF) {
+        		String methodName = t.childOfType(IDENT).getText();
+        		if (methodName != null && methodName.length() > 0) {
+        			if (Character.isUpperCase(methodName.charAt(0))) { // todo - replace naive uppercase check for constructors with check for current classname
+        				t.setType(CTOR_IDENT);
+        			}
+        		}
+        	}
+
+        	
+/*        	if (t.getType() == MODIFIERS) {
+       			GroovySourceAST publicNode = t.childOfType(LITERAL_public);
+       			if (t.getNumberOfChildren() > 1 && publicNode != null) {
+       				// has more than one modifier, and one of them is public
+       				
+       				// delete 'public' node
+       				publicNode.setType(EXPR); // near enough the same as delete for now...
+       			}
+        	}*/
+        	// ----        	
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/java/Java2GroovyConverter.java b/groovy/src/main/org/codehaus/groovy/antlr/java/Java2GroovyConverter.java
new file mode 100644
index 0000000..0806255
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/java/Java2GroovyConverter.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.antlr.java;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+import org.codehaus.groovy.antlr.treewalker.VisitorAdapter;
+
+public class Java2GroovyConverter extends VisitorAdapter{
+    private String[] tokenNames;
+    private int[] typeMapping;
+    
+	public Java2GroovyConverter(String[] tokenNames) {
+		this.tokenNames = tokenNames;
+		typeMapping = new int[400]; // magic number, much greater than current number of java tokens
+		typeMapping[JavaTokenTypes.ABSTRACT] = GroovyTokenTypes.ABSTRACT;
+		
+		typeMapping[JavaTokenTypes.EOF] = GroovyTokenTypes.EOF;
+		typeMapping[JavaTokenTypes.NULL_TREE_LOOKAHEAD] = GroovyTokenTypes.NULL_TREE_LOOKAHEAD;
+		typeMapping[JavaTokenTypes.BLOCK] = GroovyTokenTypes.BLOCK;
+		typeMapping[JavaTokenTypes.MODIFIERS] = GroovyTokenTypes.MODIFIERS;
+		typeMapping[JavaTokenTypes.OBJBLOCK] = GroovyTokenTypes.OBJBLOCK;
+		typeMapping[JavaTokenTypes.SLIST] = GroovyTokenTypes.SLIST;
+		typeMapping[JavaTokenTypes.METHOD_DEF] = GroovyTokenTypes.METHOD_DEF;
+		typeMapping[JavaTokenTypes.VARIABLE_DEF] = GroovyTokenTypes.VARIABLE_DEF;
+		typeMapping[JavaTokenTypes.INSTANCE_INIT] = GroovyTokenTypes.INSTANCE_INIT;
+		typeMapping[JavaTokenTypes.STATIC_INIT] = GroovyTokenTypes.STATIC_INIT;
+		typeMapping[JavaTokenTypes.TYPE] = GroovyTokenTypes.TYPE;
+		typeMapping[JavaTokenTypes.CLASS_DEF] = GroovyTokenTypes.CLASS_DEF;
+		typeMapping[JavaTokenTypes.INTERFACE_DEF] = GroovyTokenTypes.INTERFACE_DEF;
+		typeMapping[JavaTokenTypes.PACKAGE_DEF] = GroovyTokenTypes.PACKAGE_DEF;
+		typeMapping[JavaTokenTypes.ARRAY_DECLARATOR] = GroovyTokenTypes.ARRAY_DECLARATOR;
+		typeMapping[JavaTokenTypes.EXTENDS_CLAUSE] = GroovyTokenTypes.EXTENDS_CLAUSE;
+		typeMapping[JavaTokenTypes.IMPLEMENTS_CLAUSE] = GroovyTokenTypes.IMPLEMENTS_CLAUSE;
+		typeMapping[JavaTokenTypes.PARAMETERS] = GroovyTokenTypes.PARAMETERS;
+		typeMapping[JavaTokenTypes.PARAMETER_DEF] = GroovyTokenTypes.PARAMETER_DEF;
+		typeMapping[JavaTokenTypes.LABELED_STAT] = GroovyTokenTypes.LABELED_STAT;
+		typeMapping[JavaTokenTypes.TYPECAST] = GroovyTokenTypes.TYPECAST;
+		typeMapping[JavaTokenTypes.INDEX_OP] = GroovyTokenTypes.INDEX_OP;
+		typeMapping[JavaTokenTypes.POST_INC] = GroovyTokenTypes.POST_INC;
+		typeMapping[JavaTokenTypes.POST_DEC] = GroovyTokenTypes.POST_DEC;
+		typeMapping[JavaTokenTypes.METHOD_CALL] = GroovyTokenTypes.METHOD_CALL;
+		typeMapping[JavaTokenTypes.EXPR] = GroovyTokenTypes.EXPR;
+		typeMapping[JavaTokenTypes.ARRAY_INIT] = GroovyTokenTypes.LIST_CONSTRUCTOR; // this assumes LIST_CONSTRUCTOR set by PreJava2GroovyConvertor
+		typeMapping[JavaTokenTypes.IMPORT] = GroovyTokenTypes.IMPORT;
+		typeMapping[JavaTokenTypes.UNARY_MINUS] = GroovyTokenTypes.UNARY_MINUS;
+		typeMapping[JavaTokenTypes.UNARY_PLUS] = GroovyTokenTypes.UNARY_PLUS;
+		typeMapping[JavaTokenTypes.CASE_GROUP] = GroovyTokenTypes.CASE_GROUP;
+		typeMapping[JavaTokenTypes.ELIST] = GroovyTokenTypes.ELIST;
+		typeMapping[JavaTokenTypes.FOR_INIT] = GroovyTokenTypes.FOR_INIT;
+		typeMapping[JavaTokenTypes.FOR_CONDITION] = GroovyTokenTypes.FOR_CONDITION;
+		typeMapping[JavaTokenTypes.FOR_ITERATOR] = GroovyTokenTypes.FOR_ITERATOR;
+		typeMapping[JavaTokenTypes.EMPTY_STAT] = GroovyTokenTypes.EMPTY_STAT;
+		typeMapping[JavaTokenTypes.FINAL] = GroovyTokenTypes.FINAL;
+		typeMapping[JavaTokenTypes.ABSTRACT] = GroovyTokenTypes.ABSTRACT;
+		typeMapping[JavaTokenTypes.STRICTFP] = GroovyTokenTypes.STRICTFP;
+		typeMapping[JavaTokenTypes.SUPER_CTOR_CALL] = GroovyTokenTypes.SUPER_CTOR_CALL;
+		typeMapping[JavaTokenTypes.CTOR_CALL] = GroovyTokenTypes.CTOR_CALL;
+		typeMapping[JavaTokenTypes.VARIABLE_PARAMETER_DEF] = GroovyTokenTypes.VARIABLE_PARAMETER_DEF;
+		typeMapping[JavaTokenTypes.STATIC_IMPORT] = GroovyTokenTypes.STATIC_IMPORT;
+		typeMapping[JavaTokenTypes.ENUM_DEF] = GroovyTokenTypes.ENUM_DEF;
+		typeMapping[JavaTokenTypes.ENUM_CONSTANT_DEF] = GroovyTokenTypes.ENUM_CONSTANT_DEF;
+		typeMapping[JavaTokenTypes.FOR_EACH_CLAUSE] = GroovyTokenTypes.FOR_EACH_CLAUSE;
+		typeMapping[JavaTokenTypes.ANNOTATION_DEF] = GroovyTokenTypes.ANNOTATION_DEF;
+		typeMapping[JavaTokenTypes.ANNOTATIONS] = GroovyTokenTypes.ANNOTATIONS;
+		typeMapping[JavaTokenTypes.ANNOTATION] = GroovyTokenTypes.ANNOTATION;
+		typeMapping[JavaTokenTypes.ANNOTATION_MEMBER_VALUE_PAIR] = GroovyTokenTypes.ANNOTATION_MEMBER_VALUE_PAIR;
+		typeMapping[JavaTokenTypes.ANNOTATION_FIELD_DEF] = GroovyTokenTypes.ANNOTATION_FIELD_DEF;
+		typeMapping[JavaTokenTypes.ANNOTATION_ARRAY_INIT] = GroovyTokenTypes.ANNOTATION_ARRAY_INIT;
+		typeMapping[JavaTokenTypes.TYPE_ARGUMENTS] = GroovyTokenTypes.TYPE_ARGUMENTS;
+		typeMapping[JavaTokenTypes.TYPE_ARGUMENT] = GroovyTokenTypes.TYPE_ARGUMENT;
+		typeMapping[JavaTokenTypes.TYPE_PARAMETERS] = GroovyTokenTypes.TYPE_PARAMETERS;
+		typeMapping[JavaTokenTypes.TYPE_PARAMETER] = GroovyTokenTypes.TYPE_PARAMETER;
+		typeMapping[JavaTokenTypes.WILDCARD_TYPE] = GroovyTokenTypes.WILDCARD_TYPE;
+		typeMapping[JavaTokenTypes.TYPE_UPPER_BOUNDS] = GroovyTokenTypes.TYPE_UPPER_BOUNDS;
+		typeMapping[JavaTokenTypes.TYPE_LOWER_BOUNDS] = GroovyTokenTypes.TYPE_LOWER_BOUNDS;
+		typeMapping[JavaTokenTypes.LITERAL_package] = GroovyTokenTypes.LITERAL_package;
+		typeMapping[JavaTokenTypes.SEMI] = GroovyTokenTypes.SEMI;
+		typeMapping[JavaTokenTypes.LITERAL_import] = GroovyTokenTypes.LITERAL_import;
+		typeMapping[JavaTokenTypes.LITERAL_static] = GroovyTokenTypes.LITERAL_static;
+		typeMapping[JavaTokenTypes.LBRACK] = GroovyTokenTypes.LBRACK;
+		typeMapping[JavaTokenTypes.RBRACK] = GroovyTokenTypes.RBRACK;
+		typeMapping[JavaTokenTypes.IDENT] = GroovyTokenTypes.IDENT;
+		typeMapping[JavaTokenTypes.DOT] = GroovyTokenTypes.DOT;
+		typeMapping[JavaTokenTypes.QUESTION] = GroovyTokenTypes.QUESTION;
+		typeMapping[JavaTokenTypes.LITERAL_extends] = GroovyTokenTypes.LITERAL_extends;
+		typeMapping[JavaTokenTypes.LITERAL_super] = GroovyTokenTypes.LITERAL_super;
+		typeMapping[JavaTokenTypes.LT] = GroovyTokenTypes.LT;
+		typeMapping[JavaTokenTypes.COMMA] = GroovyTokenTypes.COMMA;
+		typeMapping[JavaTokenTypes.GT] = GroovyTokenTypes.GT;
+		typeMapping[JavaTokenTypes.SR] = GroovyTokenTypes.SR;
+		typeMapping[JavaTokenTypes.BSR] = GroovyTokenTypes.BSR;
+		typeMapping[JavaTokenTypes.LITERAL_void] = GroovyTokenTypes.LITERAL_void;
+		typeMapping[JavaTokenTypes.LITERAL_boolean] = GroovyTokenTypes.LITERAL_boolean;
+		typeMapping[JavaTokenTypes.LITERAL_byte] = GroovyTokenTypes.LITERAL_byte;
+		typeMapping[JavaTokenTypes.LITERAL_char] = GroovyTokenTypes.LITERAL_char;
+		typeMapping[JavaTokenTypes.LITERAL_short] = GroovyTokenTypes.LITERAL_short;
+		typeMapping[JavaTokenTypes.LITERAL_int] = GroovyTokenTypes.LITERAL_int;
+		typeMapping[JavaTokenTypes.LITERAL_float] = GroovyTokenTypes.LITERAL_float;
+		typeMapping[JavaTokenTypes.LITERAL_long] = GroovyTokenTypes.LITERAL_long;
+		typeMapping[JavaTokenTypes.LITERAL_double] = GroovyTokenTypes.LITERAL_double;
+		typeMapping[JavaTokenTypes.STAR] = GroovyTokenTypes.STAR;
+		typeMapping[JavaTokenTypes.LITERAL_private] = GroovyTokenTypes.LITERAL_private;
+		typeMapping[JavaTokenTypes.LITERAL_public] = GroovyTokenTypes.LITERAL_public;
+		typeMapping[JavaTokenTypes.LITERAL_protected] = GroovyTokenTypes.LITERAL_protected;
+		typeMapping[JavaTokenTypes.LITERAL_transient] = GroovyTokenTypes.LITERAL_transient;
+		typeMapping[JavaTokenTypes.LITERAL_native] = GroovyTokenTypes.LITERAL_native;
+		typeMapping[JavaTokenTypes.LITERAL_threadsafe] = GroovyTokenTypes.LITERAL_threadsafe;
+		typeMapping[JavaTokenTypes.LITERAL_synchronized] = GroovyTokenTypes.LITERAL_synchronized;
+		typeMapping[JavaTokenTypes.LITERAL_volatile] = GroovyTokenTypes.LITERAL_volatile;
+		typeMapping[JavaTokenTypes.AT] = GroovyTokenTypes.AT;
+		typeMapping[JavaTokenTypes.LPAREN] = GroovyTokenTypes.LPAREN;
+		typeMapping[JavaTokenTypes.RPAREN] = GroovyTokenTypes.RPAREN;
+		typeMapping[JavaTokenTypes.ASSIGN] = GroovyTokenTypes.ASSIGN;
+		typeMapping[JavaTokenTypes.LCURLY] = GroovyTokenTypes.LCURLY;
+		typeMapping[JavaTokenTypes.RCURLY] = GroovyTokenTypes.RCURLY;
+		typeMapping[JavaTokenTypes.LITERAL_class] = GroovyTokenTypes.LITERAL_class;
+		typeMapping[JavaTokenTypes.LITERAL_interface] = GroovyTokenTypes.LITERAL_interface;
+		typeMapping[JavaTokenTypes.LITERAL_enum] = GroovyTokenTypes.LITERAL_enum;
+		typeMapping[JavaTokenTypes.BAND] = GroovyTokenTypes.BAND;
+		typeMapping[JavaTokenTypes.LITERAL_default] = GroovyTokenTypes.LITERAL_default;
+		typeMapping[JavaTokenTypes.LITERAL_implements] = GroovyTokenTypes.LITERAL_implements;
+		typeMapping[JavaTokenTypes.LITERAL_this] = GroovyTokenTypes.LITERAL_this;
+		typeMapping[JavaTokenTypes.LITERAL_throws] = GroovyTokenTypes.LITERAL_throws;
+		typeMapping[JavaTokenTypes.TRIPLE_DOT] = GroovyTokenTypes.TRIPLE_DOT;
+		typeMapping[JavaTokenTypes.COLON] = GroovyTokenTypes.COLON;
+		typeMapping[JavaTokenTypes.LITERAL_if] = GroovyTokenTypes.LITERAL_if;
+		typeMapping[JavaTokenTypes.LITERAL_else] = GroovyTokenTypes.LITERAL_else;
+		typeMapping[JavaTokenTypes.LITERAL_while] = GroovyTokenTypes.LITERAL_while;
+		typeMapping[JavaTokenTypes.LITERAL_do] = GroovyTokenTypes.LITERAL_while; // warning - do...while... ignored
+		typeMapping[JavaTokenTypes.LITERAL_break] = GroovyTokenTypes.LITERAL_break;
+		typeMapping[JavaTokenTypes.LITERAL_continue] = GroovyTokenTypes.LITERAL_continue;
+		typeMapping[JavaTokenTypes.LITERAL_return] = GroovyTokenTypes.LITERAL_return;
+		typeMapping[JavaTokenTypes.LITERAL_switch] = GroovyTokenTypes.LITERAL_switch;
+		typeMapping[JavaTokenTypes.LITERAL_throw] = GroovyTokenTypes.LITERAL_throw;
+		typeMapping[JavaTokenTypes.LITERAL_assert] = GroovyTokenTypes.LITERAL_assert;
+		typeMapping[JavaTokenTypes.LITERAL_for] = GroovyTokenTypes.LITERAL_for;
+		typeMapping[JavaTokenTypes.LITERAL_case] = GroovyTokenTypes.LITERAL_case;
+		typeMapping[JavaTokenTypes.LITERAL_try] = GroovyTokenTypes.LITERAL_try;
+		typeMapping[JavaTokenTypes.LITERAL_finally] = GroovyTokenTypes.LITERAL_finally;
+		typeMapping[JavaTokenTypes.LITERAL_catch] = GroovyTokenTypes.LITERAL_catch;
+		typeMapping[JavaTokenTypes.PLUS_ASSIGN] = GroovyTokenTypes.PLUS_ASSIGN;
+		typeMapping[JavaTokenTypes.MINUS_ASSIGN] = GroovyTokenTypes.MINUS_ASSIGN;
+		typeMapping[JavaTokenTypes.STAR_ASSIGN] = GroovyTokenTypes.STAR_ASSIGN;
+		typeMapping[JavaTokenTypes.DIV_ASSIGN] = GroovyTokenTypes.DIV_ASSIGN;
+		typeMapping[JavaTokenTypes.MOD_ASSIGN] = GroovyTokenTypes.MOD_ASSIGN;
+		typeMapping[JavaTokenTypes.SR_ASSIGN] = GroovyTokenTypes.SR_ASSIGN;
+		typeMapping[JavaTokenTypes.BSR_ASSIGN] = GroovyTokenTypes.BSR_ASSIGN;
+		typeMapping[JavaTokenTypes.SL_ASSIGN] = GroovyTokenTypes.SL_ASSIGN;
+		typeMapping[JavaTokenTypes.BAND_ASSIGN] = GroovyTokenTypes.BAND_ASSIGN;
+		typeMapping[JavaTokenTypes.BXOR_ASSIGN] = GroovyTokenTypes.BXOR_ASSIGN;
+		typeMapping[JavaTokenTypes.BOR_ASSIGN] = GroovyTokenTypes.BOR_ASSIGN;
+		typeMapping[JavaTokenTypes.LOR] = GroovyTokenTypes.LOR;
+		typeMapping[JavaTokenTypes.LAND] = GroovyTokenTypes.LAND;
+		typeMapping[JavaTokenTypes.BOR] = GroovyTokenTypes.BOR;
+		typeMapping[JavaTokenTypes.BXOR] = GroovyTokenTypes.BXOR;
+		typeMapping[JavaTokenTypes.NOT_EQUAL] = GroovyTokenTypes.NOT_EQUAL;
+		typeMapping[JavaTokenTypes.EQUAL] = GroovyTokenTypes.EQUAL;
+		typeMapping[JavaTokenTypes.LE] = GroovyTokenTypes.LE;
+		typeMapping[JavaTokenTypes.GE] = GroovyTokenTypes.GE;
+		typeMapping[JavaTokenTypes.LITERAL_instanceof] = GroovyTokenTypes.LITERAL_instanceof;
+		typeMapping[JavaTokenTypes.SL] = GroovyTokenTypes.SL;
+		typeMapping[JavaTokenTypes.PLUS] = GroovyTokenTypes.PLUS;
+		typeMapping[JavaTokenTypes.MINUS] = GroovyTokenTypes.MINUS;
+		typeMapping[JavaTokenTypes.DIV] = GroovyTokenTypes.DIV;
+		typeMapping[JavaTokenTypes.MOD] = GroovyTokenTypes.MOD;
+		typeMapping[JavaTokenTypes.INC] = GroovyTokenTypes.INC;
+		typeMapping[JavaTokenTypes.DEC] = GroovyTokenTypes.DEC;
+		typeMapping[JavaTokenTypes.BNOT] = GroovyTokenTypes.BNOT;
+		typeMapping[JavaTokenTypes.LNOT] = GroovyTokenTypes.LNOT;
+		typeMapping[JavaTokenTypes.LITERAL_true] = GroovyTokenTypes.LITERAL_true;
+		typeMapping[JavaTokenTypes.LITERAL_false] = GroovyTokenTypes.LITERAL_false;
+		typeMapping[JavaTokenTypes.LITERAL_null] = GroovyTokenTypes.LITERAL_null;
+		typeMapping[JavaTokenTypes.LITERAL_new] = GroovyTokenTypes.LITERAL_new;
+		typeMapping[JavaTokenTypes.NUM_INT] = GroovyTokenTypes.NUM_INT;
+		typeMapping[JavaTokenTypes.CHAR_LITERAL] = GroovyTokenTypes.STRING_LITERAL; // warning: treating Java chars as "String" in Groovy
+		typeMapping[JavaTokenTypes.STRING_LITERAL] = GroovyTokenTypes.STRING_LITERAL;
+		typeMapping[JavaTokenTypes.NUM_FLOAT] = GroovyTokenTypes.NUM_FLOAT;
+		typeMapping[JavaTokenTypes.NUM_LONG] = GroovyTokenTypes.NUM_LONG;
+		typeMapping[JavaTokenTypes.NUM_DOUBLE] = GroovyTokenTypes.NUM_DOUBLE;
+		typeMapping[JavaTokenTypes.WS] = GroovyTokenTypes.WS;
+		typeMapping[JavaTokenTypes.SL_COMMENT] = GroovyTokenTypes.SL_COMMENT;
+		typeMapping[JavaTokenTypes.ML_COMMENT] = GroovyTokenTypes.ML_COMMENT;
+		typeMapping[JavaTokenTypes.ESC] = GroovyTokenTypes.ESC;
+		typeMapping[JavaTokenTypes.HEX_DIGIT] = GroovyTokenTypes.HEX_DIGIT;
+		typeMapping[JavaTokenTypes.VOCAB] = GroovyTokenTypes.VOCAB;
+		typeMapping[JavaTokenTypes.EXPONENT] = GroovyTokenTypes.EXPONENT;
+		typeMapping[JavaTokenTypes.FLOAT_SUFFIX] = GroovyTokenTypes.FLOAT_SUFFIX;
+	}
+	
+    public void visitDefault(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+        	// only want to do this once per node...
+        	t.setType(typeMapping[t.getType()]);
+           	// ----
+
+        	// need to remove double quotes in string literals
+        	// as groovy AST doesn't expect to have them
+        	if (t.getType() == GroovyTokenTypes.STRING_LITERAL) {
+        		String text = t.getText();
+        		if (isSingleQuoted(text)) {
+        			t.setText(text.substring(1, text.length() - 1)); // chop off the single quotes at start and end
+        		} else if (isDoubleQuoted(text)) {
+        			t.setText(text.substring(1, text.length() - 1)); // chop off the double quotes at start and end
+        		}
+        	}
+        }
+    }
+
+	private boolean isSingleQuoted(String text) {
+		return text != null && text.length() > 2 
+				&& text.charAt(0) == '\'' 
+				&& text.charAt(text.length() - 1) == '\'';
+	}
+	private boolean isDoubleQuoted(String text) {
+		return text != null && text.length() > 2 
+				&& text.charAt(0) == '"' 
+				&& text.charAt(text.length() - 1) == '"';
+	}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/java/Java2GroovyMain.java b/groovy/src/main/org/codehaus/groovy/antlr/java/Java2GroovyMain.java
new file mode 100644
index 0000000..37f3276
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/java/Java2GroovyMain.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.java;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.PosixParser;
+import org.codehaus.groovy.antlr.AntlrASTProcessor;
+import org.codehaus.groovy.antlr.SourceBuffer;
+import org.codehaus.groovy.antlr.UnicodeEscapingReader;
+import org.codehaus.groovy.antlr.parser.GroovyLexer;
+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
+import org.codehaus.groovy.antlr.treewalker.*;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import antlr.collections.AST;
+
+public class Java2GroovyMain {
+
+	public static void main(String[] args) {
+		try{
+			Options options = new Options();
+			PosixParser cliParser = new PosixParser();
+			CommandLine cli = cliParser.parse(options, args);
+            String[] filenames = cli.getArgs();
+            if( filenames.length == 0 ) {
+            	System.err.println("Needs at least one filename");
+            }
+            List filenameList = Arrays.asList(filenames);
+            Iterator i = filenameList.iterator();
+            while (i.hasNext()) {
+            	String filename = (String) i.next();
+            	File f = new File(filename);
+            	String text = DefaultGroovyMethods.getText(f);
+            	System.out.println(convert(filename, text, true, true));
+            }
+		} catch (Throwable t) {
+			t.printStackTrace();
+		}
+	}
+
+	public static String convert(String filename, String input) throws Exception{
+		return convert(filename, input, false, false);
+	}
+	
+	public static String convert(String filename, String input,boolean withHeader, boolean withNewLines) throws Exception{
+        JavaRecognizer parser = getJavaParser(input);
+        String[] tokenNames = parser.getTokenNames();
+        parser.compilationUnit();
+        AST ast = parser.getAST();
+        
+        // output AST in format suitable for opening in http://freemind.sourceforge.net
+        // which is a really nice way of seeing the AST, folding nodes etc
+        if ("mindmap".equals(System.getProperty("antlr.ast"))) {
+            try {
+                PrintStream out = new PrintStream(new FileOutputStream(filename + ".mm"));
+                Visitor visitor = new MindMapPrinter(out,tokenNames);
+                AntlrASTProcessor treewalker = new PreOrderTraversal(visitor);
+                treewalker.process(ast);
+            } catch (FileNotFoundException e) {
+                System.out.println("Cannot create " + filename + ".mm");
+            }
+        }
+        
+        // modify the Java AST into a Groovy AST
+        modifyJavaASTintoGroovyAST(tokenNames, ast);
+        String[] groovyTokenNames = getGroovyTokenNames(input);
+        // groovify the fat Java-Like Groovy AST
+        groovifyFatJavaLikeGroovyAST(ast, groovyTokenNames);
+
+        // now output        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        Visitor visitor = new SourcePrinter(new PrintStream(baos),groovyTokenNames, withNewLines);
+        AntlrASTProcessor traverser = new SourceCodeTraversal(visitor);
+
+        traverser.process(ast);
+        
+        String header = "";
+        if (withHeader) {
+	        header = "/*\n" +
+	        				"  Automatically Converted from Java Source \n" +
+	        				"  \n" +
+	        				"  by java2groovy v0.0.1   Copyright Jeremy Rayner 2007\n" +
+	        				"  \n" +
+	        				"  !! NOT FIT FOR ANY PURPOSE !! \n" +
+	        				"  'java2groovy' cannot be used to convert one working program into another" +
+	        				"  */\n\n";
+        }
+        return header + new String(baos.toByteArray());
+    }
+
+	/**
+	 * @param ast
+	 * @param groovyTokenNames
+	 */
+	private static void groovifyFatJavaLikeGroovyAST(AST ast, String[] groovyTokenNames) {
+		Visitor groovifier = new Groovifier(groovyTokenNames);
+        AntlrASTProcessor groovifierTraverser = new PreOrderTraversal(groovifier);
+        groovifierTraverser.process(ast);
+	}
+
+	/**
+	 * @param tokenNames
+	 * @param ast
+	 */
+	private static void modifyJavaASTintoGroovyAST(String[] tokenNames, AST ast) {
+		// mutate the tree when in Javaland
+		Visitor preJava2groovyConverter = new PreJava2GroovyConverter(tokenNames);
+		AntlrASTProcessor preJava2groovyTraverser = new PreOrderTraversal(preJava2groovyConverter);
+		preJava2groovyTraverser.process(ast);
+
+        // map the nodes to Groovy types
+        Visitor java2groovyConverter = new Java2GroovyConverter(tokenNames);
+        AntlrASTProcessor java2groovyTraverser = new PreOrderTraversal(java2groovyConverter);
+        java2groovyTraverser.process(ast);
+	}
+
+	/**
+	 * @param input
+	 * @return
+	 */
+	private static JavaRecognizer getJavaParser(String input) {
+		JavaRecognizer parser = null;
+        SourceBuffer sourceBuffer = new SourceBuffer();
+        UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(new StringReader(input),sourceBuffer);
+        JavaLexer lexer = new JavaLexer(unicodeReader);
+        unicodeReader.setLexer(lexer);
+        parser = JavaRecognizer.make(lexer);
+        parser.setSourceBuffer(sourceBuffer);
+		return parser;
+	}
+
+	public static String mindmap(String input) throws Exception{
+        JavaRecognizer parser = getJavaParser(input);
+        String[] tokenNames = parser.getTokenNames();
+        parser.compilationUnit();
+        AST ast = parser.getAST();
+        // modify the Java AST into a Groovy AST
+        modifyJavaASTintoGroovyAST(tokenNames, ast);
+        String[] groovyTokenNames = getGroovyTokenNames(input);
+        // groovify the fat Java-Like Groovy AST
+        groovifyFatJavaLikeGroovyAST(ast, groovyTokenNames);
+
+        // now output        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        Visitor visitor = new MindMapPrinter(new PrintStream(baos),groovyTokenNames);
+        AntlrASTProcessor traverser = new SourceCodeTraversal(visitor);
+
+        traverser.process(ast);
+        
+        return new String(baos.toByteArray());
+    }
+
+	public static String nodePrinter(String input) throws Exception{
+        JavaRecognizer parser = getJavaParser(input);
+        String[] tokenNames = parser.getTokenNames();
+        parser.compilationUnit();
+        AST ast = parser.getAST();
+        // modify the Java AST into a Groovy AST
+        modifyJavaASTintoGroovyAST(tokenNames, ast);
+        String[] groovyTokenNames = getGroovyTokenNames(input);
+        // groovify the fat Java-Like Groovy AST
+        groovifyFatJavaLikeGroovyAST(ast, groovyTokenNames);
+
+        // now output        
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        Visitor visitor = new NodePrinter(new PrintStream(baos),groovyTokenNames);
+        AntlrASTProcessor traverser = new SourceCodeTraversal(visitor);
+
+        traverser.process(ast);
+        
+        return new String(baos.toByteArray());
+    }
+
+	private static String[] getGroovyTokenNames(String input) {
+        GroovyRecognizer groovyParser = null;
+        SourceBuffer groovySourceBuffer = new SourceBuffer();
+        UnicodeEscapingReader groovyUnicodeReader = new UnicodeEscapingReader(new StringReader(input),groovySourceBuffer);
+        GroovyLexer groovyLexer = new GroovyLexer(groovyUnicodeReader);
+        groovyUnicodeReader.setLexer(groovyLexer);
+        groovyParser = GroovyRecognizer.make(groovyLexer);
+        return groovyParser.getTokenNames();
+	}
+	
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/java/PreJava2GroovyConverter.java b/groovy/src/main/org/codehaus/groovy/antlr/java/PreJava2GroovyConverter.java
new file mode 100644
index 0000000..0f66573
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/java/PreJava2GroovyConverter.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.antlr.java;
+
+import java.util.Stack;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+import org.codehaus.groovy.antlr.treewalker.VisitorAdapter;
+
+/** This class mutates the Java AST, whilst it is still a Java AST, in readiness for conversion to Groovy, yippee-ky-a ! */
+public class PreJava2GroovyConverter extends VisitorAdapter{
+    private String[] tokenNames;
+    private Stack stack;
+
+	public PreJava2GroovyConverter(String[] tokenNames) {
+		this.tokenNames = tokenNames;
+		this.stack = new Stack();
+	}
+	
+    public void visitDefault(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+        	if (t.getType() == JavaTokenTypes.LITERAL_do) {
+        		visitJavaLiteralDo(t);        		
+        	} else if (t.getType() == JavaTokenTypes.ARRAY_INIT) {
+        		visitJavaArrayInit(t);
+        	}
+        }
+    }
+    
+    private void visitJavaLiteralDo(GroovySourceAST t) {
+    	// todo - incomplete, as body of do...while... should be executed at least once, which this doesn't provide.
+    	swapTwoChildren(t);        		    	
+    }
+    /**
+     * <pre>
+     * String[] myArray = new String[] {"a","b","c"};
+     * 
+     * becomes
+     * 
+     * String[] myArray = ["a", "b", "c"]
+     * 
+     * ---
+     * 
+     * To convert node (t) and surrounding nodes into the right structure for List Constructor
+     * 
+     * (a) java/EXPR
+     *  |
+     *  +- (b) java/new
+     *      |
+     *      + (t) java/ARRAY_INIT
+     *    
+     *  becomes
+     *  
+     * (a) groovy/LIST_CONSTRUCTOR (via ARRAY_INIT as temporary marker type)
+     *  |
+     *  +- (t) groovy/ELIST
+     *  
+     *  * note: node (b) is thrown away...
+     * </pre>
+     */
+	private void visitJavaArrayInit(GroovySourceAST t) {
+		// given that we might have a grandParent...
+		if (stack.size() > 2) {
+			GroovySourceAST grandParent = getGrandParentNode();
+			if (grandParent.getType() == JavaTokenTypes.EXPR) {
+				grandParent.setType(JavaTokenTypes.ARRAY_INIT); //set type as indicator for Java2GroovyConvertor to turn into LIST_CONSTRUCTOR
+				grandParent.setFirstChild(t);
+				t.setType(JavaTokenTypes.ELIST);
+			}
+		}
+	}
+
+	/** To swap two children of node t...
+	 * 
+	 *<pre>
+	 *   (t)
+	 *    |
+	 *    |
+	 *   (a) -- (b)
+	 * 
+	 * t.down = firstNode
+	 * a.right = b
+	 * b.right = null
+	 *</pre> 
+	 * becomes
+	 *<pre>
+	 *   (t)
+	 *    |
+	 *    |
+	 *   (b) -- (a)
+	 *   
+	 * t.down = b
+	 * a.right = null
+	 * b.right = a
+	 *</pre>
+	 *
+	 * todo - build API of basic tree mutations like this method.
+	 */
+    public void swapTwoChildren(GroovySourceAST t) {
+		// this swaps the two child nodes, see javadoc above for explanation of implementation
+		GroovySourceAST a = (GroovySourceAST) t.getFirstChild();
+		GroovySourceAST b = (GroovySourceAST) a.getNextSibling();
+
+		t.setFirstChild(b);
+		a.setNextSibling(null);
+		b.setNextSibling(a);
+    }
+    
+    
+    
+    
+    public void push(GroovySourceAST t) {
+        stack.push(t);
+    }
+    public GroovySourceAST pop() {
+        if (!stack.empty()) {
+            return (GroovySourceAST) stack.pop();
+        }
+        return null;
+    }
+
+    private GroovySourceAST getParentNode() {
+        Object currentNode = stack.pop();
+        Object parentNode = stack.peek();
+        stack.push(currentNode);
+        return (GroovySourceAST) parentNode;
+    }
+
+    private GroovySourceAST getGrandParentNode() {
+        Object currentNode = stack.pop();
+        Object parentNode = stack.pop();
+        Object grandParentNode = stack.peek();
+        stack.push(parentNode);
+        stack.push(currentNode);
+        return (GroovySourceAST) grandParentNode;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/java/java.g b/groovy/src/main/org/codehaus/groovy/antlr/java/java.g
new file mode 100644
index 0000000..43c95e3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/java/java.g
@@ -0,0 +1,1962 @@
+// Note: Please don't use physical tabs.  Logical tabs for indent are width 4.
+header {
+package org.codehaus.groovy.antlr.java;
+import org.codehaus.groovy.antlr.*;
+import org.codehaus.groovy.antlr.parser.*;
+import java.util.*;
+import java.io.InputStream;
+import java.io.Reader;
+import antlr.InputBuffer;
+import antlr.LexerSharedInputState;
+}
+ 
+/** Java 1.5 Recognizer
+ *
+ * Run 'java Main [-showtree] directory-full-of-java-files'
+ *
+ * [The -showtree option pops up a Swing frame that shows
+ *  the AST constructed from the parser.]
+ *
+ * Run 'java Main <directory full of java files>'
+ *
+ * Contributing authors:
+ *      Jeremy Rayner       groovy@ross-rayner.com
+ *		John Mitchell		johnm@non.net
+ *		Terence Parr		parrt@magelang.com
+ *		John Lilley		jlilley@empathy.com
+ *		Scott Stanchfield	thetick@magelang.com
+ *		Markus Mohnen		mohnen@informatik.rwth-aachen.de
+ *		Peter Williams		pete.williams@sun.com
+ *		Allan Jacobs		Allan.Jacobs@eng.sun.com
+ *		Steve Messick		messick@redhills.com
+ *		John Pybus		john@pybus.org
+ *
+ * Version 1.00 December 9, 1997 -- initial release
+ * Version 1.01 December 10, 1997
+ *		fixed bug in octal def (0..7 not 0..8)
+ * Version 1.10 August 1998 (parrt)
+ *		added tree construction
+ *		fixed definition of WS,comments for mac,pc,unix newlines
+ *		added unary plus
+ * Version 1.11 (Nov 20, 1998)
+ *		Added "shutup" option to turn off last ambig warning.
+ *		Fixed inner class def to allow named class defs as statements
+ *		synchronized requires compound not simple statement
+ *		add [] after builtInType DOT class in primaryExpression
+ *		"const" is reserved but not valid..removed from modifiers
+ * Version 1.12 (Feb 2, 1999)
+ *		Changed LITERAL_xxx to xxx in tree grammar.
+ *		Updated java.g to use tokens {...} now for 2.6.0 (new feature).
+ *
+ * Version 1.13 (Apr 23, 1999)
+ *		Didn't have (stat)? for else clause in tree parser.
+ *		Didn't gen ASTs for interface extends.  Updated tree parser too.
+ *		Updated to 2.6.0.
+ * Version 1.14 (Jun 20, 1999)
+ *		Allowed final/abstract on local classes.
+ *		Removed local interfaces from methods
+ *		Put instanceof precedence where it belongs...in relationalExpr
+ *			It also had expr not type as arg; fixed it.
+ *		Missing ! on SEMI in classBlock
+ *		fixed: (expr) + "string" was parsed incorrectly (+ as unary plus).
+ *		fixed: didn't like Object[].class in parser or tree parser
+ * Version 1.15 (Jun 26, 1999)
+ *		Screwed up rule with instanceof in it. :(  Fixed.
+ *		Tree parser didn't like (expr).something; fixed.
+ *		Allowed multiple inheritance in tree grammar. oops.
+ * Version 1.16 (August 22, 1999)
+ *		Extending an interface built a wacky tree: had extra EXTENDS.
+ *		Tree grammar didn't allow multiple superinterfaces.
+ *		Tree grammar didn't allow empty var initializer: {}
+ * Version 1.17 (October 12, 1999)
+ *		ESC lexer rule allowed 399 max not 377 max.
+ *		java.tree.g didn't handle the expression of synchronized
+ *		statements.
+ * Version 1.18 (August 12, 2001)
+ *	  	Terence updated to Java 2 Version 1.3 by
+ *		observing/combining work of Allan Jacobs and Steve
+ *		Messick.  Handles 1.3 src.  Summary:
+ *		o  primary didn't include boolean.class kind of thing
+ *	  	o  constructor calls parsed explicitly now:
+ * 		   see explicitConstructorInvocation
+ *		o  add strictfp modifier
+ *	  	o  missing objBlock after new expression in tree grammar
+ *		o  merged local class definition alternatives, moved after declaration
+ *		o  fixed problem with ClassName.super.field
+ *	  	o  reordered some alternatives to make things more efficient
+ *		o  long and double constants were not differentiated from int/float
+ *		o  whitespace rule was inefficient: matched only one char
+ *		o  add an examples directory with some nasty 1.3 cases
+ *		o  made Main.java use buffered IO and a Reader for Unicode support
+ *		o  supports UNICODE?
+ *		   Using Unicode charVocabulay makes code file big, but only
+ *		   in the bitsets at the end. I need to make ANTLR generate
+ *		   unicode bitsets more efficiently.
+ * Version 1.19 (April 25, 2002)
+ *		Terence added in nice fixes by John Pybus concerning floating
+ *		constants and problems with super() calls.  John did a nice
+ *		reorg of the primary/postfix expression stuff to read better
+ *		and makes f.g.super() parse properly (it was METHOD_CALL not
+ *		a SUPER_CTOR_CALL).  Also:
+ *
+ *		o  "finally" clause was a root...made it a child of "try"
+ *		o  Added stuff for asserts too for Java 1.4, but *commented out*
+ *		   as it is not backward compatible.
+ *
+ * Version 1.20 (October 27, 2002)
+ *
+ *	  Terence ended up reorging John Pybus' stuff to
+ *	  remove some nondeterminisms and some syntactic predicates.
+ *	  Note that the grammar is stricter now; e.g., this(...) must
+ *	be the first statement.
+ *
+ *	  Trinary ?: operator wasn't working as array name:
+ *		  (isBig ? bigDigits : digits)[i];
+ *
+ *	  Checked parser/tree parser on source for
+ *		  Resin-2.0.5, jive-2.1.1, jdk 1.3.1, Lucene, antlr 2.7.2a4,
+ *		and the 110k-line jGuru server source.
+ *
+ * Version 1.21 (October 17, 2003)
+ *  Fixed lots of problems including:
+ *  Ray Waldin: add typeDefinition to interfaceBlock in java.tree.g
+ *  He found a problem/fix with floating point that start with 0
+ *  Ray also fixed problem that (int.class) was not recognized.
+ *  Thorsten van Ellen noticed that \n are allowed incorrectly in strings.
+ *  TJP fixed CHAR_LITERAL analogously.
+ *
+ * Version 1.21.2 (March, 2003)
+ *	  Changes by Matt Quail to support generics (as per JDK1.5/JSR14)
+ *	  Notes:
+ *	  o We only allow the "extends" keyword and not the "implements"
+ *		keyword, since thats what JSR14 seems to imply.
+ *	  o Thanks to Monty Zukowski for his help on the antlr-interest
+ *		mail list.
+ *	  o Thanks to Alan Eliasen for testing the grammar over his
+ *		Fink source base
+ *
+ * Version 1.22 (July, 2004)
+ *	  Changes by Michael Studman to support Java 1.5 language extensions
+ *	  Notes:
+ *	  o Added support for annotations types
+ *	  o Finished off Matt Quail's generics enhancements to support bound type arguments
+ *	  o Added support for new for statement syntax
+ *	  o Added support for static import syntax
+ *	  o Added support for enum types
+ *	  o Tested against JDK 1.5 source base and source base of jdigraph project
+ *	  o Thanks to Matt Quail for doing the hard part by doing most of the generics work
+ *
+ * Version 1.22.1 (July 28, 2004)
+ *	  Bug/omission fixes for Java 1.5 language support
+ *	  o Fixed tree structure bug with classOrInterface - thanks to Pieter Vangorpto for
+ *		spotting this
+ *	  o Fixed bug where incorrect handling of SR and BSR tokens would cause type
+ *		parameters to be recognised as type arguments.
+ *	  o Enabled type parameters on constructors, annotations on enum constants
+ *		and package definitions
+ *	  o Fixed problems when parsing if ((char.class.equals(c))) {} - solution by Matt Quail at Cenqua
+ *
+ * Version 1.22.2 (July 28, 2004)
+ *	  Slight refactoring of Java 1.5 language support
+ *	  o Refactored for/"foreach" productions so that original literal "for" literal
+ *	    is still used but the for sub-clauses vary by token type
+ *	  o Fixed bug where type parameter was not included in generic constructor's branch of AST
+ *
+ * Version 1.22.3 (August 26, 2004)
+ *	  Bug fixes as identified by Michael Stahl; clean up of tabs/spaces
+ *        and other refactorings
+ *	  o Fixed typeParameters omission in identPrimary and newStatement
+ *	  o Replaced GT reconcilliation code with simple semantic predicate
+ *	  o Adapted enum/assert keyword checking support from Michael Stahl's java15 grammar
+ *	  o Refactored typeDefinition production and field productions to reduce duplication
+ *
+ * Version 1.22.4 (October 21, 2004)
+ *    Small bux fixes
+ *    o Added typeArguments to explicitConstructorInvocation, e.g. new <String>MyParameterised()
+ *    o Added typeArguments to postfixExpression productions for anonymous inner class super
+ *      constructor invocation, e.g. new Outer().<String>super()
+ *    o Fixed bug in array declarations identified by Geoff Roy
+ *
+ * Version 1.22.4.j.1
+ *	  Changes by Jeremy Rayner to support java2groovy tool
+ *    o I have taken java.g for Java1.5 from Michael Studman (1.22.4)
+ *      and have made some changes to enable use by java2groovy tool (Jan 2007)
+ *
+ * This grammar is in the PUBLIC DOMAIN
+ */
+
+class JavaRecognizer extends Parser;
+options {
+	k = 2;							// two token lookahead
+	exportVocab=Java;				// Call its vocabulary "Java"
+	codeGenMakeSwitchThreshold = 2;	// Some optimizations
+	codeGenBitsetTestThreshold = 3;
+	defaultErrorHandler = false;	// Don't generate parser error handlers
+	buildAST = true;
+}
+
+tokens {
+	BLOCK; MODIFIERS; OBJBLOCK; SLIST; METHOD_DEF; VARIABLE_DEF;
+	INSTANCE_INIT; STATIC_INIT; TYPE; CLASS_DEF; INTERFACE_DEF;
+	PACKAGE_DEF; ARRAY_DECLARATOR; EXTENDS_CLAUSE; IMPLEMENTS_CLAUSE;
+	PARAMETERS; PARAMETER_DEF; LABELED_STAT; TYPECAST; INDEX_OP;
+	POST_INC; POST_DEC; METHOD_CALL; EXPR; ARRAY_INIT;
+	IMPORT; UNARY_MINUS; UNARY_PLUS; CASE_GROUP; ELIST; FOR_INIT; FOR_CONDITION;
+	FOR_ITERATOR; EMPTY_STAT; FINAL="final"; ABSTRACT="abstract";
+	STRICTFP="strictfp"; SUPER_CTOR_CALL; CTOR_CALL; VARIABLE_PARAMETER_DEF;
+	STATIC_IMPORT; ENUM_DEF; ENUM_CONSTANT_DEF; FOR_EACH_CLAUSE; ANNOTATION_DEF; ANNOTATIONS;
+	ANNOTATION; ANNOTATION_MEMBER_VALUE_PAIR; ANNOTATION_FIELD_DEF; ANNOTATION_ARRAY_INIT;
+	TYPE_ARGUMENTS; TYPE_ARGUMENT; TYPE_PARAMETERS; TYPE_PARAMETER; WILDCARD_TYPE;
+	TYPE_UPPER_BOUNDS; TYPE_LOWER_BOUNDS;
+}
+
+{
+    /** This factory is the correct way to wire together a Groovy parser and lexer. */
+    public static JavaRecognizer make(JavaLexer lexer) {
+        JavaRecognizer parser = new JavaRecognizer(lexer.plumb());
+        // TODO: set up a common error-handling control block, to avoid excessive tangle between these guys
+        parser.lexer = lexer;
+        lexer.parser = parser;
+        parser.setASTNodeClass("org.codehaus.groovy.antlr.GroovySourceAST");
+        return parser;
+    }
+    // Create a scanner that reads from the input stream passed to us...
+    public static JavaRecognizer make(InputStream in) { return make(new JavaLexer(in)); }
+    public static JavaRecognizer make(Reader in) { return make(new JavaLexer(in)); }
+    public static JavaRecognizer make(InputBuffer in) { return make(new JavaLexer(in)); }
+    public static JavaRecognizer make(LexerSharedInputState in) { return make(new JavaLexer(in)); }
+    
+    private static GroovySourceAST dummyVariableToforceClassLoaderToFindASTClass = new GroovySourceAST();
+    
+    JavaLexer lexer;
+    public JavaLexer getLexer() { return lexer; }
+    public void setFilename(String f) { super.setFilename(f); lexer.setFilename(f); }
+    private SourceBuffer sourceBuffer;
+    public void setSourceBuffer(SourceBuffer sourceBuffer) {
+        this.sourceBuffer = sourceBuffer;
+    }
+
+    /** Create an AST node with the token type and text passed in, but
+     *  with the same background information as another supplied Token (e.g. line numbers)
+     * to be used in place of antlr tree construction syntax,
+     * i.e. #[TOKEN,"text"]  becomes  create(TOKEN,"text",anotherToken)
+     *
+     * todo - change antlr.ASTFactory to do this instead...
+     */
+    public AST create(int type, String txt, Token first, Token last) {
+        AST t = astFactory.create(type,txt);
+        if ( t != null && first != null) {
+            // first copy details from first token
+            t.initialize(first);
+            // then ensure that type and txt are specific to this new node
+            t.initialize(type,txt);
+        }
+
+        if ((t instanceof GroovySourceAST) && last != null) {
+            GroovySourceAST node = (GroovySourceAST)t;
+            node.setLast(last);
+            // This is a good point to call node.setSnippet(),
+            // but it bulks up the AST too much for production code.
+        }
+        return t;
+    }
+
+    
+    /**
+	 * Counts the number of LT seen in the typeArguments production.
+	 * It is used in semantic predicates to ensure we have seen
+	 * enough closing '>' characters; which actually may have been
+	 * either GT, SR or BSR tokens.
+	 */
+	private int ltCounter = 0;
+}
+
+// Compilation Unit: In Java, this is a single file. This is the start
+// rule for this parser
+compilationUnit
+	:	// A compilation unit starts with an optional package definition
+		(	(annotations "package")=> packageDefinition
+		|	/* nothing */
+		)
+
+		// Next we have a series of zero or more import statements
+		( importDefinition )*
+
+		// Wrapping things up with any number of class or interface
+		// definitions
+		( typeDefinition )*
+
+		EOF!
+	;
+
+
+// Package statement: optional annotations followed by "package" then the package identifier.
+packageDefinition
+	options {defaultErrorHandler = true;} // let ANTLR handle errors
+	:	annotations p:"package"^ {#p.setType(PACKAGE_DEF);} identifier SEMI!
+	;
+
+
+// Import statement: import followed by a package or class name
+importDefinition
+	options {defaultErrorHandler = true;}
+	{ boolean isStatic = false; }
+	:	i:"import"^ {#i.setType(IMPORT);} ( "static"! {#i.setType(STATIC_IMPORT);} )? identifierStar SEMI!
+	;
+
+// A type definition is either a class, interface, enum or annotation with possible additional semis.
+typeDefinition
+	options {defaultErrorHandler = true;}
+	:	m:modifiers!
+		typeDefinitionInternal[#m]
+	|	SEMI!
+	;
+
+// Protected type definitions production for reuse in other productions
+protected typeDefinitionInternal[AST mods]
+	:	classDefinition[#mods]		// inner class
+	|	interfaceDefinition[#mods]	// inner interface
+	|	enumDefinition[#mods]		// inner enum
+	|	annotationDefinition[#mods]	// inner annotation
+	;
+
+// A declaration is the creation of a reference or primitive-type variable
+// Create a separate Type/Var tree for each var in the var list.
+declaration!
+	:	m:modifiers t:typeSpec[false] v:variableDefinitions[#m,#t]
+		{#declaration = #v;}
+	;
+
+// A type specification is a type name with possible brackets afterwards
+// (which would make it an array type).
+typeSpec[boolean addImagNode]
+	:	classTypeSpec[addImagNode]
+	|	builtInTypeSpec[addImagNode]
+	;
+
+// A class type specification is a class type with either:
+// - possible brackets afterwards
+//   (which would make it an array type).
+// - generic type arguments after
+classTypeSpec[boolean addImagNode]  {Token first = LT(1);}
+	:	classOrInterfaceType[false]
+		(options{greedy=true;}: // match as many as possible
+			lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!
+		)*
+		{
+			if ( addImagNode ) {
+				#classTypeSpec = #(create(TYPE,"TYPE",first,LT(1)), #classTypeSpec);
+			}
+		}
+	;
+
+// A non-built in type name, with possible type parameters
+classOrInterfaceType[boolean addImagNode]  {Token first = LT(1);}
+	:	IDENT^ (typeArguments)?
+		(options{greedy=true;}: // match as many as possible
+			DOT^
+			IDENT (typeArguments)?
+		)*
+		{
+			if ( addImagNode ) {
+				#classOrInterfaceType = #(create(TYPE,"TYPE",first,LT(1)), #classOrInterfaceType);
+			}
+		}
+	;
+
+// A specialised form of typeSpec where built in types must be arrays
+typeArgumentSpec
+	:	classTypeSpec[true]
+	|	builtInTypeArraySpec[true]
+	;
+
+// A generic type argument is a class type, a possibly bounded wildcard type or a built-in type array
+typeArgument  {Token first = LT(1);}
+	:	(	typeArgumentSpec
+		|	wildcardType
+		)
+		{#typeArgument = #(create(TYPE_ARGUMENT,"TYPE_ARGUMENT",first,LT(1)), #typeArgument);}
+	;
+
+// Wildcard type indicating all types (with possible constraint)
+wildcardType
+	:	q:QUESTION^ {#q.setType(WILDCARD_TYPE);}
+		(("extends" | "super")=> typeArgumentBounds)?
+	;
+
+// Type arguments to a class or interface type
+typeArguments
+{int currentLtLevel = 0;  Token first = LT(1);}
+	:
+		{currentLtLevel = ltCounter;}
+		LT! {ltCounter++;}
+		typeArgument
+		(options{greedy=true;}: // match as many as possible
+			{inputState.guessing !=0 || ltCounter == currentLtLevel + 1}?
+			COMMA! typeArgument
+		)*
+
+		(	// turn warning off since Antlr generates the right code,
+			// plus we have our semantic predicate below
+			options{generateAmbigWarnings=false;}:
+			typeArgumentsOrParametersEnd
+		)?
+
+		// make sure we have gobbled up enough '>' characters
+		// if we are at the "top level" of nested typeArgument productions
+		{(currentLtLevel != 0) || ltCounter == currentLtLevel}?
+
+		{#typeArguments = #(create(TYPE_ARGUMENTS,"TYPE_ARGUMENTS",first,LT(1)), #typeArguments);}
+	;
+
+// this gobbles up *some* amount of '>' characters, and counts how many
+// it gobbled.
+protected typeArgumentsOrParametersEnd
+	:	GT! {ltCounter-=1;}
+	|	SR! {ltCounter-=2;}
+	|	BSR! {ltCounter-=3;}
+	;
+
+// Restriction on wildcard types based on super class or derrived class
+typeArgumentBounds
+	{boolean isUpperBounds = false;  Token first = LT(1);}
+	:
+		( "extends"! {isUpperBounds=true;} | "super"! ) classOrInterfaceType[false]
+		{
+			if (isUpperBounds)
+			{
+				#typeArgumentBounds = #(create(TYPE_UPPER_BOUNDS,"TYPE_UPPER_BOUNDS",first,LT(1)), #typeArgumentBounds);
+			}
+			else
+			{
+				#typeArgumentBounds = #(create(TYPE_LOWER_BOUNDS,"TYPE_LOWER_BOUNDS",first,LT(1)), #typeArgumentBounds);
+			}
+		}
+	;
+
+// A builtin type array specification is a builtin type with brackets afterwards
+builtInTypeArraySpec[boolean addImagNode]  {Token first = LT(1);}
+	:	builtInType
+		(options{greedy=true;}: // match as many as possible
+			lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!
+		)+
+
+		{
+			if ( addImagNode ) {
+				#builtInTypeArraySpec = #(create(TYPE,"TYPE",first,LT(1)), #builtInTypeArraySpec);
+			}
+		}
+	;
+
+// A builtin type specification is a builtin type with possible brackets
+// afterwards (which would make it an array type).
+builtInTypeSpec[boolean addImagNode]  {Token first = LT(1);}
+	:	builtInType
+		(options{greedy=true;}: // match as many as possible
+			lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!
+		)*
+		{
+			if ( addImagNode ) {
+				#builtInTypeSpec = #(create(TYPE,"TYPE",first,LT(1)), #builtInTypeSpec);
+			}
+		}
+	;
+
+// A type name. which is either a (possibly qualified and parameterized)
+// class name or a primitive (builtin) type
+type
+	:	classOrInterfaceType[false]
+	|	builtInType
+	;
+
+// The primitive types.
+builtInType
+	:	"void"
+	|	"boolean"
+	|	"byte"
+	|	"char"
+	|	"short"
+	|	"int"
+	|	"float"
+	|	"long"
+	|	"double"
+	;
+
+// A (possibly-qualified) java identifier. We start with the first IDENT
+// and expand its name by adding dots and following IDENTS
+identifier
+	:	IDENT ( DOT^ IDENT )*
+	;
+
+identifierStar
+	:	IDENT
+		( DOT^ IDENT )*
+		( DOT^ STAR )?
+	;
+
+// A list of zero or more modifiers. We could have used (modifier)* in
+// place of a call to modifiers, but I thought it was a good idea to keep
+// this rule separate so they can easily be collected in a Vector if
+// someone so desires
+modifiers {Token first = LT(1);}
+	:
+		(
+			//hush warnings since the semantic check for "@interface" solves the non-determinism
+			options{generateAmbigWarnings=false;}:
+
+			modifier
+			|
+			//Semantic check that we aren't matching @interface as this is not an annotation
+			//A nicer way to do this would be nice
+			{LA(1)==AT && !LT(2).getText().equals("interface")}? annotation
+		)*
+
+		{#modifiers = #(create(MODIFIERS, "MODIFIERS",first,LT(1)), #modifiers);}
+	;
+
+// modifiers for Java classes, interfaces, class/instance vars and methods
+modifier
+	:	"private"
+	|	"public"
+	|	"protected"
+	|	"static"
+	|	"transient"
+	|	"final"
+	|	"abstract"
+	|	"native"
+	|	"threadsafe"
+	|	"synchronized"
+	|	"volatile"
+	|	"strictfp"
+	;
+
+annotation!  {Token first = LT(1);}
+	:	AT! i:identifier ( LPAREN! ( args:annotationArguments )? RPAREN! )?
+		{#annotation = #(create(ANNOTATION,"ANNOTATION",first,LT(1)), i, args);}
+	;
+
+annotations  {Token first = LT(1);}
+    :   (annotation)*
+		{#annotations = #([ANNOTATIONS, "ANNOTATIONS"], #annotations);}
+    ;
+
+annotationArguments
+	:	annotationMemberValueInitializer | anntotationMemberValuePairs
+	;
+
+anntotationMemberValuePairs
+	:	annotationMemberValuePair ( COMMA! annotationMemberValuePair )*
+	;
+
+annotationMemberValuePair!  {Token first = LT(1);}
+	:	i:IDENT ASSIGN! v:annotationMemberValueInitializer
+		{#annotationMemberValuePair = #(create(ANNOTATION_MEMBER_VALUE_PAIR,"ANNOTATION_MEMBER_VALUE_PAIR",first,LT(1)), i, v);}
+	;
+
+annotationMemberValueInitializer
+	:
+		conditionalExpression | annotation | annotationMemberArrayInitializer
+	;
+
+// This is an initializer used to set up an annotation member array.
+annotationMemberArrayInitializer
+	:	lc:LCURLY^ {#lc.setType(ANNOTATION_ARRAY_INIT);}
+			(	annotationMemberArrayValueInitializer
+				(
+					// CONFLICT: does a COMMA after an initializer start a new
+					// initializer or start the option ',' at end?
+					// ANTLR generates proper code by matching
+					// the comma as soon as possible.
+					options {
+						warnWhenFollowAmbig = false;
+					}
+				:
+					COMMA! annotationMemberArrayValueInitializer
+				)*
+				(COMMA!)?
+			)?
+		RCURLY!
+	;
+
+// The two things that can initialize an annotation array element are a conditional expression
+// and an annotation (nested annotation array initialisers are not valid)
+annotationMemberArrayValueInitializer
+	:	conditionalExpression
+	|	annotation
+	;
+
+superClassClause!  {Token first = LT(1);}
+	:	( "extends" c:classOrInterfaceType[false] )?
+		{#superClassClause = #(create(EXTENDS_CLAUSE,"EXTENDS_CLAUSE",first,LT(1)),c);}
+	;
+
+// Definition of a Java class
+classDefinition![AST modifiers] {Token first = LT(1);}
+	:	"class" IDENT
+		// it _might_ have type paramaters
+		(tp:typeParameters)?
+		// it _might_ have a superclass...
+		sc:superClassClause
+		// it might implement some interfaces...
+		ic:implementsClause
+		// now parse the body of the class
+		cb:classBlock
+		{#classDefinition = #(create(CLASS_DEF,"CLASS_DEF",first,LT(1)),
+								modifiers,IDENT,tp,sc,ic,cb);}
+	;
+
+// Definition of a Java Interface
+interfaceDefinition![AST modifiers]  {Token first = LT(1);}
+	:	"interface" IDENT
+		// it _might_ have type paramaters
+		(tp:typeParameters)?
+		// it might extend some other interfaces
+		ie:interfaceExtends
+		// now parse the body of the interface (looks like a class...)
+		ib:interfaceBlock
+		{#interfaceDefinition = #(create(INTERFACE_DEF,"INTERFACE_DEF",first,LT(1)),
+									modifiers,IDENT,tp,ie,ib);}
+	;
+
+enumDefinition![AST modifiers]  {Token first = LT(1);}
+	:	"enum" IDENT
+		// it might implement some interfaces...
+		ic:implementsClause
+		// now parse the body of the enum
+		eb:enumBlock
+		{#enumDefinition = #(create(ENUM_DEF,"ENUM_DEF",first,LT(1)),
+								modifiers,IDENT,ic,eb);}
+	;
+
+annotationDefinition![AST modifiers]  {Token first = LT(1);}
+	:	AT "interface" IDENT
+		// now parse the body of the annotation
+		ab:annotationBlock
+		{#annotationDefinition = #(create(ANNOTATION_DEF,"ANNOTATION_DEF",first,LT(1)),
+									modifiers,IDENT,ab);}
+	;
+
+typeParameters
+{int currentLtLevel = 0; Token first = LT(1);}
+	:
+		{currentLtLevel = ltCounter;}
+		LT! {ltCounter++;}
+		typeParameter (COMMA! typeParameter)*
+		(typeArgumentsOrParametersEnd)?
+
+		// make sure we have gobbled up enough '>' characters
+		// if we are at the "top level" of nested typeArgument productions
+		{(currentLtLevel != 0) || ltCounter == currentLtLevel}?
+
+		{#typeParameters = #(create(TYPE_PARAMETERS,"TYPE_PARAMETERS",first,LT(1)), #typeParameters);}
+	;
+
+typeParameter   {Token first = LT(1);}
+	:
+		// I'm pretty sure Antlr generates the right thing here:
+		(id:IDENT) ( options{generateAmbigWarnings=false;}: typeParameterBounds )?
+		{#typeParameter = #(create(TYPE_PARAMETER,"TYPE_PARAMETER",first,LT(1)), #typeParameter);}
+	;
+
+typeParameterBounds  {Token first = LT(1);}
+	:
+		"extends"! classOrInterfaceType[false]
+		(BAND! classOrInterfaceType[false])*
+		{#typeParameterBounds = #(create(TYPE_UPPER_BOUNDS,"TYPE_UPPER_BOUNDS",first,LT(1)), #typeParameterBounds);}
+	;
+
+// This is the body of a class. You can have classFields and extra semicolons.
+classBlock
+	:	LCURLY!
+			( classField | SEMI! )*
+		RCURLY!
+		{#classBlock = #([OBJBLOCK, "OBJBLOCK"], #classBlock);}
+	;
+
+// This is the body of an interface. You can have interfaceField and extra semicolons.
+interfaceBlock
+	:	LCURLY!
+			( interfaceField | SEMI! )*
+		RCURLY!
+		{#interfaceBlock = #([OBJBLOCK, "OBJBLOCK"], #interfaceBlock);}
+	;
+	
+// This is the body of an annotation. You can have annotation fields and extra semicolons,
+// That's about it (until you see what an annoation field is...)
+annotationBlock
+	:	LCURLY!
+		( annotationField | SEMI! )*
+		RCURLY!
+		{#annotationBlock = #([OBJBLOCK, "OBJBLOCK"], #annotationBlock);}
+	;
+
+// This is the body of an enum. You can have zero or more enum constants
+// followed by any number of fields like a regular class
+enumBlock
+	:	LCURLY!
+			( enumConstant ( options{greedy=true;}: COMMA! enumConstant )* ( COMMA! )? )?
+			( SEMI! ( classField | SEMI! )* )?
+		RCURLY!
+		{#enumBlock = #([OBJBLOCK, "OBJBLOCK"], #enumBlock);}
+	;
+
+// An annotation field
+annotationField!  {Token first = LT(1);}
+	:	mods:modifiers
+		(	td:typeDefinitionInternal[#mods]
+			{#annotationField = #td;}
+		|	t:typeSpec[false]		// annotation field
+			(	i:IDENT				// the name of the field
+
+				LPAREN! RPAREN!
+
+				rt:declaratorBrackets[#t]
+
+				( "default" amvi:annotationMemberValueInitializer )?
+
+				SEMI
+
+				{#annotationField =
+					#(create(ANNOTATION_FIELD_DEF,"ANNOTATION_FIELD_DEF",first,LT(1)),
+						 mods,
+						 #(create(TYPE,"TYPE",first,LT(1)),rt),
+						 i,amvi
+						 );}
+			|	v:variableDefinitions[#mods,#t] SEMI	// variable
+				{#annotationField = #v;}
+			)
+		)
+	;
+
+//An enum constant may have optional parameters and may have a
+//a class body
+enumConstant!
+	:	an:annotations
+		i:IDENT
+		(	LPAREN!
+			a:argList
+			RPAREN!
+		)?
+		( b:enumConstantBlock )?
+		{#enumConstant = #([ENUM_CONSTANT_DEF, "ENUM_CONSTANT_DEF"], an, i, a, b);}
+	;
+
+//The class-like body of an enum constant
+enumConstantBlock
+	:	LCURLY!
+		( enumConstantField | SEMI! )*
+		RCURLY!
+		{#enumConstantBlock = #([OBJBLOCK, "OBJBLOCK"], #enumConstantBlock);}
+	;
+
+//An enum constant field is just like a class field but without
+//the posibility of a constructor definition or a static initializer
+enumConstantField! {Token first = LT(1);}
+	:	mods:modifiers
+		(	td:typeDefinitionInternal[#mods]
+			{#enumConstantField = #td;}
+
+		|	// A generic method has the typeParameters before the return type.
+			// This is not allowed for variable definitions, but this production
+			// allows it, a semantic check could be used if you wanted.
+			(tp:typeParameters)? t:typeSpec[false]		// method or variable declaration(s)
+			(	IDENT									// the name of the method
+
+				// parse the formal parameter declarations.
+				LPAREN! param:parameterDeclarationList RPAREN!
+
+				rt:declaratorBrackets[#t]
+
+				// get the list of exceptions that this method is
+				// declared to throw
+				(tc:throwsClause)?
+
+				( s2:compoundStatement | SEMI )
+				{#enumConstantField = #(create(METHOD_DEF,"METHOD_DEF",first,LT(1)),
+							 mods,
+							 tp,
+							 #(create(TYPE,"TYPE",first,LT(1)),rt),
+							 IDENT,
+							 param,
+							 tc,
+							 s2);}
+			|	v:variableDefinitions[#mods,#t] SEMI
+				{#enumConstantField = #v;}
+			)
+		)
+
+	// "{ ... }" instance initializer
+	|	s4:compoundStatement
+		{#enumConstantField = #(create(INSTANCE_INIT,"INSTANCE_INIT",first,LT(1)), s4);}
+	;
+
+// An interface can extend several other interfaces...
+interfaceExtends  {Token first = LT(1);}
+	:	(
+		e:"extends"!
+		classOrInterfaceType[false] ( COMMA! classOrInterfaceType[false] )*
+		)?
+		{#interfaceExtends = #(create(EXTENDS_CLAUSE,"EXTENDS_CLAUSE",first,LT(1)),
+								#interfaceExtends);}
+	;
+
+// A class can implement several interfaces...
+implementsClause  {Token first = LT(1);}
+	:	(
+			i:"implements"! classOrInterfaceType[false] ( COMMA! classOrInterfaceType[false] )*
+		)?
+		{#implementsClause = #(create(IMPLEMENTS_CLAUSE,"IMPLEMENTS_CLAUSE",first,LT(1)),
+								 #implementsClause);}
+	;
+
+// Now the various things that can be defined inside a class
+classField!   {Token first = LT(1);}
+	:	// method, constructor, or variable declaration
+		mods:modifiers
+		(	td:typeDefinitionInternal[#mods]
+			{#classField = #td;}
+
+		|	(tp:typeParameters)?
+			(
+				h:ctorHead s:constructorBody // constructor
+				// just treat CTOR_DEF like METHOD_DEF for java2groovy
+				{#classField = #(create(METHOD_DEF,"METHOD_DEF",first,LT(1)), mods, tp, h, s);}
+
+				|	// A generic method/ctor has the typeParameters before the return type.
+					// This is not allowed for variable definitions, but this production
+					// allows it, a semantic check could be used if you wanted.
+					t:typeSpec[false]		// method or variable declaration(s)
+					(	IDENT				// the name of the method
+
+						// parse the formal parameter declarations.
+						LPAREN! param:parameterDeclarationList RPAREN!
+
+						rt:declaratorBrackets[#t]
+
+						// get the list of exceptions that this method is
+						// declared to throw
+						(tc:throwsClause)?
+
+						( s2:compoundStatement | SEMI )
+						{#classField = #(create(METHOD_DEF,"METHOD_DEF",first,LT(1)),
+									 mods,
+									 tp,
+									 #(create(TYPE,"TYPE",first,LT(1)),rt),
+									 IDENT,
+									 param,
+									 tc,
+									 s2);}
+					|	v:variableDefinitions[#mods,#t] SEMI
+						{#classField = #v;}
+					)
+			)
+		)
+
+	// "static { ... }" class initializer
+	|	"static" s3:compoundStatement
+		{#classField = #(create(STATIC_INIT,"STATIC_INIT",first,LT(1)), s3);}
+
+	// "{ ... }" instance initializer
+	|	s4:compoundStatement
+		{#classField = #(create(INSTANCE_INIT,"INSTANCE_INIT",first,LT(1)), s4);}
+	;
+
+// Now the various things that can be defined inside a interface
+interfaceField!    {Token first = LT(1);}
+	:	// method, constructor, or variable declaration
+		mods:modifiers
+		(	td:typeDefinitionInternal[#mods]
+			{#interfaceField = #td;}
+
+		|	(tp:typeParameters)?
+			// A generic method has the typeParameters before the return type.
+			// This is not allowed for variable definitions, but this production
+			// allows it, a semantic check could be used if you want a more strict
+			// grammar.
+			t:typeSpec[false]		// method or variable declaration(s)
+			(	IDENT				// the name of the method
+
+				// parse the formal parameter declarations.
+				LPAREN! param:parameterDeclarationList RPAREN!
+
+				rt:declaratorBrackets[#t]
+
+				// get the list of exceptions that this method is
+				// declared to throw
+				(tc:throwsClause)?
+
+				SEMI
+				
+				{#interfaceField = #(create(METHOD_DEF,"METHOD_DEF",first,LT(1)),
+							 mods,
+							 tp,
+							 #(create(TYPE,"TYPE",first,LT(1)),rt),
+							 IDENT,
+							 param,
+							 tc);}
+			|	v:variableDefinitions[#mods,#t] SEMI
+				{#interfaceField = #v;}
+			)
+		)
+	;
+
+constructorBody
+	:	lc:LCURLY^ {#lc.setType(SLIST);}
+			( options { greedy=true; } : explicitConstructorInvocation)?
+			(statement)*
+		RCURLY!
+	;
+
+/** Catch obvious constructor calls, but not the expr.super(...) calls */
+explicitConstructorInvocation
+	:	(typeArguments)?
+		(	"this"! lp1:LPAREN^ argList RPAREN! SEMI!
+			{#lp1.setType(CTOR_CALL);}
+		|	"super"! lp2:LPAREN^ argList RPAREN! SEMI!
+			{#lp2.setType(SUPER_CTOR_CALL);}
+		)
+	;
+
+variableDefinitions[AST mods, AST t]
+	:	variableDeclarator[getASTFactory().dupTree(mods),
+							getASTFactory().dupTree(t)]
+		(	COMMA!
+			variableDeclarator[getASTFactory().dupTree(mods),
+							getASTFactory().dupTree(t)]
+		)*
+	;
+
+/** Declaration of a variable. This can be a class/instance variable,
+ *  or a local variable in a method
+ *  It can also include possible initialization.
+ */
+variableDeclarator![AST mods, AST t] { Token first = LT(1);}
+	:	id:IDENT d:declaratorBrackets[t] v:varInitializer
+		{#variableDeclarator = #(create(VARIABLE_DEF,"VARIABLE_DEF",first,LT(1)), mods, #(create(TYPE,"TYPE",first,LT(1)),d), id, v);}
+	;
+
+declaratorBrackets[AST typ]
+	:	{#declaratorBrackets=typ;}
+		(lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
+	;
+
+varInitializer
+	:	( ASSIGN^ initializer )?
+	;
+
+// This is an initializer used to set up an array.
+arrayInitializer
+	:	lc:LCURLY^ {#lc.setType(ARRAY_INIT);}
+			(	initializer
+				(
+					// CONFLICT: does a COMMA after an initializer start a new
+					// initializer or start the option ',' at end?
+					// ANTLR generates proper code by matching
+					// the comma as soon as possible.
+					options {
+						warnWhenFollowAmbig = false;
+					}
+				:
+					COMMA! initializer
+				)*
+				(COMMA!)?
+			)?
+		RCURLY!
+	;
+
+// The two "things" that can initialize an array element are an expression
+// and another (nested) array initializer.
+initializer
+	:	expression
+	|	arrayInitializer
+	;
+
+// This is the header of a method. It includes the name and parameters
+// for the method.
+// This also watches for a list of exception classes in a "throws" clause.
+ctorHead
+	:	IDENT // the name of the method
+
+		// parse the formal parameter declarations.
+		LPAREN! parameterDeclarationList RPAREN!
+
+		// get the list of exceptions that this method is declared to throw
+		(throwsClause)?
+	;
+
+// This is a list of exception classes that the method is declared to throw
+throwsClause
+	:	"throws"^ identifier ( COMMA! identifier )*
+	;
+
+// A list of formal parameters
+//	 Zero or more parameters
+//	 If a parameter is variable length (e.g. String... myArg) it is the right-most parameter
+parameterDeclarationList {Token first = LT(1);}
+	// The semantic check in ( .... )* block is flagged as superfluous, and seems superfluous but
+	// is the only way I could make this work. If my understanding is correct this is a known bug
+	:	(	( parameterDeclaration )=> parameterDeclaration
+			( options {warnWhenFollowAmbig=false;} : ( COMMA! parameterDeclaration ) => COMMA! parameterDeclaration )*
+			( COMMA! variableLengthParameterDeclaration )?
+		|
+			variableLengthParameterDeclaration
+		)?
+		{#parameterDeclarationList = #(create(PARAMETERS,"PARAMETERS",first,LT(1)),
+                						#parameterDeclarationList);}
+	;
+
+// A formal parameter.
+parameterDeclaration! {Token first = LT(1);}
+	:	pm:parameterModifier t:typeSpec[false] id:IDENT
+		pd:declaratorBrackets[#t]
+		{#parameterDeclaration = #(create(PARAMETER_DEF,"PARAMETER_DEF",first,LT(1)),
+									pm, #(create(TYPE,"TYPE",first,LT(1)),pd), id);}
+	;
+
+variableLengthParameterDeclaration!  {Token first = LT(1);}
+	:	pm:parameterModifier t:typeSpec[false] TRIPLE_DOT! id:IDENT
+		pd:declaratorBrackets[#t]
+		{#variableLengthParameterDeclaration = #(create(VARIABLE_PARAMETER_DEF,"VARIABLE_PARAMETER_DEF",first,LT(1)),
+												pm, #(create(TYPE,"TYPE",first,LT(1)),pd), id);}
+	;
+
+parameterModifier  {Token first = LT(1);}
+	//final can appear amongst annotations in any order - greedily consume any preceding
+	//annotations to shut nond-eterminism warnings off
+	:	(options{greedy=true;} : annotation)* (f:"final")? (annotation)*
+		{#parameterModifier = #(create(MODIFIERS,"MODIFIERS",first,LT(1)), #parameterModifier);}
+	;
+
+// Compound statement. This is used in many contexts:
+// Inside a class definition prefixed with "static":
+// it is a class initializer
+// Inside a class definition without "static":
+// it is an instance initializer
+// As the body of a method
+// As a completely indepdent braced block of code inside a method
+// it starts a new scope for variable definitions
+
+compoundStatement
+	:	lc:LCURLY^ {#lc.setType(SLIST);}
+			// include the (possibly-empty) list of statements
+			(statement)*
+		RCURLY!
+	;
+
+
+statement
+	// A list of statements in curly braces -- start a new scope!
+	:	compoundStatement
+
+	// declarations are ambiguous with "ID DOT" relative to expression
+	// statements. Must backtrack to be sure. Could use a semantic
+	// predicate to test symbol table to see what the type was coming
+	// up, but that's pretty hard without a symbol table ;)
+	|	(declaration)=> declaration SEMI!
+
+	// An expression statement. This could be a method call,
+	// assignment statement, or any other expression evaluated for
+	// side-effects.
+	|	expression SEMI!
+
+	//TODO: what abour interfaces, enums and annotations
+	// class definition
+	|	m:modifiers! classDefinition[#m]
+
+	// Attach a label to the front of a statement
+	|	IDENT c:COLON^ {#c.setType(LABELED_STAT);} statement
+
+	// If-else statement
+	|	"if"^ LPAREN! expression RPAREN! statement
+		(
+			// CONFLICT: the old "dangling-else" problem...
+			// ANTLR generates proper code matching
+			// as soon as possible. Hush warning.
+			options {
+				warnWhenFollowAmbig = false;
+			}
+		:
+			"else"! statement
+		)?
+
+	// For statement
+	|	forStatement
+
+	// While statement
+	|	"while"^ LPAREN! expression RPAREN! statement
+
+	// do-while statement
+	|	"do"^ statement "while"! LPAREN! expression RPAREN! SEMI!
+
+	// get out of a loop (or switch)
+	|	"break"^ (IDENT)? SEMI!
+
+	// do next iteration of a loop
+	|	"continue"^ (IDENT)? SEMI!
+
+	// Return an expression
+	|	"return"^ (expression)? SEMI!
+
+	// switch/case statement
+	|	"switch"^ LPAREN! expression RPAREN! LCURLY!
+			( casesGroup )*
+		RCURLY!
+
+	// exception try-catch block
+	|	tryBlock
+
+	// throw an exception
+	|	"throw"^ expression SEMI!
+
+	// synchronize a statement
+	|	"synchronized"^ LPAREN! expression RPAREN! compoundStatement
+
+	// asserts (uncomment if you want 1.4 compatibility)
+	|	"assert"^ expression ( COLON! expression )? SEMI!
+
+	// empty statement
+	|	s:SEMI {#s.setType(EMPTY_STAT);}
+	;
+
+forStatement
+	:	f:"for"^
+		LPAREN!
+			(	(forInit SEMI)=>traditionalForClause
+			|	forEachClause
+			)
+		RPAREN!
+		statement					 // statement to loop over
+	;
+
+traditionalForClause
+	:
+		forInit SEMI!	// initializer
+		forCond SEMI!	// condition test
+		forIter			// updater
+	;
+
+forEachClause  {Token first = LT(1);}
+	:
+		p:parameterDeclaration COLON! expression
+		{#forEachClause = #(create(FOR_EACH_CLAUSE,"FOR_EACH_CLAUSE",first,LT(1)), #forEachClause);}
+	;
+
+casesGroup
+	:	(	// CONFLICT: to which case group do the statements bind?
+			// ANTLR generates proper code: it groups the
+			// many "case"/"default" labels together then
+			// follows them with the statements
+			options {
+				greedy = true;
+			}
+			:
+			aCase
+		)+
+		caseSList
+		{#casesGroup = #([CASE_GROUP, "CASE_GROUP"], #casesGroup);}
+	;
+
+aCase
+	:	("case"^ expression | "default") COLON!
+	;
+
+caseSList  {Token first = LT(1);}
+	:	(statement)*
+		{#caseSList = #(create(SLIST,"SLIST",first,LT(1)),#caseSList);}
+	;
+
+// The initializer for a for loop
+forInit  {Token first = LT(1);}
+		// if it looks like a declaration, it is
+	:	((declaration)=> declaration
+		// otherwise it could be an expression list...
+		|	expressionList
+		)?
+		{#forInit = #(create(FOR_INIT,"FOR_INIT",first,LT(1)),#forInit);}
+	;
+
+forCond  {Token first = LT(1);}
+	:	(expression)?
+		{#forCond = #(create(FOR_CONDITION,"FOR_CONDITION",first,LT(1)),#forCond);}
+	;
+
+forIter  {Token first = LT(1);}
+	:	(expressionList)?
+		{#forIter = #(create(FOR_ITERATOR,"FOR_ITERATOR",first,LT(1)),#forIter);}
+	;
+
+// an exception handler try/catch block
+tryBlock
+	:	"try"^ compoundStatement
+		(handler)*
+		( finallyClause )?
+	;
+
+finallyClause
+	:	"finally"^ compoundStatement
+	;
+
+// an exception handler
+handler
+	:	"catch"^ LPAREN! parameterDeclaration RPAREN! compoundStatement
+	;
+
+
+// expressions
+// Note that most of these expressions follow the pattern
+//   thisLevelExpression :
+//	   nextHigherPrecedenceExpression
+//		   (OPERATOR nextHigherPrecedenceExpression)*
+// which is a standard recursive definition for a parsing an expression.
+// The operators in java have the following precedences:
+//	lowest  (13)  = *= /= %= += -= <<= >>= >>>= &= ^= |=
+//			(12)  ?:
+//			(11)  ||
+//			(10)  &&
+//			( 9)  |
+//			( 8)  ^
+//			( 7)  &
+//			( 6)  == !=
+//			( 5)  < <= > >=
+//			( 4)  << >>
+//			( 3)  +(binary) -(binary)
+//			( 2)  * / %
+//			( 1)  ++ -- +(unary) -(unary)  ~  !  (type)
+//				  []   () (method call)  . (dot -- identifier qualification)
+//				  new   ()  (explicit parenthesis)
+//
+// the last two are not usually on a precedence chart; I put them in
+// to point out that new has a higher precedence than '.', so you
+// can validy use
+//	 new Frame().show()
+//
+// Note that the above precedence levels map to the rules below...
+// Once you have a precedence chart, writing the appropriate rules as below
+//   is usually very straightfoward
+
+
+
+// the mother of all expressions
+expression  {Token first = LT(1);}
+	:	assignmentExpression
+		{#expression = #(create(EXPR,"EXPR",first,LT(1)),#expression);}
+	;
+
+
+// This is a list of expressions.
+expressionList  {Token first = LT(1);}
+	:	expression (COMMA! expression)*
+		{#expressionList = #(create(ELIST,"ELIST",first,LT(1)), #expressionList);}
+	;
+
+
+// assignment expression (level 13)
+assignmentExpression
+	:	conditionalExpression
+		(	(	ASSIGN^
+			|	PLUS_ASSIGN^
+			|	MINUS_ASSIGN^
+			|	STAR_ASSIGN^
+			|	DIV_ASSIGN^
+			|	MOD_ASSIGN^
+			|	SR_ASSIGN^
+			|	BSR_ASSIGN^
+			|	SL_ASSIGN^
+			|	BAND_ASSIGN^
+			|	BXOR_ASSIGN^
+			|	BOR_ASSIGN^
+			)
+			assignmentExpression
+		)?
+	;
+
+
+// conditional test (level 12)
+conditionalExpression
+	:	logicalOrExpression
+		( QUESTION^ assignmentExpression COLON! conditionalExpression )?
+	;
+
+
+// logical or (||) (level 11)
+logicalOrExpression
+	:	logicalAndExpression (LOR^ logicalAndExpression)*
+	;
+
+
+// logical and (&&) (level 10)
+logicalAndExpression
+	:	inclusiveOrExpression (LAND^ inclusiveOrExpression)*
+	;
+
+
+// bitwise or non-short-circuiting or (|) (level 9)
+inclusiveOrExpression
+	:	exclusiveOrExpression (BOR^ exclusiveOrExpression)*
+	;
+
+
+// exclusive or (^) (level 8)
+exclusiveOrExpression
+	:	andExpression (BXOR^ andExpression)*
+	;
+
+
+// bitwise or non-short-circuiting and (&) (level 7)
+andExpression
+	:	equalityExpression (BAND^ equalityExpression)*
+	;
+
+
+// equality/inequality (==/!=) (level 6)
+equalityExpression
+	:	relationalExpression ((NOT_EQUAL^ | EQUAL^) relationalExpression)*
+	;
+
+
+// boolean relational expressions (level 5)
+relationalExpression
+	:	shiftExpression
+		(	(	(	LT^
+				|	GT^
+				|	LE^
+				|	GE^
+				)
+				shiftExpression
+			)*
+		|	"instanceof"^ typeSpec[true]
+		)
+	;
+
+
+// bit shift expressions (level 4)
+shiftExpression
+	:	additiveExpression ((SL^ | SR^ | BSR^) additiveExpression)*
+	;
+
+
+// binary addition/subtraction (level 3)
+additiveExpression
+	:	multiplicativeExpression ((PLUS^ | MINUS^) multiplicativeExpression)*
+	;
+
+
+// multiplication/division/modulo (level 2)
+multiplicativeExpression
+	:	unaryExpression ((STAR^ | DIV^ | MOD^ ) unaryExpression)*
+	;
+
+unaryExpression
+	:	INC^ unaryExpression
+	|	DEC^ unaryExpression
+	|	MINUS^ {#MINUS.setType(UNARY_MINUS);} unaryExpression
+	|	PLUS^ {#PLUS.setType(UNARY_PLUS);} unaryExpression
+	|	unaryExpressionNotPlusMinus
+	;
+
+unaryExpressionNotPlusMinus
+	:	BNOT^ unaryExpression
+	|	LNOT^ unaryExpression
+	|	(	// subrule allows option to shut off warnings
+			options {
+				// "(int" ambig with postfixExpr due to lack of sequence
+				// info in linear approximate LL(k). It's ok. Shut up.
+				generateAmbigWarnings=false;
+			}
+		:	// If typecast is built in type, must be numeric operand
+			// Have to backtrack to see if operator follows
+		(LPAREN builtInTypeSpec[true] RPAREN unaryExpression)=>
+		lpb:LPAREN^ {#lpb.setType(TYPECAST);} builtInTypeSpec[true] RPAREN!
+		unaryExpression
+
+		// Have to backtrack to see if operator follows. If no operator
+		// follows, it's a typecast. No semantic checking needed to parse.
+		// if it _looks_ like a cast, it _is_ a cast; else it's a "(expr)"
+	|	(LPAREN classTypeSpec[true] RPAREN unaryExpressionNotPlusMinus)=>
+		lp:LPAREN^ {#lp.setType(TYPECAST);} classTypeSpec[true] RPAREN!
+		unaryExpressionNotPlusMinus
+
+	|	postfixExpression
+	)
+	;
+
+// qualified names, array expressions, method invocation, post inc/dec
+postfixExpression
+	:
+		primaryExpression
+
+		(
+			/*
+			options {
+				// the use of postfixExpression in SUPER_CTOR_CALL adds DOT
+				// to the lookahead set, and gives loads of false non-det
+				// warnings.
+				// shut them off.
+				generateAmbigWarnings=false;
+			}
+		:	*/
+			//type arguments are only appropriate for a parameterized method/ctor invocations
+			//semantic check may be needed here to ensure that this is the case
+			DOT^ (typeArguments)?
+				(	IDENT
+					(	lp:LPAREN^ {#lp.setType(METHOD_CALL);}
+						argList
+						RPAREN!
+					)?
+				|	"super"
+					(	// (new Outer()).super() (create enclosing instance)
+						lp3:LPAREN^ argList RPAREN!
+						{#lp3.setType(SUPER_CTOR_CALL);}
+					|	DOT^ (typeArguments)? IDENT
+						(	lps:LPAREN^ {#lps.setType(METHOD_CALL);}
+							argList
+							RPAREN!
+						)?
+					)
+				)
+		|	DOT^ "this"
+		|	DOT^ newExpression
+		|	lb:LBRACK^ {#lb.setType(INDEX_OP);} expression RBRACK!
+		)*
+
+		(	// possibly add on a post-increment or post-decrement.
+			// allows INC/DEC on too much, but semantics can check
+			in:INC^ {#in.setType(POST_INC);}
+	 	|	de:DEC^ {#de.setType(POST_DEC);}
+		)?
+ 	;
+
+// the basic element of an expression
+primaryExpression
+	:	identPrimary ( options {greedy=true;} : DOT^ "class" )?
+	|	constant
+	|	"true"
+	|	"false"
+	|	"null"
+	|	newExpression
+	|	"this"
+	|	"super"
+	|	LPAREN! assignmentExpression RPAREN!
+		// look for int.class and int[].class
+	|	builtInType
+		( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )*
+		DOT^ "class"
+	;
+
+/** Match a, a.b.c refs, a.b.c(...) refs, a.b.c[], a.b.c[].class,
+ *  and a.b.c.class refs. Also this(...) and super(...). Match
+ *  this or super.
+ */
+identPrimary
+	:	(ta1:typeArguments!)?
+		IDENT
+		// Syntax for method invocation with type arguments is
+		// <String>foo("blah")
+		(
+			options {
+				// .ident could match here or in postfixExpression.
+				// We do want to match here. Turn off warning.
+				greedy=true;
+				// This turns the ambiguity warning of the second alternative
+				// off. See below. (The "false" predicate makes it non-issue)
+				warnWhenFollowAmbig=false;
+			}
+			// we have a new nondeterminism because of
+			// typeArguments... only a syntactic predicate will help...
+			// The problem is that this loop here conflicts with
+			// DOT typeArguments "super" in postfixExpression (k=2)
+			// A proper solution would require a lot of refactoring...
+		:	(DOT (typeArguments)? IDENT) =>
+				DOT^ (ta2:typeArguments!)? IDENT
+		|	{false}?	// FIXME: this is very ugly but it seems to work...
+						// this will also produce an ANTLR warning!
+				// Unfortunately a syntactic predicate can only select one of
+				// multiple alternatives on the same level, not break out of
+				// an enclosing loop, which is why this ugly hack (a fake
+				// empty alternative with always-false semantic predicate)
+				// is necessary.
+		)*
+		(
+			options {
+				// ARRAY_DECLARATOR here conflicts with INDEX_OP in
+				// postfixExpression on LBRACK RBRACK.
+				// We want to match [] here, so greedy. This overcomes
+				// limitation of linear approximate lookahead.
+				greedy=true;
+			}
+		:	(	lp:LPAREN^ {#lp.setType(METHOD_CALL);}
+				// if the input is valid, only the last IDENT may
+				// have preceding typeArguments... rather hacky, this is...
+				{if (#ta2 != null) astFactory.addASTChild(currentAST, #ta2);}
+				{if (#ta2 == null) astFactory.addASTChild(currentAST, #ta1);}
+				argList RPAREN!
+			)
+		|	( options {greedy=true;} :
+				lbc:LBRACK^ {#lbc.setType(ARRAY_DECLARATOR);} RBRACK!
+			)+
+		)?
+	;
+
+/** object instantiation.
+ *  Trees are built as illustrated by the following input/tree pairs:
+ *
+ *  new T()
+ *
+ *  new
+ *   |
+ *   T --  ELIST
+ *		   |
+ *		  arg1 -- arg2 -- .. -- argn
+ *
+ *  new int[]
+ *
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR
+ *
+ *  new int[] {1,2}
+ *
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR -- ARRAY_INIT
+ *								  |
+ *								EXPR -- EXPR
+ *								  |	  |
+ *								  1	  2
+ *
+ *  new int[3]
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR
+ *				|
+ *			  EXPR
+ *				|
+ *				3
+ *
+ *  new int[1][2]
+ *
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR
+ *			   |
+ *		 ARRAY_DECLARATOR -- EXPR
+ *			   |			  |
+ *			 EXPR			 1
+ *			   |
+ *			   2
+ *
+ */
+newExpression
+	:	"new"^ (typeArguments)? type
+		(	LPAREN! argList RPAREN! (classBlock)?
+
+			//java 1.1
+			// Note: This will allow bad constructs like
+			//	new int[4][][3] {exp,exp}.
+			//	There needs to be a semantic check here...
+			// to make sure:
+			//   a) [ expr ] and [ ] are not mixed
+			//   b) [ expr ] and an init are not used together
+
+		|	newArrayDeclarator (arrayInitializer)?
+		)
+	;
+
+argList {Token first = LT(1);}
+	:	(	expressionList
+		|	/*nothing*/
+			{#argList = create(ELIST,"ELIST",first,LT(1));}
+		)
+	;
+
+newArrayDeclarator
+	:	(
+			// CONFLICT:
+			// newExpression is a primaryExpression which can be
+			// followed by an array index reference. This is ok,
+			// as the generated code will stay in this loop as
+			// long as it sees an LBRACK (proper behavior)
+			options {
+				warnWhenFollowAmbig = false;
+			}
+		:
+			lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);}
+				(expression)?
+			RBRACK!
+		)+
+	;
+
+constant
+	:	NUM_INT
+	|	CHAR_LITERAL
+	|	STRING_LITERAL
+	|	NUM_FLOAT
+	|	NUM_LONG
+	|	NUM_DOUBLE
+	;
+
+
+//----------------------------------------------------------------------------
+// The Java scanner
+//----------------------------------------------------------------------------
+class JavaLexer extends Lexer;
+
+options {
+	exportVocab=Java;		// call the vocabulary "Java"
+	testLiterals=false;		// don't automatically test for literals
+	k=4;					// four characters of lookahead
+	charVocabulary='\u0003'..'\uFFFF';
+	// without inlining some bitset tests, couldn't do unicode;
+	// I need to make ANTLR generate smaller bitsets; see
+	// bottom of JavaLexer.java
+	codeGenBitsetTestThreshold=20;
+}
+
+{
+    protected static final int SCS_TYPE = 3, SCS_VAL = 4, SCS_LIT = 8, SCS_LIMIT = 16;
+    protected static final int SCS_SQ_TYPE = 0, SCS_TQ_TYPE = 1, SCS_RE_TYPE = 2;
+    protected int stringCtorState = 0;  // hack string and regexp constructor boundaries
+    protected int lastSigTokenType = EOF;  // last returned non-whitespace token
+
+    /** flag for enabling the "assert" keyword */
+	private boolean assertEnabled = true;
+	/** flag for enabling the "enum" keyword */
+	private boolean enumEnabled = true;
+    /** flag for including whitespace tokens (for IDE preparsing) */
+    private boolean whitespaceIncluded = false;
+
+	/** Enable the "assert" keyword */
+	public void enableAssert(boolean shouldEnable) { assertEnabled = shouldEnable; }
+	/** Query the "assert" keyword state */
+	public boolean isAssertEnabled() { return assertEnabled; }
+	/** Enable the "enum" keyword */
+	public void enableEnum(boolean shouldEnable) { enumEnabled = shouldEnable; }
+	/** Query the "enum" keyword state */
+	public boolean isEnumEnabled() { return enumEnabled; }
+
+    /** This is a bit of plumbing which resumes collection of string constructor bodies,
+     *  after an embedded expression has been parsed.
+     *  Usage:  new JavaRecognizer(new JavaLexer(in).plumb()).
+     */
+    public TokenStream plumb() {
+        return new TokenStream() {
+            public Token nextToken() throws TokenStreamException {
+                if (stringCtorState >= SCS_LIT) {
+                    // This goo is modeled upon the ANTLR code for nextToken:
+                    int quoteType = (stringCtorState & SCS_TYPE);
+                    stringCtorState = 0;  // get out of this mode, now
+                    resetText();
+/*                    try {
+                        switch (quoteType) {
+                        case SCS_SQ_TYPE:
+//todo: suitable replacement???     mSTRING_CTOR_END(true, false, false); 
+                        	break;
+                        case SCS_TQ_TYPE:
+//                            mSTRING_CTOR_END(true, false, true); 
+                        	break;
+                        case SCS_RE_TYPE:
+//                            mREGEXP_CTOR_END(true, false); 
+                        	break;
+                        default:  throw new AssertionError(false);
+                        }
+                        lastSigTokenType = _returnToken.getType();
+                        return _returnToken;
+                    }*//* catch (RecognitionException e) {
+                        throw new TokenStreamRecognitionException(e);
+                    }*/ /*catch (CharStreamException cse) {
+                        if ( cse instanceof CharStreamIOException ) {
+                            throw new TokenStreamIOException(((CharStreamIOException)cse).io);
+                        }
+                        else {
+                            throw new TokenStreamException(cse.getMessage());
+                        }
+                    }*/
+                }
+                Token token = JavaLexer.this.nextToken();
+                int lasttype = token.getType();
+                if (whitespaceIncluded) {
+                    switch (lasttype) {  // filter out insignificant types
+                    case WS:
+                    case SL_COMMENT:
+                    case ML_COMMENT:
+                        lasttype = lastSigTokenType;  // back up!
+                    }
+                }
+                lastSigTokenType = lasttype;
+                return token;
+            }
+        };
+    }
+    
+    protected JavaRecognizer parser;  // little-used link; TODO: get rid of
+}
+
+// OPERATORS
+QUESTION		:	'?'		;
+LPAREN			:	'('		;
+RPAREN			:	')'		;
+LBRACK			:	'['		;
+RBRACK			:	']'		;
+LCURLY			:	'{'		;
+RCURLY			:	'}'		;
+COLON			:	':'		;
+COMMA			:	','		;
+//DOT			:	'.'		;
+ASSIGN			:	'='		;
+EQUAL			:	"=="	;
+LNOT			:	'!'		;
+BNOT			:	'~'		;
+NOT_EQUAL		:	"!="	;
+DIV				:	'/'		;
+DIV_ASSIGN		:	"/="	;
+PLUS			:	'+'		;
+PLUS_ASSIGN		:	"+="	;
+INC				:	"++"	;
+MINUS			:	'-'		;
+MINUS_ASSIGN	:	"-="	;
+DEC				:	"--"	;
+STAR			:	'*'		;
+STAR_ASSIGN		:	"*="	;
+MOD				:	'%'		;
+MOD_ASSIGN		:	"%="	;
+SR				:	">>"	;
+SR_ASSIGN		:	">>="	;
+BSR				:	">>>"	;
+BSR_ASSIGN		:	">>>="	;
+GE				:	">="	;
+GT				:	">"		;
+SL				:	"<<"	;
+SL_ASSIGN		:	"<<="	;
+LE				:	"<="	;
+LT				:	'<'		;
+BXOR			:	'^'		;
+BXOR_ASSIGN		:	"^="	;
+BOR				:	'|'		;
+BOR_ASSIGN		:	"|="	;
+LOR				:	"||"	;
+BAND			:	'&'		;
+BAND_ASSIGN		:	"&="	;
+LAND			:	"&&"	;
+SEMI			:	';'		;
+
+
+// Whitespace -- ignored
+WS	:	(	' '
+		|	'\t'
+		|	'\f'
+			// handle newlines
+		|	(	options {generateAmbigWarnings=false;}
+			:	"\r\n"	// Evil DOS
+			|	'\r'	// Macintosh
+			|	'\n'	// Unix (the right way)
+			)
+			{ newline(); }
+		)+
+		{ _ttype = Token.SKIP; }
+	;
+
+// Single-line comments
+SL_COMMENT
+	:	"//"
+		(~('\n'|'\r'))* ('\n'|'\r'('\n')?)
+		{$setType(Token.SKIP); newline();}
+	;
+
+// multiple-line comments
+ML_COMMENT
+	:	"/*"
+		(	/*	'\r' '\n' can be matched in one alternative or by matching
+				'\r' in one iteration and '\n' in another. I am trying to
+				handle any flavor of newline that comes in, but the language
+				that allows both "\r\n" and "\r" and "\n" to all be valid
+				newline is ambiguous. Consequently, the resulting grammar
+				must be ambiguous. I'm shutting this warning off.
+			 */
+			options {
+				generateAmbigWarnings=false;
+			}
+		:
+			{ LA(2)!='/' }? '*'
+		|	'\r' '\n'		{newline();}
+		|	'\r'			{newline();}
+		|	'\n'			{newline();}
+		|	~('*'|'\n'|'\r')
+		)*
+		"*/"
+		{$setType(Token.SKIP);}
+	;
+
+
+//	 character literals
+	CHAR_LITERAL
+		:	'\'' ( ESC | ~('\''|'\n'|'\r'|'\\') ) '\''
+		;
+
+//	 string literals
+	STRING_LITERAL
+		:	'"' (ESC|~('"'|'\\'|'\n'|'\r'))* '"'
+		;
+
+
+// escape sequence -- note that this is protected; it can only be called
+// from another lexer rule -- it will not ever directly return a token to
+// the parser
+// There are various ambiguities hushed in this rule. The optional
+// '0'...'9' digit matches should be matched here rather than letting
+// them go back to STRING_LITERAL to be matched. ANTLR does the
+// right thing by matching immediately; hence, it's ok to shut off
+// the FOLLOW ambig warnings.
+protected
+ESC
+	:	'\\'
+		(	'n'
+		|	'r'
+		|	't'
+		|	'b'
+		|	'f'
+		|	'"'
+		|	'\''
+		|	'\\'
+		|	('u')+ HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
+		|	'0'..'3'
+			(
+				options {
+					warnWhenFollowAmbig = false;
+				}
+			:	'0'..'7'
+				(
+					options {
+						warnWhenFollowAmbig = false;
+					}
+				:	'0'..'7'
+				)?
+			)?
+		|	'4'..'7'
+			(
+				options {
+					warnWhenFollowAmbig = false;
+				}
+			:	'0'..'7'
+			)?
+		)
+	;
+
+
+// hexadecimal digit (again, note it's protected!)
+protected
+HEX_DIGIT
+	:	('0'..'9'|'A'..'F'|'a'..'f')
+	;
+
+
+// a dummy rule to force vocabulary to be all characters (except special
+// ones that ANTLR uses internally (0 to 2)
+protected
+VOCAB
+	:	'\3'..'\377'
+	;
+
+
+// an identifier. Note that testLiterals is set to true! This means
+// that after we match the rule, we look in the literals table to see
+// if it's a literal or really an identifer
+IDENT
+	options {testLiterals=true;}
+	:	('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'$')*
+		{
+			// check if "assert" keyword is enabled
+			if (assertEnabled && "assert".equals($getText)) {
+				$setType(LITERAL_assert); // set token type for the rule in the parser
+			}
+			// check if "enum" keyword is enabled
+			if (enumEnabled && "enum".equals($getText)) {
+				$setType(LITERAL_enum); // set token type for the rule in the parser
+			}
+		}
+	;
+
+
+// a numeric literal
+NUM_INT
+	{boolean isDecimal=false; Token t=null;}
+	:	'.' {_ttype = DOT;}
+			(
+				(('0'..'9')+ (EXPONENT)? (f1:FLOAT_SUFFIX {t=f1;})?
+				{
+				if (t != null && t.getText().toUpperCase().indexOf('F')>=0) {
+					_ttype = NUM_FLOAT;
+				}
+				else {
+					_ttype = NUM_DOUBLE; // assume double
+				}
+				})
+				|
+				// JDK 1.5 token for variable length arguments
+				(".." {_ttype = TRIPLE_DOT;})
+			)?
+
+	|	(	'0' {isDecimal = true;} // special case for just '0'
+			(	('x'|'X')
+				(											// hex
+					// the 'e'|'E' and float suffix stuff look
+					// like hex digits, hence the (...)+ doesn't
+					// know when to stop: ambig. ANTLR resolves
+					// it correctly by matching immediately. It
+					// is therefor ok to hush warning.
+					options {
+						warnWhenFollowAmbig=false;
+					}
+				:	HEX_DIGIT
+				)+
+
+			|	//float or double with leading zero
+				(('0'..'9')+ ('.'|EXPONENT|FLOAT_SUFFIX)) => ('0'..'9')+
+
+			|	('0'..'7')+									// octal
+			)?
+		|	('1'..'9') ('0'..'9')*  {isDecimal=true;}		// non-zero decimal
+		)
+		(	('l'|'L') { _ttype = NUM_LONG; }
+
+		// only check to see if it's a float if looks like decimal so far
+		|	{isDecimal}?
+			(	'.' ('0'..'9')* (EXPONENT)? (f2:FLOAT_SUFFIX {t=f2;})?
+			|	EXPONENT (f3:FLOAT_SUFFIX {t=f3;})?
+			|	f4:FLOAT_SUFFIX {t=f4;}
+			)
+			{
+			if (t != null && t.getText().toUpperCase() .indexOf('F') >= 0) {
+				_ttype = NUM_FLOAT;
+			}
+			else {
+				_ttype = NUM_DOUBLE; // assume double
+			}
+			}
+		)?
+	;
+
+// JDK 1.5 token for annotations and their declarations
+AT
+	:	'@'
+	;
+
+// a couple protected methods to assist in matching floating point numbers
+protected
+EXPONENT
+	:	('e'|'E') ('+'|'-')? ('0'..'9')+
+	;
+
+
+protected
+FLOAT_SUFFIX
+	:	'f'|'F'|'d'|'D'
+	;
+			
+
+//			 Note: Please don't use physical tabs.  Logical tabs for indent are width 4.
+//			 Here's a little hint for you, Emacs:
+//			 Local Variables:
+//			 tab-width: 4
+//			 mode: antlr-mode
+//			 indent-tabs-mode: nil
+//			 End:
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/package.html b/groovy/src/main/org/codehaus/groovy/antlr/package.html
new file mode 100644
index 0000000..c2da9e7
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.antlr.*</title>
+  </head>
+  <body>
+    <p>Parser related classes.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/CompositeVisitor.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/CompositeVisitor.java
new file mode 100644
index 0000000..4f9b562
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/CompositeVisitor.java
@@ -0,0 +1,1161 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import java.util.*;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+
+/**
+ * A composite of many visitors. Any call to a method from Visitor
+ * will invoke each visitor in turn, and reverse the invocation
+ * order on a closing visit.
+ * i.e.
+ * with the list of visitors = [a,b,c]
+ * composite.visitDefault() would...
+ * call on the opening visit - a.visitDefault() then b.visitDefault() then c.visitDefault()
+ * call on the closing visit - c.visitDefault() then b.visitDefault() then a.visitDefault()
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+public class CompositeVisitor implements Visitor{
+    final List visitors;
+    final List backToFrontVisitors;
+    private final Stack stack;
+
+    /**
+     * A composite of the supplied list of antlr AST visitors.
+     * @param visitors a List of implementations of the Visitor interface
+     */
+    public CompositeVisitor(List visitors) {
+        this.visitors = visitors;
+        this.stack = new Stack();
+        backToFrontVisitors = new ArrayList();
+        backToFrontVisitors.addAll(visitors);
+        Collections.reverse(backToFrontVisitors);
+    }
+
+    private Iterator itr(int visit) {
+        Iterator itr=visitors.iterator();
+        if (visit == CLOSING_VISIT) {
+            itr = backToFrontVisitors.iterator();
+        }
+        return itr;
+    }
+
+    public void setUp() {
+        Iterator itr = visitors.iterator();
+        while (itr.hasNext()) {((Visitor)itr.next()).setUp();}
+    }
+
+    public void visitAbstract(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitAbstract(t,visit);}
+    }
+
+    public void visitAnnotation(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitAnnotation(t,visit);}
+    }
+
+    public void visitAnnotations(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitAnnotations(t,visit);}
+    }
+
+    public void visitAnnotationArrayInit(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitAnnotationArrayInit(t,visit);}
+    }
+
+    public void visitAnnotationDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitAnnotationDef(t,visit);}
+    }
+
+    public void visitAnnotationFieldDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitAnnotationFieldDef(t,visit);}
+    }
+
+    public void visitAnnotationMemberValuePair(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitAnnotationMemberValuePair(t,visit);}
+    }
+
+    public void visitArrayDeclarator(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitArrayDeclarator(t,visit);}
+    }
+
+    public void visitAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitAssign(t,visit);}
+    }
+
+    public void visitAt(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitAt(t,visit);}
+    }
+
+    public void visitBand(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBand(t,visit);}
+    }
+
+    public void visitBandAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBandAssign(t,visit);}
+    }
+
+    public void visitBigSuffix(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBigSuffix(t,visit);}
+    }
+
+    public void visitBlock(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBlock(t,visit);}
+    }
+
+    public void visitBnot(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBnot(t,visit);}
+    }
+
+    public void visitBor(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBor(t,visit);}
+    }
+
+    public void visitBorAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBorAssign(t,visit);}
+    }
+
+    public void visitBsr(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBsr(t,visit);}
+    }
+
+    public void visitBsrAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBsrAssign(t,visit);}
+    }
+
+    public void visitBxor(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBxor(t,visit);}
+    }
+
+    public void visitBxorAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitBxorAssign(t,visit);}
+    }
+
+    public void visitCaseGroup(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitCaseGroup(t,visit);}
+    }
+
+    public void visitClassDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitClassDef(t,visit);}
+    }
+
+    public void visitClosedBlock(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitClosedBlock(t,visit);}
+    }
+
+	public void visitClosureList(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitClosureList(t,visit);}
+	}
+
+    public void visitClosureOp(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitClosureOp(t,visit);}
+    }
+
+    public void visitColon(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitColon(t,visit);}
+    }
+
+    public void visitComma(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitComma(t,visit);}
+    }
+
+    public void visitCompareTo(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitCompareTo(t,visit);}
+    }
+
+    public void visitCtorCall(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitCtorCall(t,visit);}
+    }
+
+    public void visitCtorIdent(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitCtorIdent(t,visit);}
+    }
+
+    public void visitDec(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitDec(t,visit);}
+    }
+
+    public void visitDigit(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitDigit(t,visit);}
+    }
+
+    public void visitDiv(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitDiv(t,visit);}
+    }
+
+    public void visitDivAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitDivAssign(t,visit);}
+    }
+
+    public void visitDollar(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitDollar(t,visit);}
+    }
+
+    public void visitDot(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitDot(t,visit);}
+    }
+
+    public void visitDynamicMember(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitDynamicMember(t,visit);}
+    }
+
+    public void visitElist(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitElist(t,visit);}
+    }
+
+    public void visitEmptyStat(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitEmptyStat(t,visit);}
+    }
+
+    public void visitEnumConstantDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitEnumConstantDef(t,visit);}
+    }
+
+    public void visitEnumDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitEnumDef(t,visit);}
+    }
+
+    public void visitEof(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitEof(t,visit);}
+    }
+
+    public void visitEqual(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitEqual(t,visit);}
+    }
+
+    public void visitEsc(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitEsc(t,visit);}
+    }
+
+    public void visitExponent(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitExponent(t,visit);}
+    }
+
+    public void visitExpr(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitExpr(t,visit);}
+    }
+
+    public void visitExtendsClause(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitExtendsClause(t,visit);}
+    }
+
+    public void visitFinal(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitFinal(t,visit);}
+    }
+
+    public void visitFloatSuffix(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitFloatSuffix(t,visit);}
+    }
+
+    public void visitForCondition(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitForCondition(t,visit);}
+    }
+
+    public void visitForEachClause(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitForEachClause(t,visit);}
+    }
+
+    public void visitForInit(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitForInit(t,visit);}
+    }
+
+    public void visitForInIterable(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitForInIterable(t,visit);}
+    }
+
+    public void visitForIterator(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitForIterator(t,visit);}
+    }
+
+    public void visitGe(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitGe(t,visit);}
+    }
+
+    public void visitGt(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitGt(t,visit);}
+    }
+
+    public void visitHexDigit(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitHexDigit(t,visit);}
+    }
+
+    public void visitIdent(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitIdent(t,visit);}
+    }
+
+    public void visitImplementsClause(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitImplementsClause(t,visit);}
+    }
+
+    public void visitImplicitParameters(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitImplicitParameters(t,visit);}
+    }
+
+    public void visitImport(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitImport(t,visit);}
+    }
+
+    public void visitInc(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitInc(t,visit);}
+    }
+
+    public void visitIndexOp(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitIndexOp(t,visit);}
+    }
+
+    public void visitInstanceInit(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitInstanceInit(t,visit);}
+    }
+
+    public void visitInterfaceDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitInterfaceDef(t,visit);}
+    }
+
+    public void visitLabeledArg(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLabeledArg(t,visit);}
+    }
+
+    public void visitLabeledStat(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLabeledStat(t,visit);}
+    }
+
+    public void visitLand(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLand(t,visit);}
+    }
+
+    public void visitLbrack(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLbrack(t,visit);}
+    }
+
+    public void visitLcurly(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLcurly(t,visit);}
+    }
+
+    public void visitLe(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLe(t,visit);}
+    }
+
+    public void visitLetter(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLetter(t,visit);}
+    }
+
+    public void visitListConstructor(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitListConstructor(t,visit);}
+    }
+
+    public void visitLiteralAs(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralAs(t,visit);}
+    }
+
+    public void visitLiteralAssert(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralAssert(t,visit);}
+    }
+
+    public void visitLiteralBoolean(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralBoolean(t,visit);}
+    }
+
+    public void visitLiteralBreak(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralBreak(t,visit);}
+    }
+
+    public void visitLiteralByte(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralByte(t,visit);}
+    }
+
+    public void visitLiteralCase(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralCase(t,visit);}
+    }
+
+    public void visitLiteralCatch(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralCatch(t,visit);}
+    }
+
+    public void visitLiteralChar(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralChar(t,visit);}
+    }
+
+    public void visitLiteralClass(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralClass(t,visit);}
+    }
+
+    public void visitLiteralContinue(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralContinue(t,visit);}
+    }
+
+    public void visitLiteralDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralDef(t,visit);}
+    }
+
+    public void visitLiteralDefault(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralDefault(t,visit);}
+    }
+
+    public void visitLiteralDouble(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralDouble(t,visit);}
+    }
+
+    public void visitLiteralElse(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralElse(t,visit);}
+    }
+
+    public void visitLiteralEnum(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralEnum(t,visit);}
+    }
+
+    public void visitLiteralExtends(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralExtends(t,visit);}
+    }
+
+    public void visitLiteralFalse(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralFalse(t,visit);}
+    }
+
+    public void visitLiteralFinally(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralFinally(t,visit);}
+    }
+
+    public void visitLiteralFloat(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralFloat(t,visit);}
+    }
+
+    public void visitLiteralFor(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralFor(t,visit);}
+    }
+
+    public void visitLiteralIf(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralIf(t,visit);}
+    }
+
+    public void visitLiteralImplements(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralImplements(t,visit);}
+    }
+
+    public void visitLiteralImport(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralImport(t,visit);}
+    }
+
+    public void visitLiteralIn(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralIn(t,visit);}
+    }
+
+    public void visitLiteralInstanceof(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralInstanceof(t,visit);}
+    }
+
+    public void visitLiteralInt(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralInt(t,visit);}
+    }
+
+    public void visitLiteralInterface(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralInterface(t,visit);}
+    }
+
+    public void visitLiteralLong(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralLong(t,visit);}
+    }
+
+    public void visitLiteralNative(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralNative(t,visit);}
+    }
+
+    public void visitLiteralNew(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralNew(t,visit);}
+    }
+
+    public void visitLiteralNull(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralNull(t,visit);}
+    }
+
+    public void visitLiteralPackage(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralPackage(t,visit);}
+    }
+
+    public void visitLiteralPrivate(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralPrivate(t,visit);}
+    }
+
+    public void visitLiteralProtected(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralProtected(t,visit);}
+    }
+
+    public void visitLiteralPublic(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralPublic(t,visit);}
+    }
+
+    public void visitLiteralReturn(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralReturn(t,visit);}
+    }
+
+    public void visitLiteralShort(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralShort(t,visit);}
+    }
+
+    public void visitLiteralStatic(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralStatic(t,visit);}
+    }
+
+    public void visitLiteralSuper(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralSuper(t,visit);}
+    }
+
+    public void visitLiteralSwitch(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralSwitch(t,visit);}
+    }
+
+    public void visitLiteralSynchronized(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralSynchronized(t,visit);}
+    }
+
+    public void visitLiteralThis(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralThis(t,visit);}
+    }
+
+    public void visitLiteralThreadsafe(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralThreadsafe(t,visit);}
+    }
+
+    public void visitLiteralThrow(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralThrow(t,visit);}
+    }
+
+    public void visitLiteralThrows(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralThrows(t,visit);}
+    }
+
+    public void visitLiteralTransient(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralTransient(t,visit);}
+    }
+
+    public void visitLiteralTrue(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralTrue(t,visit);}
+    }
+
+    public void visitLiteralTry(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralTry(t,visit);}
+    }
+
+    public void visitLiteralVoid(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralVoid(t,visit);}
+    }
+
+    public void visitLiteralVolatile(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralVolatile(t,visit);}
+    }
+
+    public void visitLiteralWhile(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLiteralWhile(t,visit);}
+    }
+
+    public void visitLnot(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLnot(t,visit);}
+    }
+
+    public void visitLor(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLor(t,visit);}
+    }
+
+    public void visitLparen(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLparen(t,visit);}
+    }
+
+    public void visitLt(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitLt(t,visit);}
+    }
+
+    public void visitMapConstructor(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitMapConstructor(t,visit);}
+    }
+
+    public void visitMemberPointer(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitMemberPointer(t,visit);}
+    }
+
+    public void visitMethodCall(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitMethodCall(t,visit);}
+    }
+
+    public void visitMethodDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitMethodDef(t,visit);}
+    }
+
+    public void visitMinus(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitMinus(t,visit);}
+    }
+
+    public void visitMinusAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitMinusAssign(t,visit);}
+    }
+
+    public void visitMlComment(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitMlComment(t,visit);}
+    }
+
+    public void visitMod(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitMod(t,visit);}
+    }
+
+    public void visitModifiers(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitModifiers(t,visit);}
+    }
+
+    public void visitModAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitModAssign(t,visit);}
+    }
+
+    public void visitNls(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitNls(t,visit);}
+    }
+
+    public void visitNotEqual(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitNotEqual(t,visit);}
+    }
+
+    public void visitNullTreeLookahead(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitNullTreeLookahead(t,visit);}
+    }
+
+    public void visitNumBigDecimal(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitNumBigDecimal(t,visit);}
+    }
+
+    public void visitNumBigInt(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitNumBigInt(t,visit);}
+    }
+
+    public void visitNumDouble(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitNumDouble(t,visit);}
+    }
+
+    public void visitNumFloat(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitNumFloat(t,visit);}
+    }
+
+    public void visitNumInt(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitNumInt(t,visit);}
+    }
+
+    public void visitNumLong(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitNumLong(t,visit);}
+    }
+
+    public void visitObjblock(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitObjblock(t,visit);}
+    }
+
+    public void visitOneNl(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitOneNl(t,visit);}
+    }
+
+    public void visitOptionalDot(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitOptionalDot(t,visit);}
+    }
+
+    public void visitPackageDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitPackageDef(t,visit);}
+    }
+
+    public void visitParameters(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitParameters(t,visit);}
+    }
+
+    public void visitParameterDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitParameterDef(t,visit);}
+    }
+
+    public void visitPlus(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitPlus(t,visit);}
+    }
+
+    public void visitPlusAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitPlusAssign(t,visit);}
+    }
+
+    public void visitPostDec(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitPostDec(t,visit);}
+    }
+
+    public void visitPostInc(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitPostInc(t,visit);}
+    }
+
+    public void visitQuestion(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitQuestion(t,visit);}
+    }
+
+    public void visitRangeExclusive(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitRangeExclusive(t,visit);}
+    }
+
+    public void visitRangeInclusive(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitRangeInclusive(t,visit);}
+    }
+
+    public void visitRbrack(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitRbrack(t,visit);}
+    }
+
+    public void visitRcurly(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitRcurly(t,visit);}
+    }
+
+    public void visitRegexpCtorEnd(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitRegexpCtorEnd(t,visit);}
+    }
+
+    public void visitRegexpLiteral(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitRegexpLiteral(t,visit);}
+    }
+
+    public void visitRegexpSymbol(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitRegexpSymbol(t,visit);}
+    }
+
+    public void visitRegexFind(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitRegexFind(t,visit);}
+    }
+
+    public void visitRegexMatch(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitRegexMatch(t,visit);}
+    }
+
+    public void visitRparen(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitRparen(t,visit);}
+    }
+
+    public void visitSelectSlot(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSelectSlot(t,visit);}
+    }
+
+    public void visitSemi(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSemi(t,visit);}
+    }
+
+    public void visitShComment(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitShComment(t,visit);}
+    }
+
+    public void visitSl(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSl(t,visit);}
+    }
+
+    public void visitSlist(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSlist(t,visit);}
+    }
+
+    public void visitSlAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSlAssign(t,visit);}
+    }
+
+    public void visitSlComment(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSlComment(t,visit);}
+    }
+
+    public void visitSpreadArg(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSpreadArg(t,visit);}
+    }
+
+    public void visitSpreadDot(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSpreadDot(t,visit);}
+    }
+
+    public void visitSpreadMapArg(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSpreadMapArg(t,visit);}
+    }
+
+    public void visitSr(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSr(t,visit);}
+    }
+
+    public void visitSrAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSrAssign(t,visit);}
+    }
+
+    public void visitStar(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStar(t,visit);}
+    }
+
+    public void visitStarAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStarAssign(t,visit);}
+    }
+
+    public void visitStarStar(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStarStar(t,visit);}
+    }
+
+    public void visitStarStarAssign(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStarStarAssign(t,visit);}
+    }
+
+    public void visitStaticImport(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStaticImport(t,visit);}
+    }
+
+    public void visitStaticInit(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStaticInit(t,visit);}
+    }
+
+    public void visitStrictfp(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStrictfp(t,visit);}
+    }
+
+    public void visitStringCh(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStringCh(t,visit);}
+    }
+
+    public void visitStringConstructor(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStringConstructor(t,visit);}
+    }
+
+    public void visitStringCtorEnd(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStringCtorEnd(t,visit);}
+    }
+
+    public void visitStringCtorMiddle(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStringCtorMiddle(t,visit);}
+    }
+
+    public void visitStringCtorStart(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStringCtorStart(t,visit);}
+    }
+
+    public void visitStringLiteral(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStringLiteral(t,visit);}
+    }
+
+    public void visitStringNl(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitStringNl(t,visit);}
+    }
+
+    public void visitSuperCtorCall(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitSuperCtorCall(t,visit);}
+    }
+
+    public void visitTripleDot(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitTripleDot(t,visit);}
+    }
+
+    public void visitType(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitType(t,visit);}
+    }
+
+    public void visitTypecast(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitTypecast(t,visit);}
+    }
+
+    public void visitTypeArgument(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitTypeArgument(t,visit);}
+    }
+
+    public void visitTypeArguments(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitTypeArguments(t,visit);}
+    }
+
+    public void visitTypeLowerBounds(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitTypeLowerBounds(t,visit);}
+    }
+
+    public void visitTypeParameter(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitTypeParameter(t,visit);}
+    }
+
+    public void visitTypeParameters(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitTypeParameters(t,visit);}
+    }
+
+    public void visitTypeUpperBounds(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitTypeUpperBounds(t,visit);}
+    }
+
+    public void visitUnaryMinus(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitUnaryMinus(t,visit);}
+    }
+
+    public void visitUnaryPlus(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitUnaryPlus(t,visit);}
+    }
+
+    public void visitUnusedConst(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitUnusedConst(t,visit);}
+    }
+
+    public void visitUnusedDo(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitUnusedDo(t,visit);}
+    }
+
+    public void visitUnusedGoto(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitUnusedGoto(t,visit);}
+    }
+
+    public void visitVariableDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitVariableDef(t,visit);}
+    }
+
+    public void visitVariableParameterDef(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitVariableParameterDef(t,visit);}
+    }
+
+    public void visitVocab(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitVocab(t,visit);}
+    }
+
+    public void visitWildcardType(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitWildcardType(t,visit);}
+    }
+
+    public void visitWs(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitWs(t,visit);}
+    }
+
+    public void visitDefault(GroovySourceAST t, int visit) {
+        Iterator itr = itr(visit);
+        while (itr.hasNext()) {((Visitor)itr.next()).visitDefault(t,visit);}
+    }
+
+    public void tearDown() {
+        Iterator itr = backToFrontVisitors.iterator();
+        while (itr.hasNext()) {((Visitor)itr.next()).tearDown();}
+    }
+
+    public void push(GroovySourceAST t) {
+        Iterator itr = visitors.iterator();
+        while (itr.hasNext()) {((Visitor)itr.next()).push(t);}
+    }
+    public GroovySourceAST pop() {
+        GroovySourceAST lastNodePopped = null;
+        Iterator itr = backToFrontVisitors.iterator();
+        while (itr.hasNext()) {lastNodePopped = (GroovySourceAST) ((Visitor)itr.next()).pop();}
+        return lastNodePopped;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/FlatNodeListTraversal.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/FlatNodeListTraversal.java
new file mode 100644
index 0000000..d3ec1ae
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/FlatNodeListTraversal.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.antlr.AntlrASTProcessor;
+import org.codehaus.groovy.antlr.GroovySourceAST;
+
+import antlr.collections.AST;
+
+/**
+ * A simple iterator over an ordered (flat) List of the nodes of the AST.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision: 3626 $
+ */
+public class FlatNodeListTraversal extends TraversalHelper {
+    
+    public FlatNodeListTraversal(Visitor visitor) {
+        super(visitor);
+    }
+
+    public AST process(AST t) {
+        GroovySourceAST node = (GroovySourceAST) t;
+
+        // fetch all the nodes in this AST into a List
+        NodeCollector collector = new NodeCollector();
+        AntlrASTProcessor internalTraversal = new PreOrderTraversal(collector);
+        internalTraversal.process(t);
+        List listOfAllNodesInThisAST = collector.getNodes();
+        
+        // process each node in turn
+        setUp(node);        
+        Iterator itr = listOfAllNodesInThisAST.iterator();
+        while (itr.hasNext()) {
+        	GroovySourceAST currentNode = (GroovySourceAST) itr.next();
+        	accept(currentNode);
+        }
+        tearDown(node);
+        return null;
+    }
+
+	protected void accept(GroovySourceAST currentNode) {
+    	openingVisit(currentNode);
+    	closingVisit(currentNode);
+	}    
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/MindMapPrinter.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/MindMapPrinter.java
new file mode 100644
index 0000000..15222c9
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/MindMapPrinter.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+import org.codehaus.groovy.antlr.LineColumn;
+import org.codehaus.groovy.antlr.SourceBuffer;
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+
+import java.io.PrintStream;
+
+/**
+ * An antlr AST visitor that prints a format suitable for viewing in http://freemind.sourceforge.net
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+public class MindMapPrinter extends VisitorAdapter {
+    private final String[] tokenNames;
+    private final PrintStream out;
+    private int depth;
+    private SourceBuffer sourceBuffer;
+
+    /**
+     * A visitor that prints a format suitable for viewing in http://freemind.sourceforge.net
+     * @param out where to print the mindmap file contents to
+     * @param tokenNames an array of token names from antlr
+     */
+
+    public MindMapPrinter(PrintStream out,String[] tokenNames) {
+        this.tokenNames = tokenNames;
+        this.out = out;
+    }
+
+    public MindMapPrinter(PrintStream out,String[] tokenNames, SourceBuffer sourceBuffer) {
+        this.tokenNames = tokenNames;
+        this.out = out;
+        this.sourceBuffer = sourceBuffer;
+    }
+    public void setUp() {
+        depth = 0;
+        out.println("<map version='0.7.1'><node TEXT='AST'>");
+    }
+
+    public void visitDefault(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            depth++;
+            String name = getName(t);
+            String colour = getColour(t);
+            String folded = getFolded(t);
+            out.print("<node TEXT='" + name + "' POSITION='right'" + colour + folded + ">");
+        } else if (visit == CLOSING_VISIT) {
+            out.println("</node>");
+            depth--;
+        }
+    }
+
+    public void tearDown() {
+        out.println("</node></map>");
+    }
+
+    private String getFolded(GroovySourceAST t) {
+        if (depth > 2 && t.getNumberOfChildren() > 0) {
+            switch (t.getType()) {
+                case GroovyTokenTypes.EXPR :
+                case GroovyTokenTypes.METHOD_DEF :
+                case GroovyTokenTypes.VARIABLE_DEF :
+                    return " FOLDED='true'";
+            }
+        }
+        if (t.getType() == GroovyTokenTypes.IMPORT) {
+            return " FOLDED='true'";
+        }
+        return "";
+    }
+
+    private String getColour(GroovySourceAST t) {
+        String colour = "";
+        String black = " COLOR=\"#000000\"";
+        String cyan = " COLOR=\"#006699\"";
+        String blue = " COLOR=\"#17178B\"";
+        String green = " COLOR=\"#008000\"";
+        switch (t.getType()) {
+            case GroovyTokenTypes.ABSTRACT                      :
+            case GroovyTokenTypes.ANNOTATION                    :
+            case GroovyTokenTypes.ANNOTATIONS                   :
+            case GroovyTokenTypes.ANNOTATION_ARRAY_INIT         :
+            case GroovyTokenTypes.ANNOTATION_DEF                :
+            case GroovyTokenTypes.ANNOTATION_FIELD_DEF          :
+            case GroovyTokenTypes.ANNOTATION_MEMBER_VALUE_PAIR  :
+            case GroovyTokenTypes.ARRAY_DECLARATOR              :
+            case GroovyTokenTypes.ASSIGN                        :
+            case GroovyTokenTypes.AT                            :
+            case GroovyTokenTypes.BAND                          :
+            case GroovyTokenTypes.BAND_ASSIGN                   :
+            case GroovyTokenTypes.BIG_SUFFIX                    :
+            case GroovyTokenTypes.BLOCK                         :
+            case GroovyTokenTypes.BNOT                          :
+            case GroovyTokenTypes.BOR                           :
+            case GroovyTokenTypes.BOR_ASSIGN                    :
+            case GroovyTokenTypes.BSR                           :
+            case GroovyTokenTypes.BSR_ASSIGN                    :
+            case GroovyTokenTypes.BXOR                          :
+            case GroovyTokenTypes.BXOR_ASSIGN                   :
+            case GroovyTokenTypes.CASE_GROUP                    :
+            case GroovyTokenTypes.CLOSABLE_BLOCK                :
+            case GroovyTokenTypes.CLOSABLE_BLOCK_OP             :
+            case GroovyTokenTypes.COLON                         :
+            case GroovyTokenTypes.COMMA                         :
+            case GroovyTokenTypes.COMPARE_TO                    :
+            case GroovyTokenTypes.CTOR_CALL                     :
+            case GroovyTokenTypes.CTOR_IDENT                    :
+            case GroovyTokenTypes.DEC                           :
+            case GroovyTokenTypes.DIGIT                         :
+            case GroovyTokenTypes.DIV                           :
+            case GroovyTokenTypes.DIV_ASSIGN                    :
+            case GroovyTokenTypes.DOLLAR                        :
+            case GroovyTokenTypes.DOT                           :
+            case GroovyTokenTypes.DYNAMIC_MEMBER                :
+            case GroovyTokenTypes.ELIST                         :
+            case GroovyTokenTypes.EMPTY_STAT                    :
+            case GroovyTokenTypes.ENUM_CONSTANT_DEF             :
+            case GroovyTokenTypes.ENUM_DEF                      :
+            case GroovyTokenTypes.EOF                           :
+            case GroovyTokenTypes.EQUAL                         :
+            case GroovyTokenTypes.ESC                           :
+            case GroovyTokenTypes.EXPONENT                      :
+            case GroovyTokenTypes.EXPR                          :
+            case GroovyTokenTypes.FINAL                         :
+            case GroovyTokenTypes.FLOAT_SUFFIX                  :
+            case GroovyTokenTypes.FOR_CONDITION                 :
+            case GroovyTokenTypes.FOR_EACH_CLAUSE               :
+            case GroovyTokenTypes.FOR_INIT                      :
+            case GroovyTokenTypes.FOR_IN_ITERABLE               :
+            case GroovyTokenTypes.FOR_ITERATOR                  :
+            case GroovyTokenTypes.GE                            :
+            case GroovyTokenTypes.GT                            :
+            case GroovyTokenTypes.HEX_DIGIT                     :
+            case GroovyTokenTypes.IMPLICIT_PARAMETERS           :
+            case GroovyTokenTypes.INC                           :
+            case GroovyTokenTypes.INDEX_OP                      :
+            case GroovyTokenTypes.INSTANCE_INIT                 :
+            case GroovyTokenTypes.INTERFACE_DEF                 :
+            case GroovyTokenTypes.LABELED_ARG                   :
+            case GroovyTokenTypes.LABELED_STAT                  :
+            case GroovyTokenTypes.LAND                          :
+            case GroovyTokenTypes.LBRACK                        :
+            case GroovyTokenTypes.LCURLY                        :
+            case GroovyTokenTypes.LE                            :
+            case GroovyTokenTypes.LETTER                        :
+            case GroovyTokenTypes.LIST_CONSTRUCTOR              :
+            case GroovyTokenTypes.LNOT                          :
+            case GroovyTokenTypes.LOR                           :
+            case GroovyTokenTypes.LPAREN                        :
+            case GroovyTokenTypes.LT                            :
+            case GroovyTokenTypes.MAP_CONSTRUCTOR               :
+            case GroovyTokenTypes.MEMBER_POINTER                :
+            case GroovyTokenTypes.METHOD_CALL                   :
+            case GroovyTokenTypes.METHOD_DEF                    :
+            case GroovyTokenTypes.MINUS                         :
+            case GroovyTokenTypes.MINUS_ASSIGN                  :
+            case GroovyTokenTypes.ML_COMMENT                    :
+            case GroovyTokenTypes.MOD                           :
+            case GroovyTokenTypes.MODIFIERS                     :
+            case GroovyTokenTypes.MOD_ASSIGN                    :
+            case GroovyTokenTypes.NLS                           :
+            case GroovyTokenTypes.NOT_EQUAL                     :
+            case GroovyTokenTypes.NULL_TREE_LOOKAHEAD           :
+            case GroovyTokenTypes.NUM_BIG_DECIMAL               :
+            case GroovyTokenTypes.NUM_BIG_INT                   :
+            case GroovyTokenTypes.NUM_DOUBLE                    :
+            case GroovyTokenTypes.NUM_FLOAT                     :
+            case GroovyTokenTypes.NUM_INT                       :
+            case GroovyTokenTypes.NUM_LONG                      :
+            case GroovyTokenTypes.OBJBLOCK                      :
+            case GroovyTokenTypes.ONE_NL                        :
+            case GroovyTokenTypes.OPTIONAL_DOT                  :
+            case GroovyTokenTypes.PARAMETERS                    :
+            case GroovyTokenTypes.PARAMETER_DEF                 :
+            case GroovyTokenTypes.PLUS                          :
+            case GroovyTokenTypes.PLUS_ASSIGN                   :
+            case GroovyTokenTypes.POST_DEC                      :
+            case GroovyTokenTypes.POST_INC                      :
+            case GroovyTokenTypes.QUESTION                      :
+            case GroovyTokenTypes.RANGE_EXCLUSIVE               :
+            case GroovyTokenTypes.RANGE_INCLUSIVE               :
+            case GroovyTokenTypes.RBRACK                        :
+            case GroovyTokenTypes.RCURLY                        :
+            case GroovyTokenTypes.REGEXP_CTOR_END               :
+            case GroovyTokenTypes.REGEXP_SYMBOL                 :
+            case GroovyTokenTypes.REGEX_FIND                    :
+            case GroovyTokenTypes.REGEX_MATCH                   :
+            case GroovyTokenTypes.RPAREN                        :
+            case GroovyTokenTypes.SELECT_SLOT                   :
+            case GroovyTokenTypes.SEMI                          :
+            case GroovyTokenTypes.SH_COMMENT                    :
+            case GroovyTokenTypes.SL                            :
+            case GroovyTokenTypes.SLIST                         :
+            case GroovyTokenTypes.SL_ASSIGN                     :
+            case GroovyTokenTypes.SL_COMMENT                    :
+            case GroovyTokenTypes.SPREAD_ARG                    :
+            case GroovyTokenTypes.SPREAD_DOT                    :
+            case GroovyTokenTypes.SPREAD_MAP_ARG                :
+            case GroovyTokenTypes.SR                            :
+            case GroovyTokenTypes.SR_ASSIGN                     :
+            case GroovyTokenTypes.STAR                          :
+            case GroovyTokenTypes.STAR_ASSIGN                   :
+            case GroovyTokenTypes.STAR_STAR                     :
+            case GroovyTokenTypes.STAR_STAR_ASSIGN              :
+            case GroovyTokenTypes.STATIC_IMPORT                 :
+            case GroovyTokenTypes.STATIC_INIT                   :
+            case GroovyTokenTypes.STRICTFP                      :
+            case GroovyTokenTypes.STRING_CH                     :
+            case GroovyTokenTypes.STRING_CONSTRUCTOR            :
+            case GroovyTokenTypes.STRING_CTOR_END               :
+            case GroovyTokenTypes.STRING_CTOR_MIDDLE            :
+            case GroovyTokenTypes.STRING_CTOR_START             :
+            case GroovyTokenTypes.STRING_NL                     :
+            case GroovyTokenTypes.SUPER_CTOR_CALL               :
+            case GroovyTokenTypes.TRIPLE_DOT                    :
+            case GroovyTokenTypes.TYPECAST                      :
+            case GroovyTokenTypes.TYPE_ARGUMENT                 :
+            case GroovyTokenTypes.TYPE_ARGUMENTS                :
+            case GroovyTokenTypes.TYPE_LOWER_BOUNDS             :
+            case GroovyTokenTypes.TYPE_PARAMETER                :
+            case GroovyTokenTypes.TYPE_PARAMETERS               :
+            case GroovyTokenTypes.TYPE_UPPER_BOUNDS             :
+            case GroovyTokenTypes.UNARY_MINUS                   :
+            case GroovyTokenTypes.UNARY_PLUS                    :
+            case GroovyTokenTypes.UNUSED_CONST                  :
+            case GroovyTokenTypes.UNUSED_DO                     :
+            case GroovyTokenTypes.UNUSED_GOTO                   :
+            case GroovyTokenTypes.VARIABLE_DEF                  :
+            case GroovyTokenTypes.VARIABLE_PARAMETER_DEF        :
+            case GroovyTokenTypes.VOCAB                         :
+            case GroovyTokenTypes.WILDCARD_TYPE                 :
+            case GroovyTokenTypes.WS                            :
+                colour = black;
+                break;
+
+            case GroovyTokenTypes.STRING_LITERAL                :
+            case GroovyTokenTypes.REGEXP_LITERAL                :
+                colour = green;
+                break;
+
+            case GroovyTokenTypes.CLASS_DEF                     :
+            case GroovyTokenTypes.EXTENDS_CLAUSE                :
+            case GroovyTokenTypes.IMPLEMENTS_CLAUSE             :
+            case GroovyTokenTypes.IMPORT                        :
+            case GroovyTokenTypes.LITERAL_as                    :
+            case GroovyTokenTypes.LITERAL_assert                :
+            case GroovyTokenTypes.LITERAL_boolean               :
+            case GroovyTokenTypes.LITERAL_break                 :
+            case GroovyTokenTypes.LITERAL_byte                  :
+            case GroovyTokenTypes.LITERAL_case                  :
+            case GroovyTokenTypes.LITERAL_catch                 :
+            case GroovyTokenTypes.LITERAL_char                  :
+            case GroovyTokenTypes.LITERAL_class                 :
+            case GroovyTokenTypes.LITERAL_continue              :
+            case GroovyTokenTypes.LITERAL_def                   :
+            case GroovyTokenTypes.LITERAL_default               :
+            case GroovyTokenTypes.LITERAL_double                :
+            case GroovyTokenTypes.LITERAL_else                  :
+            case GroovyTokenTypes.LITERAL_enum                  :
+            case GroovyTokenTypes.LITERAL_extends               :
+            case GroovyTokenTypes.LITERAL_false                 :
+            case GroovyTokenTypes.LITERAL_finally               :
+            case GroovyTokenTypes.LITERAL_float                 :
+            case GroovyTokenTypes.LITERAL_for                   :
+            case GroovyTokenTypes.LITERAL_if                    :
+            case GroovyTokenTypes.LITERAL_implements            :
+            case GroovyTokenTypes.LITERAL_import                :
+            case GroovyTokenTypes.LITERAL_in                    :
+            case GroovyTokenTypes.LITERAL_instanceof            :
+            case GroovyTokenTypes.LITERAL_int                   :
+            case GroovyTokenTypes.LITERAL_interface             :
+            case GroovyTokenTypes.LITERAL_long                  :
+            case GroovyTokenTypes.LITERAL_native                :
+            case GroovyTokenTypes.LITERAL_new                   :
+            case GroovyTokenTypes.LITERAL_null                  :
+            case GroovyTokenTypes.LITERAL_package               :
+            case GroovyTokenTypes.LITERAL_private               :
+            case GroovyTokenTypes.LITERAL_protected             :
+            case GroovyTokenTypes.LITERAL_public                :
+            case GroovyTokenTypes.LITERAL_return                :
+            case GroovyTokenTypes.LITERAL_short                 :
+            case GroovyTokenTypes.LITERAL_static                :
+            case GroovyTokenTypes.LITERAL_super                 :
+            case GroovyTokenTypes.LITERAL_switch                :
+            case GroovyTokenTypes.LITERAL_synchronized          :
+            case GroovyTokenTypes.LITERAL_this                  :
+            case GroovyTokenTypes.LITERAL_threadsafe            :
+            case GroovyTokenTypes.LITERAL_throw                 :
+            case GroovyTokenTypes.LITERAL_throws                :
+            case GroovyTokenTypes.LITERAL_transient             :
+            case GroovyTokenTypes.LITERAL_true                  :
+            case GroovyTokenTypes.LITERAL_try                   :
+            case GroovyTokenTypes.LITERAL_void                  :
+            case GroovyTokenTypes.LITERAL_volatile              :
+            case GroovyTokenTypes.LITERAL_while                 :
+            case GroovyTokenTypes.PACKAGE_DEF                   :
+            case GroovyTokenTypes.TYPE                          :
+                colour = blue;
+                break;
+
+            case GroovyTokenTypes.IDENT                         :
+                colour = cyan;
+                break;
+
+            default:
+                colour = black;
+                break;
+        }
+
+        // leaf nodes that haven't been coloured yet
+        if (black.equals(colour) && t.getNumberOfChildren() == 0) {
+            colour = cyan;
+        }
+
+
+
+        return colour;
+    }
+
+    private String getName(GroovySourceAST t) {
+        String name = tokenNames[t.getType()] + " <" + t.getType() + ">";
+        if (!(escape(tokenNames[t.getType()]).equals(escape(t.getText())))) {
+            name = name + " : " + t.getText();
+        }
+        switch (t.getType()) {
+            case GroovyTokenTypes.METHOD_DEF :
+            case GroovyTokenTypes.VARIABLE_DEF :
+                GroovySourceAST identNode = t.childOfType(GroovyTokenTypes.IDENT);
+                if (identNode != null) {
+                    name = name + " : " + identNode.getText() + "";
+                }
+        }
+        name = escape(name);
+        if (sourceBuffer != null) {
+            name += "&#xa;";
+            name += t.getLine() + "," + t.getColumn() + " - " + t.getLineLast() + "," + t.getColumnLast();
+            name += "&#xa;";
+            name += escape(sourceBuffer.getSnippet(new LineColumn(t.getLine(), t.getColumn()), new LineColumn(t.getLineLast(), t.getColumnLast())));
+        }
+        return name;
+    }
+
+    private String escape(String name) {
+        if (name == null) return null;
+        // remove middle of large bits of text
+        if (name.length() > 200) {
+            name = name.substring(0,100) + " ..... " + name.substring(name.length() - 100);
+        }
+        name = name.replace('"',' ');
+        name = name.replace('\'',' ');
+        name = name.replaceAll("&","&amp;");
+        name = name.replaceAll("<","&lt;");
+        name = name.replaceAll(">","&gt;");
+        name = name.trim();
+        return name;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/NodeAsHTMLPrinter.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/NodeAsHTMLPrinter.java
new file mode 100644
index 0000000..8f9b622
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/NodeAsHTMLPrinter.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import java.io.PrintStream;
+import java.util.Stack;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+
+/**
+ * A visitor that prints a html tags of each node to the supplied PrintStream
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+public class NodeAsHTMLPrinter extends VisitorAdapter {
+    private final String[] tokenNames;
+    private final PrintStream out;
+    private final Stack stack;
+
+    /**
+     * A visitor that prints a html tags, for each node, to the supplied PrintStream.
+     * @param out supplied PrintStream to output nodes to
+     * @param tokenNames an array of token names to use
+     */
+    public NodeAsHTMLPrinter(PrintStream out,String[] tokenNames) {
+        this.tokenNames = tokenNames;
+        this.out = out;
+        this.stack = new Stack();
+    }
+
+    public void setUp() {
+        out.println("<html><head></head><body><pre>");
+    }
+
+    public void visitDefault(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            out.print("<code title=" + quote(tokenNames[t.getType()]) + "><font color='#" + colour(t) + "'>");
+        } else if (visit == CLOSING_VISIT) {
+            out.print("</font></code>");
+        }
+    }
+
+    private String quote(String tokenName) {
+        if (tokenName.length() > 0 && tokenName.charAt(0) != '\'')
+            return "'" + tokenName + "'";
+        else
+            return "\"" + tokenName + "\"";
+    }
+
+    public void tearDown() {
+        out.println("</pre></body></html>");
+    }
+
+    private String colour(GroovySourceAST t) {
+        String black = "000000";
+        String blue = "17178B";
+        String green = "008000";
+        //String purple = "7C308D";
+        String colour = black;
+        switch (t.getType()) {
+            case GroovyTokenTypes.ABSTRACT                      :
+            case GroovyTokenTypes.ANNOTATION                    :
+            case GroovyTokenTypes.ANNOTATIONS                   :
+            case GroovyTokenTypes.ANNOTATION_ARRAY_INIT         :
+            case GroovyTokenTypes.ANNOTATION_DEF                :
+            case GroovyTokenTypes.ANNOTATION_FIELD_DEF          :
+            case GroovyTokenTypes.ANNOTATION_MEMBER_VALUE_PAIR  :
+            case GroovyTokenTypes.ARRAY_DECLARATOR              :
+            case GroovyTokenTypes.ASSIGN                        :
+            case GroovyTokenTypes.AT                            :
+            case GroovyTokenTypes.BAND                          :
+            case GroovyTokenTypes.BAND_ASSIGN                   :
+            case GroovyTokenTypes.BIG_SUFFIX                    :
+            case GroovyTokenTypes.BLOCK                         :
+            case GroovyTokenTypes.BNOT                          :
+            case GroovyTokenTypes.BOR                           :
+            case GroovyTokenTypes.BOR_ASSIGN                    :
+            case GroovyTokenTypes.BSR                           :
+            case GroovyTokenTypes.BSR_ASSIGN                    :
+            case GroovyTokenTypes.BXOR                          :
+            case GroovyTokenTypes.BXOR_ASSIGN                   :
+            case GroovyTokenTypes.CASE_GROUP                    :
+            case GroovyTokenTypes.CLOSABLE_BLOCK                :
+            case GroovyTokenTypes.CLOSABLE_BLOCK_OP             :
+            case GroovyTokenTypes.COLON                         :
+            case GroovyTokenTypes.COMMA                         :
+            case GroovyTokenTypes.COMPARE_TO                    :
+            case GroovyTokenTypes.CTOR_CALL                     :
+            case GroovyTokenTypes.CTOR_IDENT                    :
+            case GroovyTokenTypes.DEC                           :
+            case GroovyTokenTypes.DIGIT                         :
+            case GroovyTokenTypes.DIV                           :
+            case GroovyTokenTypes.DIV_ASSIGN                    :
+            case GroovyTokenTypes.DOLLAR                        :
+            case GroovyTokenTypes.DOT                           :
+            case GroovyTokenTypes.DYNAMIC_MEMBER                :
+            case GroovyTokenTypes.ELIST                         :
+            case GroovyTokenTypes.EMPTY_STAT                    :
+            case GroovyTokenTypes.ENUM_CONSTANT_DEF             :
+            case GroovyTokenTypes.ENUM_DEF                      :
+            case GroovyTokenTypes.EOF                           :
+            case GroovyTokenTypes.EQUAL                         :
+            case GroovyTokenTypes.ESC                           :
+            case GroovyTokenTypes.EXPONENT                      :
+            case GroovyTokenTypes.EXPR                          :
+            case GroovyTokenTypes.FINAL                         :
+            case GroovyTokenTypes.FLOAT_SUFFIX                  :
+            case GroovyTokenTypes.FOR_CONDITION                 :
+            case GroovyTokenTypes.FOR_EACH_CLAUSE               :
+            case GroovyTokenTypes.FOR_INIT                      :
+            case GroovyTokenTypes.FOR_IN_ITERABLE               :
+            case GroovyTokenTypes.FOR_ITERATOR                  :
+            case GroovyTokenTypes.GE                            :
+            case GroovyTokenTypes.GT                            :
+            case GroovyTokenTypes.HEX_DIGIT                     :
+            case GroovyTokenTypes.IDENT                         :
+            case GroovyTokenTypes.IMPLICIT_PARAMETERS           :
+            case GroovyTokenTypes.INC                           :
+            case GroovyTokenTypes.INDEX_OP                      :
+            case GroovyTokenTypes.INSTANCE_INIT                 :
+            case GroovyTokenTypes.INTERFACE_DEF                 :
+            case GroovyTokenTypes.LABELED_ARG                   :
+            case GroovyTokenTypes.LABELED_STAT                  :
+            case GroovyTokenTypes.LAND                          :
+            case GroovyTokenTypes.LBRACK                        :
+            case GroovyTokenTypes.LCURLY                        :
+            case GroovyTokenTypes.LE                            :
+            case GroovyTokenTypes.LETTER                        :
+            case GroovyTokenTypes.LIST_CONSTRUCTOR              :
+            case GroovyTokenTypes.LNOT                          :
+            case GroovyTokenTypes.LOR                           :
+            case GroovyTokenTypes.LPAREN                        :
+            case GroovyTokenTypes.LT                            :
+            case GroovyTokenTypes.MAP_CONSTRUCTOR               :
+            case GroovyTokenTypes.MEMBER_POINTER                :
+            case GroovyTokenTypes.METHOD_CALL                   :
+            case GroovyTokenTypes.METHOD_DEF                    :
+            case GroovyTokenTypes.MINUS                         :
+            case GroovyTokenTypes.MINUS_ASSIGN                  :
+            case GroovyTokenTypes.ML_COMMENT                    :
+            case GroovyTokenTypes.MOD                           :
+            case GroovyTokenTypes.MODIFIERS                     :
+            case GroovyTokenTypes.MOD_ASSIGN                    :
+            case GroovyTokenTypes.NLS                           :
+            case GroovyTokenTypes.NOT_EQUAL                     :
+            case GroovyTokenTypes.NULL_TREE_LOOKAHEAD           :
+            case GroovyTokenTypes.NUM_BIG_DECIMAL               :
+            case GroovyTokenTypes.NUM_BIG_INT                   :
+            case GroovyTokenTypes.NUM_DOUBLE                    :
+            case GroovyTokenTypes.NUM_FLOAT                     :
+            case GroovyTokenTypes.NUM_INT                       :
+            case GroovyTokenTypes.NUM_LONG                      :
+            case GroovyTokenTypes.OBJBLOCK                      :
+            case GroovyTokenTypes.ONE_NL                        :
+            case GroovyTokenTypes.OPTIONAL_DOT                  :
+            case GroovyTokenTypes.PARAMETERS                    :
+            case GroovyTokenTypes.PARAMETER_DEF                 :
+            case GroovyTokenTypes.PLUS                          :
+            case GroovyTokenTypes.PLUS_ASSIGN                   :
+            case GroovyTokenTypes.POST_DEC                      :
+            case GroovyTokenTypes.POST_INC                      :
+            case GroovyTokenTypes.QUESTION                      :
+            case GroovyTokenTypes.RANGE_EXCLUSIVE               :
+            case GroovyTokenTypes.RANGE_INCLUSIVE               :
+            case GroovyTokenTypes.RBRACK                        :
+            case GroovyTokenTypes.RCURLY                        :
+            case GroovyTokenTypes.REGEXP_CTOR_END               :
+            case GroovyTokenTypes.REGEXP_SYMBOL                 :
+            case GroovyTokenTypes.REGEX_FIND                    :
+            case GroovyTokenTypes.REGEX_MATCH                   :
+            case GroovyTokenTypes.RPAREN                        :
+            case GroovyTokenTypes.SELECT_SLOT                   :
+            case GroovyTokenTypes.SEMI                          :
+            case GroovyTokenTypes.SH_COMMENT                    :
+            case GroovyTokenTypes.SL                            :
+            case GroovyTokenTypes.SLIST                         :
+            case GroovyTokenTypes.SL_ASSIGN                     :
+            case GroovyTokenTypes.SL_COMMENT                    :
+            case GroovyTokenTypes.SPREAD_ARG                    :
+            case GroovyTokenTypes.SPREAD_DOT                    :
+            case GroovyTokenTypes.SPREAD_MAP_ARG                :
+            case GroovyTokenTypes.SR                            :
+            case GroovyTokenTypes.SR_ASSIGN                     :
+            case GroovyTokenTypes.STAR                          :
+            case GroovyTokenTypes.STAR_ASSIGN                   :
+            case GroovyTokenTypes.STAR_STAR                     :
+            case GroovyTokenTypes.STAR_STAR_ASSIGN              :
+            case GroovyTokenTypes.STATIC_IMPORT                 :
+            case GroovyTokenTypes.STATIC_INIT                   :
+            case GroovyTokenTypes.STRICTFP                      :
+            case GroovyTokenTypes.STRING_CH                     :
+            case GroovyTokenTypes.STRING_CONSTRUCTOR            :
+            case GroovyTokenTypes.STRING_CTOR_END               :
+            case GroovyTokenTypes.STRING_CTOR_MIDDLE            :
+            case GroovyTokenTypes.STRING_CTOR_START             :
+            case GroovyTokenTypes.STRING_NL                     :
+            case GroovyTokenTypes.SUPER_CTOR_CALL               :
+            case GroovyTokenTypes.TRIPLE_DOT                    :
+            case GroovyTokenTypes.TYPECAST                      :
+            case GroovyTokenTypes.TYPE_ARGUMENT                 :
+            case GroovyTokenTypes.TYPE_ARGUMENTS                :
+            case GroovyTokenTypes.TYPE_LOWER_BOUNDS             :
+            case GroovyTokenTypes.TYPE_PARAMETER                :
+            case GroovyTokenTypes.TYPE_PARAMETERS               :
+            case GroovyTokenTypes.TYPE_UPPER_BOUNDS             :
+            case GroovyTokenTypes.UNARY_MINUS                   :
+            case GroovyTokenTypes.UNARY_PLUS                    :
+            case GroovyTokenTypes.UNUSED_CONST                  :
+            case GroovyTokenTypes.UNUSED_DO                     :
+            case GroovyTokenTypes.UNUSED_GOTO                   :
+            case GroovyTokenTypes.VARIABLE_DEF                  :
+            case GroovyTokenTypes.VARIABLE_PARAMETER_DEF        :
+            case GroovyTokenTypes.VOCAB                         :
+            case GroovyTokenTypes.WILDCARD_TYPE                 :
+            case GroovyTokenTypes.WS                            :
+                colour = black;
+                break;
+
+            case GroovyTokenTypes.STRING_LITERAL                :
+            case GroovyTokenTypes.REGEXP_LITERAL                :
+                colour = green;
+                break;
+
+            case GroovyTokenTypes.CLASS_DEF                     :
+            case GroovyTokenTypes.EXTENDS_CLAUSE                :
+            case GroovyTokenTypes.IMPLEMENTS_CLAUSE             :
+            case GroovyTokenTypes.IMPORT                        :
+            case GroovyTokenTypes.LITERAL_as                    :
+            case GroovyTokenTypes.LITERAL_assert                :
+            case GroovyTokenTypes.LITERAL_boolean               :
+            case GroovyTokenTypes.LITERAL_break                 :
+            case GroovyTokenTypes.LITERAL_byte                  :
+            case GroovyTokenTypes.LITERAL_case                  :
+            case GroovyTokenTypes.LITERAL_catch                 :
+            case GroovyTokenTypes.LITERAL_char                  :
+            case GroovyTokenTypes.LITERAL_class                 :
+            case GroovyTokenTypes.LITERAL_continue              :
+            case GroovyTokenTypes.LITERAL_def                   :
+            case GroovyTokenTypes.LITERAL_default               :
+            case GroovyTokenTypes.LITERAL_double                :
+            case GroovyTokenTypes.LITERAL_else                  :
+            case GroovyTokenTypes.LITERAL_enum                  :
+            case GroovyTokenTypes.LITERAL_extends               :
+            case GroovyTokenTypes.LITERAL_false                 :
+            case GroovyTokenTypes.LITERAL_finally               :
+            case GroovyTokenTypes.LITERAL_float                 :
+            case GroovyTokenTypes.LITERAL_for                   :
+            case GroovyTokenTypes.LITERAL_if                    :
+            case GroovyTokenTypes.LITERAL_implements            :
+            case GroovyTokenTypes.LITERAL_import                :
+            case GroovyTokenTypes.LITERAL_in                    :
+            case GroovyTokenTypes.LITERAL_instanceof            :
+            case GroovyTokenTypes.LITERAL_int                   :
+            case GroovyTokenTypes.LITERAL_interface             :
+            case GroovyTokenTypes.LITERAL_long                  :
+            case GroovyTokenTypes.LITERAL_native                :
+            case GroovyTokenTypes.LITERAL_new                   :
+            case GroovyTokenTypes.LITERAL_null                  :
+            case GroovyTokenTypes.LITERAL_package               :
+            case GroovyTokenTypes.LITERAL_private               :
+            case GroovyTokenTypes.LITERAL_protected             :
+            case GroovyTokenTypes.LITERAL_public                :
+            case GroovyTokenTypes.LITERAL_return                :
+            case GroovyTokenTypes.LITERAL_short                 :
+            case GroovyTokenTypes.LITERAL_static                :
+            case GroovyTokenTypes.LITERAL_super                 :
+            case GroovyTokenTypes.LITERAL_switch                :
+            case GroovyTokenTypes.LITERAL_synchronized          :
+            case GroovyTokenTypes.LITERAL_this                  :
+            case GroovyTokenTypes.LITERAL_threadsafe            :
+            case GroovyTokenTypes.LITERAL_throw                 :
+            case GroovyTokenTypes.LITERAL_throws                :
+            case GroovyTokenTypes.LITERAL_transient             :
+            case GroovyTokenTypes.LITERAL_true                  :
+            case GroovyTokenTypes.LITERAL_try                   :
+            case GroovyTokenTypes.LITERAL_void                  :
+            case GroovyTokenTypes.LITERAL_volatile              :
+            case GroovyTokenTypes.LITERAL_while                 :
+            case GroovyTokenTypes.PACKAGE_DEF                   :
+            case GroovyTokenTypes.TYPE                          :
+                colour = blue;
+                break;
+
+            default:
+                colour = black;
+                break;
+        }
+        return colour;
+    }
+
+    public void push(GroovySourceAST t) {
+        stack.push(t);
+    }
+    public GroovySourceAST pop() {
+        if (!stack.empty()) {
+            return (GroovySourceAST) stack.pop();
+        }
+        return null;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/NodeCollector.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/NodeCollector.java
new file mode 100644
index 0000000..4e0da99
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/NodeCollector.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+
+/**
+ * A simple antlr AST visitor that collects all nodes into a List.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision: 4032 $
+ */
+
+public class NodeCollector extends VisitorAdapter {
+	private List nodes;
+	public NodeCollector() {
+		nodes = new ArrayList();
+	}
+	public List getNodes() {
+		return nodes;
+	}
+    public void visitDefault(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+        	nodes.add(t);
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/NodePrinter.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/NodePrinter.java
new file mode 100644
index 0000000..b2b36b7
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/NodePrinter.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import java.io.PrintStream;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+
+/**
+ * A simple antlr AST visitor that outputs the tokenName of each node in a pseudo xml style.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+public class NodePrinter extends VisitorAdapter {
+    private String[] tokenNames;
+    private PrintStream out;
+
+    /**
+     * A visitor that prints a pseudo xml output to the supplied PrintStream
+     * @param out supplied PrintStream to output nodes to
+     * @param tokenNames an array of token names to use
+     */
+    public NodePrinter(PrintStream out,String[] tokenNames) {
+        this.tokenNames = tokenNames;
+        this.out = out;
+    }
+
+    public void visitDefault(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            out.print("<"+ tokenNames[t.getType()] + ">");
+        } else {
+            out.print("</" + tokenNames[t.getType()] + ">");
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/PreOrderTraversal.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/PreOrderTraversal.java
new file mode 100644
index 0000000..c92f617
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/PreOrderTraversal.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+
+/**
+ * A simple preorder traversal over the supplied antlr AST.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+public class PreOrderTraversal extends TraversalHelper {
+    
+    /**
+     * A simple preorder traversal over the supplied antlr AST.
+     * @param visitor the Visitor to call for each node visited 
+     */
+    public PreOrderTraversal(Visitor visitor) {
+        super(visitor);
+    }
+
+    public void accept(GroovySourceAST currentNode) {
+        push(currentNode);
+        openingVisit(currentNode);
+        acceptChildren(currentNode);
+        closingVisit(currentNode);
+        pop();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/SourceCodeTraversal.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/SourceCodeTraversal.java
new file mode 100644
index 0000000..1b2deac
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/SourceCodeTraversal.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2005 Jeremy Rayner
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+
+/**
+ * A treewalker for the antlr generated AST that attempts to visit the
+ * AST nodes in the order needed to generate valid groovy source code.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+public class SourceCodeTraversal extends TraversalHelper {
+    /**
+     * Constructs a treewalker for the antlr generated AST that attempts to visit the
+     * AST nodes in the order needed to generate valid groovy source code.
+     * @param visitor the visitor implementation to call for each AST node.
+     */
+    public SourceCodeTraversal(Visitor visitor) {
+        super(visitor);
+    }
+
+    /**
+     * gather, sort and process all unvisited nodes
+     * @param t the AST to process
+     */
+    public void setUp(GroovySourceAST t) {
+        super.setUp(t);
+        
+        // gather and sort all unvisited AST nodes
+        unvisitedNodes = new ArrayList();
+        traverse(t);
+        Collections.sort(unvisitedNodes);
+    }
+
+    /**
+     * traverse an AST node
+     * @param t the AST node to traverse
+     */
+    private void traverse(GroovySourceAST t) {
+        if (t == null) { return; }
+        if (unvisitedNodes != null) {
+           unvisitedNodes.add(t);
+        }
+        GroovySourceAST child = (GroovySourceAST)t.getFirstChild();
+        if (child != null) {
+            traverse(child);
+        }
+        GroovySourceAST sibling = (GroovySourceAST)t.getNextSibling();
+        if (sibling != null) {
+            traverse(sibling);
+        }
+    }
+
+    protected void accept(GroovySourceAST currentNode) {
+        if (currentNode != null && unvisitedNodes != null && unvisitedNodes.size() > 0) {
+            GroovySourceAST t = currentNode;
+
+            if (!(unvisitedNodes.contains(currentNode))) {
+                return;
+            }
+            push(t);
+            switch (t.getType()) {
+                case GroovyTokenTypes.QUESTION: // expr?foo:bar
+                    accept_FirstChild_v_SecondChild_v_ThirdChild_v(t);
+                    break;
+
+                case GroovyTokenTypes.CASE_GROUP: //
+                case GroovyTokenTypes.LITERAL_instanceof: // foo instanceof MyType
+                    accept_FirstChild_v_SecondChildsChildren_v(t);
+                    break;
+
+                case GroovyTokenTypes.ANNOTATION:
+                    accept_v_FirstChild_2ndv_SecondChild_v___LastChild_v(t);
+                    break;
+
+                case GroovyTokenTypes.CLOSURE_LIST: // (a=1; a<10; a++)
+                case GroovyTokenTypes.ELIST: // a,b,c
+                case GroovyTokenTypes.PARAMETERS: // a,b,c
+                case GroovyTokenTypes.TYPE_ARGUMENTS: // <String, Object>
+                case GroovyTokenTypes.STRING_CONSTRUCTOR: // "foo${bar}wibble"
+                case GroovyTokenTypes.TYPE_PARAMETER: // class Foo<T extends F>
+                case GroovyTokenTypes.TYPE_PARAMETERS: // class Foo<T>
+                case GroovyTokenTypes.TYPE_UPPER_BOUNDS: // class Foo<T extends F>
+                    accept_v_FirstChild_v_SecondChild_v___LastChild_v(t);
+                    // todo : confirm that TYPE_LOWER_BOUNDS does not have multiple children
+                    break;
+
+                case GroovyTokenTypes.VARIABLE_PARAMETER_DEF: // void f(String ... others) {}
+                    accept_v_FirstChild_SecondChild_v_ThirdChild_v(t);
+                    break;
+
+                case GroovyTokenTypes.INDEX_OP:
+                    accept_FirstChild_v_SecondChild_v(t);
+                    break;
+
+                case GroovyTokenTypes.ENUM_CONSTANT_DEF: // enum Foo(THESE,ARE,THEY)
+                case GroovyTokenTypes.EXPR:
+                case GroovyTokenTypes.IMPORT:
+                case GroovyTokenTypes.VARIABLE_DEF:
+                case GroovyTokenTypes.METHOD_DEF:
+                case GroovyTokenTypes.OBJBLOCK: //class Foo {def bar()}  <-- this block
+                case GroovyTokenTypes.PARAMETER_DEF: // void f(String me) {}
+                case GroovyTokenTypes.SLIST: // list of expressions, variable defs etc
+                    accept_v_AllChildren_v(t);
+                    break;
+
+                case GroovyTokenTypes.ANNOTATION_MEMBER_VALUE_PAIR: // @Blue(foo=123)
+                case GroovyTokenTypes.ASSIGN: // a = b
+                case GroovyTokenTypes.BAND_ASSIGN: // a &= b
+                case GroovyTokenTypes.BOR_ASSIGN: // a |= b
+                case GroovyTokenTypes.BSR_ASSIGN: // a >>>= b
+                case GroovyTokenTypes.BXOR_ASSIGN: // a ^= b
+                case GroovyTokenTypes.COMPARE_TO: // a <=> b
+                case GroovyTokenTypes.DIV_ASSIGN: // a /= b
+                case GroovyTokenTypes.EQUAL: // a == b
+                case GroovyTokenTypes.MINUS_ASSIGN: // a -= b
+                case GroovyTokenTypes.MOD_ASSIGN: // a %= b
+                case GroovyTokenTypes.NOT_EQUAL: // a != b
+                case GroovyTokenTypes.PLUS_ASSIGN: // a += b
+                case GroovyTokenTypes.REGEX_FIND: // a =~ b
+                case GroovyTokenTypes.REGEX_MATCH: // a ==~ b
+                case GroovyTokenTypes.SL_ASSIGN: // a <<= b
+                case GroovyTokenTypes.SR_ASSIGN: // a >>= b
+                case GroovyTokenTypes.STAR_ASSIGN: // a *= b
+                case GroovyTokenTypes.STAR_STAR_ASSIGN: // x **= 3
+                    if (t.childAt(1) != null) {
+                        accept_FirstChild_v_RestOfTheChildren(t);
+                    } else {
+                        accept_v_FirstChild_v_RestOfTheChildren(t);
+                    }
+                    break;
+
+                case GroovyTokenTypes.ANNOTATION_FIELD_DEF: // @interface Foo{ int bar()...
+                    accept_FirstSecondAndThirdChild_v_v_ForthChild(t);
+                    break;
+                    
+                case GroovyTokenTypes.ANNOTATION_DEF: // @interface Foo...
+                case GroovyTokenTypes.BAND: // 1 & 2
+                case GroovyTokenTypes.BOR: // 1 | 2
+                case GroovyTokenTypes.BSR: // 1 >>> 2
+                case GroovyTokenTypes.BXOR: // 1 ^ 2
+                case GroovyTokenTypes.CLASS_DEF: // class Foo...
+                case GroovyTokenTypes.CTOR_IDENT: // private Foo() {...
+                case GroovyTokenTypes.DIV: //  3/4
+                case GroovyTokenTypes.DOT: // foo.bar
+                case GroovyTokenTypes.ENUM_DEF: // enum Foo...
+                case GroovyTokenTypes.GE: // a >= b
+                case GroovyTokenTypes.GT: // a > b
+                case GroovyTokenTypes.INTERFACE_DEF: // interface Foo...
+                case GroovyTokenTypes.LABELED_ARG: // myMethod(name:"Jez")
+                case GroovyTokenTypes.LABELED_STAT: // foo:x=1                    	
+                case GroovyTokenTypes.LAND: // true && false
+                case GroovyTokenTypes.LE: // a <= b
+                case GroovyTokenTypes.LITERAL_as: // foo as Bar
+                case GroovyTokenTypes.LITERAL_in: // if (i in myList) ...
+                case GroovyTokenTypes.LOR: // true && false
+                case GroovyTokenTypes.LT: // a < b
+                case GroovyTokenTypes.MEMBER_POINTER: // this.&foo()
+                case GroovyTokenTypes.MOD: //  4 % 3
+                case GroovyTokenTypes.MINUS: // 1 - 1
+                case GroovyTokenTypes.OPTIONAL_DOT: // foo?.bar
+                case GroovyTokenTypes.PACKAGE_DEF:
+                case GroovyTokenTypes.PLUS: // 1 + 1
+                case GroovyTokenTypes.RANGE_EXCLUSIVE: // [1..<10]
+                case GroovyTokenTypes.RANGE_INCLUSIVE: // [1..10]
+                case GroovyTokenTypes.SL: // a << b
+                case GroovyTokenTypes.SPREAD_DOT: // foo*.bar
+                case GroovyTokenTypes.SR: // a >> b
+                case GroovyTokenTypes.STAR: // a * b   or    import foo.*
+                case GroovyTokenTypes.STAR_STAR: // x ** 3
+                    accept_FirstChild_v_RestOfTheChildren(t);
+                    break;
+
+                case GroovyTokenTypes.CTOR_CALL:
+                case GroovyTokenTypes.METHOD_CALL:
+                    if (t.getNumberOfChildren() == 2 && t.childAt(1) != null && t.childAt(1).getType() == GroovyTokenTypes.CLOSABLE_BLOCK) {
+                        // myMethod {...
+                        accept_FirstChild_v_SecondChild(t);
+                    } else {
+                        GroovySourceAST lastChild = t.childAt(t.getNumberOfChildren() -1);
+                        if (lastChild != null && lastChild.getType() == GroovyTokenTypes.CLOSABLE_BLOCK) {
+                            // myMethod(a,b) {...
+                            accept_FirstChild_v_RestOfTheChildren_v_LastChild(t);
+                        } else {
+                            // myMethod(a,b)
+                            accept_FirstChild_v_RestOfTheChildren_v(t);
+                        }
+                    }
+                    break;
+
+                case GroovyTokenTypes.LITERAL_while:
+//deprecated                case GroovyTokenTypes.LITERAL_with:
+                case GroovyTokenTypes.TYPECAST: // (String)itr.next()
+                    accept_v_FirstChildsFirstChild_v_RestOfTheChildren(t);
+                    break;
+
+                case GroovyTokenTypes.LITERAL_if: // if (grandchild) {child1} else {child2} ...
+                    accept_v_FirstChildsFirstChild_v_Child2_Child3_v_Child4_v___v_LastChild(t);
+                    break;
+
+                case GroovyTokenTypes.CLOSABLE_BLOCK: // [1,2,3].each {foo(it)}  <-- Closure
+                    if (t.childAt(0) != null && t.childAt(0).getType() == GroovyTokenTypes.IMPLICIT_PARAMETERS) {
+                        accept_v_AllChildren_v(t);
+                    } else {
+                        accept_v_FirstChild_v_RestOfTheChildren_v(t);
+                    }
+                    break;
+
+                case GroovyTokenTypes.FOR_IN_ITERABLE:
+                case GroovyTokenTypes.LITERAL_for:
+                case GroovyTokenTypes.LITERAL_new:
+                case GroovyTokenTypes.LITERAL_switch:
+                    accept_v_FirstChild_v_RestOfTheChildren_v(t);
+                    break;
+ 
+                case GroovyTokenTypes.ANNOTATIONS: // just like modifiers but for package/enum declarations
+                case GroovyTokenTypes.LITERAL_assert:
+                case GroovyTokenTypes.LITERAL_catch:
+                case GroovyTokenTypes.LITERAL_synchronized:
+                case GroovyTokenTypes.LITERAL_try:
+                case GroovyTokenTypes.MODIFIERS:
+                    accept_v_FirstChild_v_RestOfTheChildren(t);
+                    break;
+                    
+                case GroovyTokenTypes.WILDCARD_TYPE:
+                    accept_v_Siblings_v(t);
+                    break;
+
+                default:
+                    accept_v_FirstChild_v(t);
+                    break;
+            }
+            pop();
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/SourcePrinter.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/SourcePrinter.java
new file mode 100644
index 0000000..ddf12b3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/SourcePrinter.java
@@ -0,0 +1,1069 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import java.io.PrintStream;
+import java.util.Stack;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+
+/**
+ * An antlr AST visitor that prints groovy source code for each visited node
+ * to the supplied PrintStream.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+public class SourcePrinter extends VisitorAdapter {
+    private final String[] tokenNames;
+    private int tabLevel;
+    private int lastLinePrinted;
+    private final boolean newLines;
+    protected final PrintStream out;
+    private String className;
+    private final Stack stack;
+    private int stringConstructorCounter;
+
+    /**
+     * A visitor that prints groovy source code for each node visited.
+     * @param out where to print the source code to
+     * @param tokenNames an array of token names from antlr
+     */
+    public SourcePrinter(PrintStream out,String[] tokenNames) {
+        this(out,tokenNames,true);
+    }
+
+    /**
+     * A visitor that prints groovy source code for each node visited.
+     * @param out where to print the source code to
+     * @param tokenNames an array of token names from antlr
+     * @param newLines output newline character
+     */
+    public SourcePrinter(PrintStream out,String[] tokenNames, boolean newLines) {
+        this.tokenNames = tokenNames;
+        tabLevel = 0;
+        lastLinePrinted = 0;
+        this.out = out;
+        this.newLines = newLines;
+        this.stack = new Stack();
+    }
+    
+
+	public void visitAbstract(GroovySourceAST t, int visit) {
+		print(t,visit,"abstract ",null,null);
+	}
+
+	public void visitAnnotation(GroovySourceAST t, int visit) {
+		if (visit == OPENING_VISIT) {
+			print(t,visit,"@");
+		}
+		if (visit == SECOND_VISIT) {
+			print(t,visit,"(");
+		}
+		if (visit == SUBSEQUENT_VISIT) {
+			print(t,visit,", ");
+		}
+		if (visit == CLOSING_VISIT) {
+			if (t.getNumberOfChildren() > 1) {
+				print(t,visit,") ");
+			} else {
+				print(t,visit," ");
+			}
+		}
+
+    }
+
+    public void visitAnnotations(GroovySourceAST t, int visit) {
+    	// do nothing
+    }
+
+    public void visitAnnotationDef(GroovySourceAST t,int visit) {
+        print(t,visit,"@interface ",null,null);
+    }
+
+	public void visitAnnotationFieldDef(GroovySourceAST t, int visit) {
+    	print(t,visit,"() ","default ",null);
+	}
+
+	public void visitAnnotationMemberValuePair(GroovySourceAST t, int visit) {
+		print(t,visit," = ",null,null);
+	}
+
+	public void visitArrayDeclarator(GroovySourceAST t, int visit) {
+		//<ARRAY_DECLARATOR>int</ARRAY_DECLARATOR> primes = new int(<ARRAY_DECLARATOR>5</ARRAY_DECLARATOR>)
+		if (getParentNode().getType() == GroovyTokenTypes.TYPE ||
+				getParentNode().getType() == GroovyTokenTypes.TYPECAST) { // ugly hack
+			// type defintion, i.e.   int[] x;
+			print(t,visit,null,null,"[]");
+		} else {
+			// usually in new, i.e.   def y = new int[5];
+			print(t,visit,"[",null,"]");
+		}
+	}
+
+	public void visitAssign(GroovySourceAST t,int visit) {
+        print(t,visit," = ",null,null);
+    }
+	
+    // visitAt() ...
+    //   token type 'AT' should never be visited, as annotation definitions and usage, and
+    //   direct field access should have all moved this token out of the way. No test needed.
+
+	//   one of the BAND tokens is actually replaced by TYPE_UPPER_BOUNDS (e.g. class Foo<T extends C & I> {T t} )
+    public void visitBand(GroovySourceAST t, int visit) {
+        print(t,visit," & ",null,null);
+    }
+
+	public void visitBandAssign(GroovySourceAST t,int visit) {
+        print(t,visit," &= ",null,null);
+    }
+	
+    // visitBigSuffix() ...
+	//   token type BIG_SUFFIX never created/visited, NUM_BIG_INT, NUM_BIG_DECIMAL instead...    
+    
+	// visitBlock() ...
+	//   token type BLOCK never created/visited, see CLOSABLE_BLOCK etc...
+	
+	public void visitBnot(GroovySourceAST t, int visit) {
+		print(t,visit,"~",null,null);
+	}
+	
+	// Note: old closure syntax using BOR is deprecated, and also never creates/visits a BOR node
+    public void visitBor(GroovySourceAST t, int visit) {
+        print(t,visit," | ",null,null);
+    }
+	
+	public void visitBorAssign(GroovySourceAST t,int visit) {
+        print(t,visit," |= ",null,null);
+    }
+	
+    public void visitBsr(GroovySourceAST t, int visit) {
+        print(t,visit," >>> ",null,null);
+    }
+	
+	public void visitBsrAssign(GroovySourceAST t,int visit) {
+        print(t,visit," >>>= ",null,null);
+    }
+	
+    public void visitBxor(GroovySourceAST t, int visit) {
+        print(t,visit," ^ ",null,null);
+    }
+	
+	public void visitBxorAssign(GroovySourceAST t,int visit) {
+        print(t,visit," ^= ",null,null);
+    }
+	
+    public void visitCaseGroup(GroovySourceAST t, int visit) {
+        if (visit == OPENING_VISIT) {
+            tabLevel++;
+        }
+        if (visit == CLOSING_VISIT) {
+            tabLevel--;
+        }
+    }
+
+    public void visitClassDef(GroovySourceAST t,int visit) {
+        print(t,visit,"class ",null,null);
+
+        if (visit == OPENING_VISIT) {
+            // store name of class away for use in constructor ident
+            className = t.childOfType(GroovyTokenTypes.IDENT).getText();
+        }
+    }
+
+    public void visitClosedBlock(GroovySourceAST t, int visit) {
+        printUpdatingTabLevel(t,visit,"{","-> ","}");
+    }
+    
+    public void visitClosureList(GroovySourceAST t, int visit) {
+    	print(t,visit,"(","; ",")");
+    }
+    // visitClosureOp ...
+	//   token type CLOSABLE_BLOCK_OP never created/visited, see CLOSABLE_BLOCK...
+	
+
+    // visitColon ...
+    //   token type COLON never created/visited, see LABELED_STAT, FOR_IN_ITERABLE, 
+    //   ASSERT, CASE, QUESTION, MAP_CONSTRUCTOR, LABELED_ARG, SPREAD_MAP_ARG
+
+    // visitComma ...
+    //   token type COMMA never created/visited,
+    //   see TYPE_ARGUMENTS, ANNOTATION, many others ...
+    
+    public void visitCompareTo(GroovySourceAST t,int visit) {
+        print(t,visit," <=> ",null,null);
+    }
+
+    public void visitCtorCall(GroovySourceAST t,int visit) {
+        printUpdatingTabLevel(t,visit,"this("," ",")");
+    }
+
+    public void visitCtorIdent(GroovySourceAST t, int visit) {
+        // use name of class for constructor from the class definition
+        print(t,visit,className,null,null);
+    }
+
+    public void visitDec(GroovySourceAST t, int visit) {
+    	print(t,visit,"--",null,null);
+    }
+    
+    // visitDigit ...
+    //    never created/visited
+    
+    public void visitDiv(GroovySourceAST t, int visit) {
+        print(t,visit," / ",null,null);
+    }
+
+	public void visitDivAssign(GroovySourceAST t,int visit) {
+        print(t,visit," /= ",null,null);
+    }
+	
+    // visitDollar ...
+    //   token type DOLLAR never created/visited, see SCOPE_ESCAPE instead
+    
+    public void visitDot(GroovySourceAST t,int visit) {
+        print(t,visit,".",null,null);
+    }
+    
+    public void visitDynamicMember(GroovySourceAST t, int visit) {
+    	if (t.childOfType(GroovyTokenTypes.STRING_CONSTRUCTOR) == null) {
+    		printUpdatingTabLevel(t,visit,"(",null,")");
+    	}
+    }
+    
+    public void visitElist(GroovySourceAST t,int visit) {
+    	if (getParentNode().getType() == GroovyTokenTypes.ENUM_CONSTANT_DEF) {
+    		print(t,visit,"(",", ",")");
+    	} else {
+    		print(t,visit,null,", ",null);
+    	}
+    }
+
+    // visitEmptyStat ...
+    //   token type EMPTY_STAT obsolete and should be removed, never visited/created
+    
+    public void visitEnumConstantDef(GroovySourceAST t,int visit) {
+    	GroovySourceAST sibling = (GroovySourceAST)t.getNextSibling();
+    	if (sibling != null && sibling.getType() == GroovyTokenTypes.ENUM_CONSTANT_DEF) {
+    		print(t,visit,null,null,", ");
+    	}
+    }
+
+    public void visitEnumDef(GroovySourceAST t,int visit) {
+        print(t,visit,"enum ",null,null);
+    }
+
+    // visitEof ...
+    //   token type EOF never visited/created
+
+    public void visitEqual(GroovySourceAST t,int visit) {
+        print(t,visit," == ",null,null);
+    }
+
+    // visitExponent ...
+    //   token type EXPONENT only used by lexer, never visited/created
+    
+    public void visitExpr(GroovySourceAST t,int visit) {
+    	// do nothing
+    }
+
+    public void visitExtendsClause(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            if (t.getNumberOfChildren() != 0) {
+                print(t,visit," extends ");
+            }
+        }
+    }
+    
+	public void visitFinal(GroovySourceAST t, int visit) {
+        print(t,visit,"final ",null,null);
+	}
+
+	// visitFloatSuffix ... never visited/created see NUM_DOUBLE or NUM_FLOAT instead
+	
+	public void visitForCondition(GroovySourceAST t, int visit) {
+    	print(t,visit," ; ",null,null);
+    }
+	
+	// visitForEachClause ... 
+	//   FOR_EACH_CLAUSE obsolete and should be removed, never visited/created
+
+    public void visitForInit(GroovySourceAST t, int visit) {
+    	print(t,visit,"(",null,null);
+    }
+    
+    public void visitForInIterable(GroovySourceAST t, int visit) {
+        printUpdatingTabLevel(t,visit,"("," in ",") ");
+    }
+
+    public void visitForIterator(GroovySourceAST t, int visit) {
+    	print(t,visit," ; ",null,")");
+    }
+    
+    public void visitGe(GroovySourceAST t, int visit) {
+    	print(t,visit," >= ",null,null);
+    }
+    
+    public void visitGt(GroovySourceAST t, int visit) {
+        print(t,visit," > ",null,null);
+    }
+
+    public void visitIdent(GroovySourceAST t,int visit) {
+        print(t,visit,t.getText(),null,null);
+    }
+    public void visitImplementsClause(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            if (t.getNumberOfChildren() != 0) {
+                print(t,visit," implements ");
+            }
+        }
+        if (visit == CLOSING_VISIT) {
+            //space between classdef and objblock
+            print(t,visit," ");
+        }
+    }
+
+    public void visitImplicitParameters(GroovySourceAST t, int visit) {
+    	// do nothing
+    }
+
+    public void visitImport(GroovySourceAST t,int visit) {
+        print(t,visit,"import ",null,null);
+    }
+
+    public void visitInc(GroovySourceAST t, int visit) {
+    	print(t,visit,"++",null,null);
+    }
+
+    public void visitIndexOp(GroovySourceAST t, int visit) {
+        printUpdatingTabLevel(t,visit,"[",null,"]");
+    }
+
+    public void visitInterfaceDef(GroovySourceAST t,int visit) {
+        print(t,visit,"interface ",null,null);
+    }
+
+    public void visitInstanceInit(GroovySourceAST t, int visit) {
+    	// do nothing
+	}
+
+	public void visitLabeledArg(GroovySourceAST t, int visit) {
+        print(t,visit,":",null,null);
+    }
+
+	public void visitLabeledStat(GroovySourceAST t, int visit) {
+        print(t,visit,":",null,null);
+    }
+
+    public void visitLand(GroovySourceAST t, int visit) {
+        print(t,visit," && ",null,null);
+    }
+
+    // visit lbrack()
+    //   token type LBRACK only used inside parser, never visited/created
+
+    // visit lcurly()
+    //   token type LCURLY only used inside parser, never visited/created
+    
+    public void visitLe(GroovySourceAST t, int visit) {
+    	print(t,visit," <= ",null,null);
+    }
+
+    // visitLetter ...
+    //   token type LETTER only used by lexer, never visited/created
+
+    public void visitListConstructor(GroovySourceAST t, int visit) {
+        printUpdatingTabLevel(t,visit,"[",null,"]");
+    }
+
+    public void visitLiteralAs(GroovySourceAST t,int visit) {
+        print(t,visit," as ",null,null);
+    }
+
+    public void visitLiteralAssert(GroovySourceAST t,int visit) {
+    	if (t.getNumberOfChildren() > 1) {
+    		print(t,visit,"assert ",null," : ");
+    	} else {
+    		print(t,visit,"assert ",null,null);
+    	}
+    }
+
+    public void visitLiteralBoolean(GroovySourceAST t, int visit) {
+        print(t,visit,"boolean",null,null);
+    }
+
+    public void visitLiteralBreak(GroovySourceAST t, int visit) {
+        print(t,visit,"break ",null,null);
+    }
+
+    public void visitLiteralByte(GroovySourceAST t, int visit) {
+        print(t,visit,"byte",null,null);
+    }
+
+    public void visitLiteralCase(GroovySourceAST t, int visit) {
+        print(t,visit,"case ",null,":");
+    }
+
+    public void visitLiteralCatch(GroovySourceAST t,int visit) {
+        printUpdatingTabLevel(t,visit," catch (",null,") ");
+    }
+
+    public void visitLiteralChar(GroovySourceAST t, int visit) {
+        print(t,visit,"char",null,null);
+    }
+
+    // visitLiteralClass ...
+    //   token type "class" only used by parser, never visited/created directly
+
+    public void visitLiteralContinue(GroovySourceAST t, int visit) {
+        print(t,visit,"continue ",null,null);
+    }
+
+    // visitLiteralDef ...
+    //   token type "def" only used by parser, never visited/created directly
+
+    public void visitLiteralDefault(GroovySourceAST t,int visit) {
+        print(t,visit,"default",null,":");
+    }
+
+    public void visitLiteralDouble(GroovySourceAST t, int visit) {
+        print(t,visit,"double",null,null);
+    }
+
+    // visitLiteralElse ...
+    //   token type "else" only used by parser, never visited/created directly
+
+    // visitLiteralEnum ...
+    //   token type "enum" only used by parser, never visited/created directly
+
+    // visitLiteralExtends
+    //   token type "extends" only used by parser, never visited/created directly
+    
+    public void visitLiteralFalse(GroovySourceAST t,int visit) {
+        print(t,visit,"false",null,null);
+    }
+
+    public void visitLiteralFinally(GroovySourceAST t,int visit) {
+        print(t,visit,"finally ",null,null);
+    }
+    public void visitLiteralFloat(GroovySourceAST t,int visit) {
+        print(t,visit,"float",null,null);
+    }
+
+    public void visitLiteralFor(GroovySourceAST t,int visit) {
+        print(t,visit,"for ",null,null);
+    }
+    
+    public void visitLiteralIf(GroovySourceAST t,int visit) {
+        // slightly strange as subsequent visit is done after closing visit
+        printUpdatingTabLevel(t,visit,"if ("," else ",") ");
+    }
+
+    // visitLiteralImplements
+    //   token type "implements" only used by parser, never visited/created directly
+
+    // visitLiteralImport
+    //   token type "import" only used by parser, never visited/created directly
+
+    public void visitLiteralIn(GroovySourceAST t, int visit) {
+        print(t,visit," in ",null,null);
+    }
+
+    public void visitLiteralInstanceof(GroovySourceAST t, int visit) {
+        print(t,visit," instanceof ",null,null);
+    }
+
+    public void visitLiteralInt(GroovySourceAST t,int visit) {
+        print(t,visit,"int",null,null);
+    }
+
+    // visitLiteralInterface
+    //   token type "interface" only used by parser, never visited/created directly
+
+    public void visitLiteralLong(GroovySourceAST t,int visit) {
+        print(t,visit,"long",null,null);
+    }
+
+    public void visitLiteralNative(GroovySourceAST t,int visit) {
+        print(t,visit,"native ",null,null);
+    }
+    public void visitLiteralNew(GroovySourceAST t,int visit) {
+    	if (t.childOfType(GroovyTokenTypes.ARRAY_DECLARATOR) == null) {
+    		// only print parenthesis if is not of form def x = new int[5]
+    		print(t,visit,"new ","(",")");
+    	} else {
+    		print(t,visit,"new ",null,null);
+    	}
+    }
+
+    public void visitLiteralNull(GroovySourceAST t, int visit) {
+        print(t,visit,"null",null,null);
+    }
+
+    // visitLiteralPackage
+    //   token type "package" only used by parser, never visited/created directly
+
+    public void visitLiteralPrivate(GroovySourceAST t,int visit) {
+        print(t,visit,"private ",null,null);
+    }
+
+    public void visitLiteralProtected(GroovySourceAST t,int visit) {
+        print(t,visit,"protected ",null,null);
+    }
+
+    public void visitLiteralPublic(GroovySourceAST t,int visit) {
+        print(t,visit,"public ",null,null);
+    }
+
+    public void visitLiteralReturn(GroovySourceAST t, int visit) {
+        print(t,visit,"return ",null,null);
+    }
+
+    public void visitLiteralShort(GroovySourceAST t,int visit) {
+        print(t,visit,"short",null,null);
+    }
+
+    public void visitLiteralStatic(GroovySourceAST t, int visit) {
+        print(t,visit,"static ",null,null);
+    }
+
+    public void visitLiteralSuper(GroovySourceAST t, int visit) {
+    	// only visited when calling super() without parentheses, i.e. "super 99" is equivalent to "super(99)"
+    	print(t,visit,"super",null,null);
+    }
+
+    public void visitLiteralSwitch(GroovySourceAST t, int visit) {
+        if (visit == OPENING_VISIT) {
+            print(t,visit,"switch (");
+            tabLevel++;
+        }
+        if (visit == SUBSEQUENT_VISIT) {
+            print(t,visit,") {");
+        }
+        if (visit == CLOSING_VISIT) {
+            tabLevel--;
+            print(t,visit,"}");
+        }
+    }
+
+    public void visitLiteralSynchronized(GroovySourceAST t,int visit) {
+    	if (t.getNumberOfChildren() > 0) {
+    		print(t,visit,"synchronized (",null,") ");
+    	} else {
+    		print(t,visit,"synchronized ",null,null);    		
+    	}
+	}
+
+    public void visitLiteralThis(GroovySourceAST t, int visit) {
+        print(t,visit,"this",null,null);
+    }
+
+    public void visitLiteralThreadsafe(GroovySourceAST t,int visit) {
+        print(t,visit,"threadsafe ",null,null);
+    }
+
+    public void visitLiteralThrow(GroovySourceAST t, int visit) {
+        print(t,visit,"throw ",null,null);
+    }
+
+    public void visitLiteralThrows(GroovySourceAST t, int visit) {
+        print(t,visit,"throws ",null,null);
+    }
+
+    public void visitLiteralTransient(GroovySourceAST t,int visit) {
+        print(t,visit,"transient ",null,null);
+    }
+
+    public void visitLiteralTrue(GroovySourceAST t,int visit) {
+        print(t,visit,"true",null,null);
+    }
+    public void visitLiteralTry(GroovySourceAST t,int visit) {
+        print(t,visit,"try ",null,null);
+    }
+    public void visitLiteralVoid(GroovySourceAST t,int visit) {
+        print(t,visit,"void",null,null);
+    }
+    public void visitLiteralVolatile(GroovySourceAST t,int visit) {
+        print(t,visit,"volatile ",null,null);
+    }
+    public void visitLiteralWhile(GroovySourceAST t,int visit) {
+        printUpdatingTabLevel(t,visit,"while (",null,") ");
+    }
+
+//deprecated
+//  public void visitLiteralWith(GroovySourceAST t,int visit) {
+//        printUpdatingTabLevel(t,visit,"with (",null,") ");
+//    }
+    
+    public void visitLnot(GroovySourceAST t, int visit) {
+        print(t,visit,"!",null,null);
+    }
+
+	// Note: old closure syntax using LOR is deprecated, and also never creates/visits a LOR node
+    public void visitLor(GroovySourceAST t, int visit) {
+        print(t,visit," || ",null,null);
+    }
+
+    public void visitLt(GroovySourceAST t, int visit) {
+        print(t,visit," < ",null,null);
+    }
+
+    public void visitMapConstructor(GroovySourceAST t, int visit) {
+        if (t.getNumberOfChildren() == 0) {
+            print(t,visit,"[:]",null,null);
+        } else {
+            printUpdatingTabLevel(t,visit,"[",null,"]");
+        }
+    }
+
+    public void visitMemberPointer(GroovySourceAST t, int visit) {
+        print(t,visit,".&",null,null);
+    }
+
+    public void visitMethodCall(GroovySourceAST t,int visit) {
+    	if ("<command>".equals(t.getText())) {
+    		printUpdatingTabLevel(t,visit," "," ",null);
+    	} else {
+    		printUpdatingTabLevel(t,visit,"("," ",")");
+    	}
+    }
+    public void visitMethodDef(GroovySourceAST t,int visit) {
+        //do nothing
+    }
+    public void visitMinus(GroovySourceAST t,int visit) {
+        print(t,visit," - ",null,null);
+    }
+    public void visitMinusAssign(GroovySourceAST t, int visit) {
+        print(t,visit," -= ",null,null);
+    }
+
+    // visitMlComment
+    //   multi-line comments are not created on the AST currently.
+
+    public void visitMod(GroovySourceAST t, int visit) {
+        print(t,visit," % ",null,null);
+    }
+
+    public void visitModifiers(GroovySourceAST t,int visit) {
+        //do nothing
+    }
+    public void visitModAssign(GroovySourceAST t, int visit) {
+        print(t,visit," %= ",null,null);
+    }
+
+    // visitNls
+    //   new lines are used by parser, but are not created on the AST,
+    //   they can be implied by the source code line/column information
+
+    // visitNullTreeLookahead
+    //   not used explicitly by parser.
+    
+    
+    public void visitNotEqual(GroovySourceAST t, int visit) {
+        print(t,visit," != ",null,null);
+    }
+
+    public void visitNumBigDecimal(GroovySourceAST t,int visit) {
+        print(t,visit,t.getText(),null,null);
+    }
+    public void visitNumBigInt(GroovySourceAST t,int visit) {
+        print(t,visit,t.getText(),null,null);
+    }
+    public void visitNumDouble(GroovySourceAST t,int visit) {
+        print(t,visit,t.getText(),null,null);
+    }
+    public void visitNumInt(GroovySourceAST t,int visit) {
+        print(t,visit,t.getText(),null,null);
+    }
+    public void visitNumFloat(GroovySourceAST t,int visit) {
+        print(t,visit,t.getText(),null,null);
+    }
+    public void visitNumLong(GroovySourceAST t,int visit) {
+        print(t,visit,t.getText(),null,null);
+    }
+    public void visitObjblock(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            tabLevel++;
+            print(t,visit,"{");
+        } else {
+            tabLevel--;
+            print(t,visit,"}");
+        }
+    }
+
+    // visitOneNl
+    //   new lines are used by parser, but are not created on the AST,
+    //   they can be implied by the source code line/column information
+
+    public void visitOptionalDot(GroovySourceAST t,int visit) {
+        print(t,visit,"?.",null,null);
+    }
+    
+    public void visitPackageDef(GroovySourceAST t, int visit) {
+        print(t,visit,"package ",null,null);
+    }
+
+    public void visitParameterDef(GroovySourceAST t,int visit) {
+        //do nothing
+    }
+
+    public void visitParameters(GroovySourceAST t,int visit) {
+    	if (getParentNode().getType() == GroovyTokenTypes.CLOSABLE_BLOCK) {
+    		printUpdatingTabLevel(t,visit,null,","," ");
+    	} else {
+    		printUpdatingTabLevel(t,visit,"(",", ",") ");
+    	}
+    }
+
+    public void visitPlus(GroovySourceAST t, int visit) {
+        print(t,visit," + ",null,null);
+    }
+    
+    public void visitPlusAssign(GroovySourceAST t, int visit) {
+        print(t,visit," += ",null,null);
+    }
+    public void visitPostDec(GroovySourceAST t, int visit) {
+    	print(t,visit,null,null,"--");
+    }
+
+    public void visitPostInc(GroovySourceAST t, int visit) {
+    	print(t,visit,null,null,"++");
+    }
+
+    public void visitQuestion(GroovySourceAST t, int visit) {
+        // ternary operator
+        print(t,visit,"?",":",null);
+    }
+
+    public void visitRangeExclusive(GroovySourceAST t, int visit) {
+        print(t,visit,"..<",null,null);
+    }
+
+    public void visitRangeInclusive(GroovySourceAST t, int visit) {
+        print(t,visit,"..",null,null);
+    }
+
+    // visit rbrack()
+    //   token type RBRACK only used inside parser, never visited/created
+
+    // visit rcurly()
+    //   token type RCURLY only used inside parser, never visited/created
+
+    // visit RegexpCtorEnd
+    // visit RegexpLiteral
+    // visit RegexpSymbol
+    //    token types REGEXP_CTOR_END, REGEXP_LITERAL, REGEXP_SYMBOL only used inside lexer
+    
+    public void visitRegexFind(GroovySourceAST t, int visit) {
+    	print(t,visit," =~ ",null,null);
+    }
+    public void visitRegexMatch(GroovySourceAST t, int visit) {
+    	print(t,visit," ==~ ",null,null);
+    }
+    // visit rparen()
+    //   token type RPAREN only used inside parser, never visited/created
+
+    public void visitSelectSlot(GroovySourceAST t, int visit) {
+    	print(t,visit,"@",null,null);
+    }
+    
+    // visit semi()
+    //  SEMI only used inside parser, never visited/created (see visitForCondition(), visitForIterator())
+    
+    // visit ShComment()
+    //  never visited/created by parser
+    
+    public void visitSl(GroovySourceAST t, int visit) {
+    	print(t,visit," << ",null,null);
+    }
+    public void visitSlAssign(GroovySourceAST t, int visit) {
+    	print(t,visit," <<= ",null,null);
+    }
+    public void visitSlist(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            tabLevel++;
+            print(t,visit,"{");
+        } else {
+            tabLevel--;
+            print(t,visit,"}");
+        }
+    }
+
+    // visit SlComment()
+    //   never visited/created by parser
+    
+    public void visitSpreadArg(GroovySourceAST t,int visit) {
+    	print(t,visit,"*",null,null);
+    }
+
+    public void visitSpreadDot(GroovySourceAST t,int visit) {
+	print(t,visit,"*.",null,null);
+    }
+
+    public void visitSpreadMapArg(GroovySourceAST t,int visit) {
+    	print(t,visit,"*:",null,null);
+    }
+    
+    public void visitSr(GroovySourceAST t, int visit) {
+    	print(t,visit," >> ",null,null);
+    }
+    public void visitSrAssign(GroovySourceAST t, int visit) {
+    	print(t,visit," >>= ",null,null);
+    }
+
+    public void visitStar(GroovySourceAST t,int visit) {
+        print(t,visit,"*",null,null);
+    }
+    public void visitStarAssign(GroovySourceAST t, int visit) {
+    	print(t,visit," *= ",null,null);
+    }
+    public void visitStarStar(GroovySourceAST t,int visit) {
+        print(t,visit,"**",null,null);
+    }
+    public void visitStarStarAssign(GroovySourceAST t, int visit) {
+    	print(t,visit," **= ",null,null);
+    }
+    
+    public void visitStaticInit(GroovySourceAST t, int visit) {
+    	print(t,visit,"static ",null,null);
+    }
+    public void visitStaticImport(GroovySourceAST t,int visit) {
+        print(t,visit,"import static ",null,null);
+    }
+    public void visitStrictfp(GroovySourceAST t,int visit) {
+    	print(t,visit,"strictfp ",null,null);
+    }
+
+    // visitStringch
+    //   String characters only used by lexer, never visited/created directly
+
+
+    public void visitStringConstructor(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            stringConstructorCounter = 0;
+            print(t,visit,"\"");
+        }
+        if (visit == SUBSEQUENT_VISIT) {
+            // every other subsequent visit use an escaping $
+            if (stringConstructorCounter % 2 == 0) {
+               print(t,visit,"$");
+            }
+            stringConstructorCounter++;
+        }
+        if (visit == CLOSING_VISIT) {
+            print(t,visit,"\"");
+        }
+    }
+
+    public void visitStringLiteral(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            String theString = escape(t.getText());
+        if (getParentNode().getType() != GroovyTokenTypes.LABELED_ARG &&
+            getParentNode().getType() != GroovyTokenTypes.STRING_CONSTRUCTOR) {
+                theString = "\"" + theString + "\"";
+            }
+            print(t,visit,theString);
+        }
+    }
+
+    private String escape(String literal) {
+        literal = literal.replaceAll("\n","\\\\<<REMOVE>>n"); // can't seem to do \n in one go with Java regex
+        literal = literal.replaceAll("<<REMOVE>>","");
+        return literal;
+    }
+
+    public void visitSuperCtorCall(GroovySourceAST t,int visit) {
+		printUpdatingTabLevel(t,visit,"super("," ",")");
+    }
+    
+    // visit TripleDot, not used in the AST
+    
+    public void visitType(GroovySourceAST t,int visit) {
+        GroovySourceAST parent = getParentNode();
+        GroovySourceAST modifiers = parent.childOfType(GroovyTokenTypes.MODIFIERS);
+
+        // No need to print 'def' if we already have some modifiers
+        if (modifiers == null || modifiers.getNumberOfChildren() == 0) {
+
+            if (visit == OPENING_VISIT) {
+                if (t.getNumberOfChildren() == 0 && 
+                		parent.getType() != GroovyTokenTypes.PARAMETER_DEF) { // no need for 'def' if in a parameter list
+                    print(t,visit,"def");
+                }
+            } 
+            if (visit == CLOSING_VISIT) {
+                if (  parent.getType() == GroovyTokenTypes.VARIABLE_DEF         ||
+                      parent.getType() == GroovyTokenTypes.METHOD_DEF           ||
+                      parent.getType() == GroovyTokenTypes.ANNOTATION_FIELD_DEF ||
+                     (parent.getType() == GroovyTokenTypes.PARAMETER_DEF && t.getNumberOfChildren()!=0))             
+                {
+                    print(t,visit," ");
+                }
+            }
+            
+        	/*if (visit == CLOSING_VISIT) {
+        		print(t,visit," ");
+            }*/
+        } else {
+        	if (visit == CLOSING_VISIT) {
+        		if (t.getNumberOfChildren() != 0) {
+        			print(t,visit," ");
+        		}
+        	}
+        }
+    }
+    public void visitTypeArgument(GroovySourceAST t, int visit) {
+    	// print nothing
+    }
+
+    public void visitTypeArguments(GroovySourceAST t, int visit) {
+    	print(t,visit,"<",", ",">");
+    }
+
+    public void visitTypecast(GroovySourceAST t,int visit) {
+        print(t,visit,"(",null,")");
+    }
+    public void visitTypeLowerBounds(GroovySourceAST t,int visit) {
+        print(t,visit," super "," & ",null);
+    }
+    public void visitTypeParameter(GroovySourceAST t, int visit) {
+    	// print nothing
+    }
+
+    public void visitTypeParameters(GroovySourceAST t, int visit) {
+    	print(t,visit,"<",", ",">");
+    }
+
+    public void visitTypeUpperBounds(GroovySourceAST t,int visit) {
+        print(t,visit," extends "," & ",null);
+    }
+    public void visitUnaryMinus(GroovySourceAST t, int visit) {
+    	print(t,visit,"-",null,null);
+    }
+    public void visitUnaryPlus(GroovySourceAST t, int visit) {
+    	print(t,visit,"+",null,null);
+    }
+
+    // visit Unused "const", "do", "goto" - unsurprisingly these are unused by the AST.
+    
+    public void visitVariableDef(GroovySourceAST t,int visit) {
+        // do nothing
+    }
+
+    // a.k.a. "variable arity parameter" in the JLS
+    public void visitVariableParameterDef(GroovySourceAST t,int visit) {
+        print(t,visit,null,"... ",null);
+    }
+    
+    // visit Vocab - only used by Lexer
+    
+    public void visitWildcardType(GroovySourceAST t, int visit) {
+    	print(t,visit,"?",null,null);
+    }
+
+    // visit WS - only used by lexer
+    
+    
+    
+    public void visitDefault(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            print(t,visit,"<" + tokenNames[t.getType()] + ">");
+            //out.print("<" + t.getType() + ">");
+        } else {
+            print(t,visit,"</" + tokenNames[t.getType()] + ">");
+            //out.print("</" + t.getType() + ">");
+        }
+    }
+
+    protected void printUpdatingTabLevel(GroovySourceAST t,int visit,String opening, String subsequent, String closing) {
+        if (visit == OPENING_VISIT && opening != null) {
+            print(t,visit,opening);
+            tabLevel++;
+        }
+        if (visit == SUBSEQUENT_VISIT && subsequent != null) {
+            print(t,visit,subsequent);
+        }
+        if (visit == CLOSING_VISIT && closing != null) {
+            tabLevel--;
+            print(t,visit,closing);
+        }
+    }
+
+    protected void print(GroovySourceAST t,int visit,String opening, String subsequent, String closing) {
+        if (visit == OPENING_VISIT && opening != null) {
+            print(t,visit,opening);
+        }
+        if (visit == SUBSEQUENT_VISIT && subsequent != null) {
+            print(t,visit,subsequent);
+        }
+        if (visit == CLOSING_VISIT && closing != null) {
+            print(t,visit,closing);
+        }
+    }
+    protected void print(GroovySourceAST t,int visit,String value) {
+        if(visit == OPENING_VISIT) {
+            printNewlineAndIndent(t, visit);
+        }
+        if (visit == CLOSING_VISIT) {
+            printNewlineAndIndent(t, visit);
+        }
+        out.print(value);
+    }
+
+    protected void printNewlineAndIndent(GroovySourceAST t, int visit) {
+        int currentLine = t.getLine();
+        if (lastLinePrinted == 0) { lastLinePrinted = currentLine; }
+        if (lastLinePrinted != currentLine) {
+            if (newLines) {
+                if (!(visit == OPENING_VISIT && t.getType() == GroovyTokenTypes.SLIST)) {
+                    for (int i=lastLinePrinted;i<currentLine;i++) {
+                        out.println();
+                    }
+                    if (lastLinePrinted > currentLine) {
+                        out.println();
+                        lastLinePrinted = currentLine;
+                    }
+                    if (visit == OPENING_VISIT || (visit == CLOSING_VISIT && lastLinePrinted > currentLine)) {
+                        for (int i=0;i<tabLevel;i++) {
+                            out.print("    ");
+                        }
+                    }
+                }
+            }
+            lastLinePrinted = Math.max(currentLine,lastLinePrinted);
+        }
+    }
+
+    public void push(GroovySourceAST t) {
+        stack.push(t);
+    }
+    public GroovySourceAST pop() {
+        if (!stack.empty()) {
+            return (GroovySourceAST) stack.pop();
+        }
+        return null;
+    }
+
+    private GroovySourceAST getParentNode() {
+        Object currentNode = stack.pop();
+        Object parentNode = stack.peek();
+        stack.push(currentNode);
+        return (GroovySourceAST) parentNode;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/TraversalHelper.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/TraversalHelper.java
new file mode 100644
index 0000000..bf5452d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/TraversalHelper.java
@@ -0,0 +1,535 @@
+/*
+ * Copyright 2005 Jeremy Rayner
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+import org.codehaus.groovy.antlr.AntlrASTProcessor;
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+
+import antlr.collections.AST;
+
+/**                                                  
+ * Helper Class for Antlr AST traversal and visitation.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+public abstract class TraversalHelper implements AntlrASTProcessor {
+    protected List unvisitedNodes;
+    private final Visitor v;
+
+    public TraversalHelper(Visitor visitor) {
+        this.unvisitedNodes = new ArrayList();
+        this.v = visitor;
+    }
+
+    protected void setUp(GroovySourceAST ast) {
+        v.setUp();
+    }
+    protected void tearDown(GroovySourceAST ast) {
+        v.tearDown();
+    }
+
+    protected void push(GroovySourceAST ast) {
+        v.push(ast);
+    }
+    protected GroovySourceAST pop() {
+        return v.pop();
+    }
+
+    protected void visitNode(GroovySourceAST ast, int n) {
+        if (ast != null) {
+            switch (ast.getType()) {
+                case GroovyTokenTypes.ABSTRACT                      :   v.visitAbstract(ast,n);                     break;
+                case GroovyTokenTypes.ANNOTATION                    :   v.visitAnnotation(ast,n);                   break;
+                case GroovyTokenTypes.ANNOTATIONS                   :   v.visitAnnotations(ast,n);                  break;
+                case GroovyTokenTypes.ANNOTATION_ARRAY_INIT         :   v.visitAnnotationArrayInit(ast,n);          break; // obsolete?
+                case GroovyTokenTypes.ANNOTATION_DEF                :   v.visitAnnotationDef(ast,n);                break;
+                case GroovyTokenTypes.ANNOTATION_FIELD_DEF          :   v.visitAnnotationFieldDef(ast,n);           break;
+                case GroovyTokenTypes.ANNOTATION_MEMBER_VALUE_PAIR  :   v.visitAnnotationMemberValuePair(ast,n);    break;
+                case GroovyTokenTypes.ARRAY_DECLARATOR              :   v.visitArrayDeclarator(ast,n);              break;
+                case GroovyTokenTypes.ASSIGN                        :   v.visitAssign(ast,n);                       break;
+                case GroovyTokenTypes.AT                            :   v.visitAt(ast,n);                           break;
+                case GroovyTokenTypes.BAND                          :   v.visitBand(ast,n);                         break;
+                case GroovyTokenTypes.BAND_ASSIGN                   :   v.visitBandAssign(ast,n);                   break;
+                case GroovyTokenTypes.BIG_SUFFIX                    :   v.visitBigSuffix(ast,n);                    break;
+                case GroovyTokenTypes.BLOCK                         :   v.visitBlock(ast,n);                        break;
+                case GroovyTokenTypes.BNOT                          :   v.visitBnot(ast,n);                         break;
+                case GroovyTokenTypes.BOR                           :   v.visitBor(ast,n);                          break;
+                case GroovyTokenTypes.BOR_ASSIGN                    :   v.visitBorAssign(ast,n);                    break;
+                case GroovyTokenTypes.BSR                           :   v.visitBsr(ast,n);                          break;
+                case GroovyTokenTypes.BSR_ASSIGN                    :   v.visitBsrAssign(ast,n);                    break;
+                case GroovyTokenTypes.BXOR                          :   v.visitBxor(ast,n);                         break;
+                case GroovyTokenTypes.BXOR_ASSIGN                   :   v.visitBxorAssign(ast,n);                   break;
+                case GroovyTokenTypes.CASE_GROUP                    :   v.visitCaseGroup(ast,n);                    break;
+                case GroovyTokenTypes.CLASS_DEF                     :   v.visitClassDef(ast,n);                     break;
+                case GroovyTokenTypes.CLOSABLE_BLOCK                :   v.visitClosedBlock(ast,n);                  break;
+                case GroovyTokenTypes.CLOSABLE_BLOCK_OP             :   v.visitClosureOp(ast,n);                    break;
+                case GroovyTokenTypes.CLOSURE_LIST                  :   v.visitClosureList(ast,n);                  break;
+                case GroovyTokenTypes.COLON                         :   v.visitColon(ast,n);                        break;
+                case GroovyTokenTypes.COMMA                         :   v.visitComma(ast,n);                        break;
+                case GroovyTokenTypes.COMPARE_TO                    :   v.visitCompareTo(ast,n);                    break;
+                case GroovyTokenTypes.CTOR_CALL                     :   v.visitCtorCall(ast,n);                     break;
+                case GroovyTokenTypes.CTOR_IDENT                    :   v.visitCtorIdent(ast,n);                    break;
+                case GroovyTokenTypes.DEC                           :   v.visitDec(ast,n);                          break;
+                case GroovyTokenTypes.DIGIT                         :   v.visitDigit(ast,n);                        break;
+                case GroovyTokenTypes.DIV                           :   v.visitDiv(ast,n);                          break;
+                case GroovyTokenTypes.DIV_ASSIGN                    :   v.visitDivAssign(ast,n);                    break;
+                case GroovyTokenTypes.DOLLAR                        :   v.visitDollar(ast,n);                       break;
+                case GroovyTokenTypes.DOT                           :   v.visitDot(ast,n);                          break;
+                case GroovyTokenTypes.DYNAMIC_MEMBER                :   v.visitDynamicMember(ast,n);                break;
+                case GroovyTokenTypes.ELIST                         :   v.visitElist(ast,n);                        break;
+                case GroovyTokenTypes.EMPTY_STAT                    :   v.visitEmptyStat(ast,n);                    break;
+                case GroovyTokenTypes.ENUM_CONSTANT_DEF             :   v.visitEnumConstantDef(ast,n);              break;
+                case GroovyTokenTypes.ENUM_DEF                      :   v.visitEnumDef(ast,n);                      break;
+                case GroovyTokenTypes.EOF                           :   v.visitEof(ast,n);                          break;
+                case GroovyTokenTypes.EQUAL                         :   v.visitEqual(ast,n);                        break;
+                case GroovyTokenTypes.ESC                           :   v.visitEsc(ast,n);                          break;
+                case GroovyTokenTypes.EXPONENT                      :   v.visitExponent(ast,n);                     break;
+                case GroovyTokenTypes.EXPR                          :   v.visitExpr(ast,n);                         break;
+                case GroovyTokenTypes.EXTENDS_CLAUSE                :   v.visitExtendsClause(ast,n);                break;
+                case GroovyTokenTypes.FINAL                         :   v.visitFinal(ast,n);                        break;
+                case GroovyTokenTypes.FLOAT_SUFFIX                  :   v.visitFloatSuffix(ast,n);                  break;
+                case GroovyTokenTypes.FOR_CONDITION                 :   v.visitForCondition(ast,n);                 break;
+                case GroovyTokenTypes.FOR_EACH_CLAUSE               :   v.visitForEachClause(ast,n);                break;
+                case GroovyTokenTypes.FOR_INIT                      :   v.visitForInit(ast,n);                      break;
+                case GroovyTokenTypes.FOR_IN_ITERABLE               :   v.visitForInIterable(ast,n);                break;
+                case GroovyTokenTypes.FOR_ITERATOR                  :   v.visitForIterator(ast,n);                  break;
+                case GroovyTokenTypes.GE                            :   v.visitGe(ast,n);                           break;
+                case GroovyTokenTypes.GT                            :   v.visitGt(ast,n);                           break;
+                case GroovyTokenTypes.HEX_DIGIT                     :   v.visitHexDigit(ast,n);                     break;
+                case GroovyTokenTypes.IDENT                         :   v.visitIdent(ast,n);                        break;
+                case GroovyTokenTypes.IMPLEMENTS_CLAUSE             :   v.visitImplementsClause(ast,n);             break;
+                case GroovyTokenTypes.IMPLICIT_PARAMETERS           :   v.visitImplicitParameters(ast,n);           break;
+                case GroovyTokenTypes.IMPORT                        :   v.visitImport(ast,n);                       break;
+                case GroovyTokenTypes.INC                           :   v.visitInc(ast,n);                          break;
+                case GroovyTokenTypes.INDEX_OP                      :   v.visitIndexOp(ast,n);                      break;
+                case GroovyTokenTypes.INSTANCE_INIT                 :   v.visitInstanceInit(ast,n);                 break;
+                case GroovyTokenTypes.INTERFACE_DEF                 :   v.visitInterfaceDef(ast,n);                 break;
+                case GroovyTokenTypes.LABELED_ARG                   :   v.visitLabeledArg(ast,n);                   break;
+                case GroovyTokenTypes.LABELED_STAT                  :   v.visitLabeledStat(ast,n);                  break;
+                case GroovyTokenTypes.LAND                          :   v.visitLand(ast,n);                         break;
+                case GroovyTokenTypes.LBRACK                        :   v.visitLbrack(ast,n);                       break;
+                case GroovyTokenTypes.LCURLY                        :   v.visitLcurly(ast,n);                       break;
+                case GroovyTokenTypes.LE                            :   v.visitLe(ast,n);                           break;
+                case GroovyTokenTypes.LETTER                        :   v.visitLetter(ast,n);                       break;
+                case GroovyTokenTypes.LIST_CONSTRUCTOR              :   v.visitListConstructor(ast,n);              break;
+                case GroovyTokenTypes.LITERAL_as                    :   v.visitLiteralAs(ast,n);                    break;
+                case GroovyTokenTypes.LITERAL_assert                :   v.visitLiteralAssert(ast,n);                break;
+                case GroovyTokenTypes.LITERAL_boolean               :   v.visitLiteralBoolean(ast,n);               break;
+                case GroovyTokenTypes.LITERAL_break                 :   v.visitLiteralBreak(ast,n);                 break;
+                case GroovyTokenTypes.LITERAL_byte                  :   v.visitLiteralByte(ast,n);                  break;
+                case GroovyTokenTypes.LITERAL_case                  :   v.visitLiteralCase(ast,n);                  break;
+                case GroovyTokenTypes.LITERAL_catch                 :   v.visitLiteralCatch(ast,n);                 break;
+                case GroovyTokenTypes.LITERAL_char                  :   v.visitLiteralChar(ast,n);                  break;
+                case GroovyTokenTypes.LITERAL_class                 :   v.visitLiteralClass(ast,n);                 break;
+                case GroovyTokenTypes.LITERAL_continue              :   v.visitLiteralContinue(ast,n);              break;
+                case GroovyTokenTypes.LITERAL_def                   :   v.visitLiteralDef(ast,n);                   break;
+                case GroovyTokenTypes.LITERAL_default               :   v.visitLiteralDefault(ast,n);               break;
+                case GroovyTokenTypes.LITERAL_double                :   v.visitLiteralDouble(ast,n);                break;
+                case GroovyTokenTypes.LITERAL_else                  :   v.visitLiteralElse(ast,n);                  break;
+                case GroovyTokenTypes.LITERAL_enum                  :   v.visitLiteralEnum(ast,n);                  break;
+                case GroovyTokenTypes.LITERAL_extends               :   v.visitLiteralExtends(ast,n);               break;
+                case GroovyTokenTypes.LITERAL_false                 :   v.visitLiteralFalse(ast,n);                 break;
+                case GroovyTokenTypes.LITERAL_finally               :   v.visitLiteralFinally(ast,n);               break;
+                case GroovyTokenTypes.LITERAL_float                 :   v.visitLiteralFloat(ast,n);                 break;
+                case GroovyTokenTypes.LITERAL_for                   :   v.visitLiteralFor(ast,n);                   break;
+                case GroovyTokenTypes.LITERAL_if                    :   v.visitLiteralIf(ast,n);                    break;
+                case GroovyTokenTypes.LITERAL_implements            :   v.visitLiteralImplements(ast,n);            break;
+                case GroovyTokenTypes.LITERAL_import                :   v.visitLiteralImport(ast,n);                break;
+                case GroovyTokenTypes.LITERAL_in                    :   v.visitLiteralIn(ast,n);                    break;
+                case GroovyTokenTypes.LITERAL_instanceof            :   v.visitLiteralInstanceof(ast,n);            break;
+                case GroovyTokenTypes.LITERAL_int                   :   v.visitLiteralInt(ast,n);                   break;
+                case GroovyTokenTypes.LITERAL_interface             :   v.visitLiteralInterface(ast,n);             break;
+                case GroovyTokenTypes.LITERAL_long                  :   v.visitLiteralLong(ast,n);                  break;
+                case GroovyTokenTypes.LITERAL_native                :   v.visitLiteralNative(ast,n);                break;
+                case GroovyTokenTypes.LITERAL_new                   :   v.visitLiteralNew(ast,n);                   break;
+                case GroovyTokenTypes.LITERAL_null                  :   v.visitLiteralNull(ast,n);                  break;
+                case GroovyTokenTypes.LITERAL_package               :   v.visitLiteralPackage(ast,n);               break;
+                case GroovyTokenTypes.LITERAL_private               :   v.visitLiteralPrivate(ast,n);               break;
+                case GroovyTokenTypes.LITERAL_protected             :   v.visitLiteralProtected(ast,n);             break;
+                case GroovyTokenTypes.LITERAL_public                :   v.visitLiteralPublic(ast,n);                break;
+                case GroovyTokenTypes.LITERAL_return                :   v.visitLiteralReturn(ast,n);                break;
+                case GroovyTokenTypes.LITERAL_short                 :   v.visitLiteralShort(ast,n);                 break;
+                case GroovyTokenTypes.LITERAL_static                :   v.visitLiteralStatic(ast,n);                break;
+                case GroovyTokenTypes.LITERAL_super                 :   v.visitLiteralSuper(ast,n);                 break;
+                case GroovyTokenTypes.LITERAL_switch                :   v.visitLiteralSwitch(ast,n);                break;
+                case GroovyTokenTypes.LITERAL_synchronized          :   v.visitLiteralSynchronized(ast,n);          break;
+                case GroovyTokenTypes.LITERAL_this                  :   v.visitLiteralThis(ast,n);                  break;
+                case GroovyTokenTypes.LITERAL_threadsafe            :   v.visitLiteralThreadsafe(ast,n);            break;
+                case GroovyTokenTypes.LITERAL_throw                 :   v.visitLiteralThrow(ast,n);                 break;
+                case GroovyTokenTypes.LITERAL_throws                :   v.visitLiteralThrows(ast,n);                break;
+                case GroovyTokenTypes.LITERAL_transient             :   v.visitLiteralTransient(ast,n);             break;
+                case GroovyTokenTypes.LITERAL_true                  :   v.visitLiteralTrue(ast,n);                  break;
+                case GroovyTokenTypes.LITERAL_try                   :   v.visitLiteralTry(ast,n);                   break;
+                case GroovyTokenTypes.LITERAL_void                  :   v.visitLiteralVoid(ast,n);                  break;
+                case GroovyTokenTypes.LITERAL_volatile              :   v.visitLiteralVolatile(ast,n);              break;
+                case GroovyTokenTypes.LITERAL_while                 :   v.visitLiteralWhile(ast,n);                 break;
+                case GroovyTokenTypes.LNOT                          :   v.visitLnot(ast,n);                         break;
+                case GroovyTokenTypes.LOR                           :   v.visitLor(ast,n);                          break;
+                case GroovyTokenTypes.LPAREN                        :   v.visitLparen(ast,n);                       break;
+                case GroovyTokenTypes.LT                            :   v.visitLt(ast,n);                           break;
+                case GroovyTokenTypes.MAP_CONSTRUCTOR               :   v.visitMapConstructor(ast,n);               break;
+                case GroovyTokenTypes.MEMBER_POINTER                :   v.visitMemberPointer(ast,n);                break;
+                case GroovyTokenTypes.METHOD_CALL                   :   v.visitMethodCall(ast,n);                   break;
+                case GroovyTokenTypes.METHOD_DEF                    :   v.visitMethodDef(ast,n);                    break;
+                case GroovyTokenTypes.MINUS                         :   v.visitMinus(ast,n);                        break;
+                case GroovyTokenTypes.MINUS_ASSIGN                  :   v.visitMinusAssign(ast,n);                  break;
+                case GroovyTokenTypes.ML_COMMENT                    :   v.visitMlComment(ast,n);                    break;
+                case GroovyTokenTypes.MOD                           :   v.visitMod(ast,n);                          break;
+                case GroovyTokenTypes.MODIFIERS                     :   v.visitModifiers(ast,n);                    break;
+                case GroovyTokenTypes.MOD_ASSIGN                    :   v.visitModAssign(ast,n);                    break;
+                case GroovyTokenTypes.NLS                           :   v.visitNls(ast,n);                          break;
+                case GroovyTokenTypes.NOT_EQUAL                     :   v.visitNotEqual(ast,n);                     break;
+                case GroovyTokenTypes.NULL_TREE_LOOKAHEAD           :   v.visitNullTreeLookahead(ast,n);            break;
+                case GroovyTokenTypes.NUM_BIG_DECIMAL               :   v.visitNumBigDecimal(ast,n);                break;
+                case GroovyTokenTypes.NUM_BIG_INT                   :   v.visitNumBigInt(ast,n);                    break;
+                case GroovyTokenTypes.NUM_DOUBLE                    :   v.visitNumDouble(ast,n);                    break;
+                case GroovyTokenTypes.NUM_FLOAT                     :   v.visitNumFloat(ast,n);                     break;
+                case GroovyTokenTypes.NUM_INT                       :   v.visitNumInt(ast,n);                       break;
+                case GroovyTokenTypes.NUM_LONG                      :   v.visitNumLong(ast,n);                      break;
+                case GroovyTokenTypes.OBJBLOCK                      :   v.visitObjblock(ast,n);                     break;
+                case GroovyTokenTypes.ONE_NL                        :   v.visitOneNl(ast,n);                        break;
+                case GroovyTokenTypes.OPTIONAL_DOT                  :   v.visitOptionalDot(ast,n);                  break;
+                case GroovyTokenTypes.PACKAGE_DEF                   :   v.visitPackageDef(ast,n);                   break;
+                case GroovyTokenTypes.PARAMETERS                    :   v.visitParameters(ast,n);                   break;
+                case GroovyTokenTypes.PARAMETER_DEF                 :   v.visitParameterDef(ast,n);                 break;
+                case GroovyTokenTypes.PLUS                          :   v.visitPlus(ast,n);                         break;
+                case GroovyTokenTypes.PLUS_ASSIGN                   :   v.visitPlusAssign(ast,n);                   break;
+                case GroovyTokenTypes.POST_DEC                      :   v.visitPostDec(ast,n);                      break;
+                case GroovyTokenTypes.POST_INC                      :   v.visitPostInc(ast,n);                      break;
+                case GroovyTokenTypes.QUESTION                      :   v.visitQuestion(ast,n);                     break;
+                case GroovyTokenTypes.RANGE_EXCLUSIVE               :   v.visitRangeExclusive(ast,n);               break;
+                case GroovyTokenTypes.RANGE_INCLUSIVE               :   v.visitRangeInclusive(ast,n);               break;
+                case GroovyTokenTypes.RBRACK                        :   v.visitRbrack(ast,n);                       break;
+                case GroovyTokenTypes.RCURLY                        :   v.visitRcurly(ast,n);                       break;
+                case GroovyTokenTypes.REGEXP_CTOR_END               :   v.visitRegexpCtorEnd(ast,n);                break;
+                case GroovyTokenTypes.REGEXP_LITERAL                :   v.visitRegexpLiteral(ast,n);                break;
+                case GroovyTokenTypes.REGEXP_SYMBOL                 :   v.visitRegexpSymbol(ast,n);                 break;
+                case GroovyTokenTypes.REGEX_FIND                    :   v.visitRegexFind(ast,n);                    break;
+                case GroovyTokenTypes.REGEX_MATCH                   :   v.visitRegexMatch(ast,n);                   break;
+                case GroovyTokenTypes.RPAREN                        :   v.visitRparen(ast,n);                       break;
+                case GroovyTokenTypes.SELECT_SLOT                   :   v.visitSelectSlot(ast,n);                   break;
+                case GroovyTokenTypes.SEMI                          :   v.visitSemi(ast,n);                         break;
+                case GroovyTokenTypes.SH_COMMENT                    :   v.visitShComment(ast,n);                    break;
+                case GroovyTokenTypes.SL                            :   v.visitSl(ast,n);                           break;
+                case GroovyTokenTypes.SLIST                         :   v.visitSlist(ast,n);                        break;
+                case GroovyTokenTypes.SL_ASSIGN                     :   v.visitSlAssign(ast,n);                     break;
+                case GroovyTokenTypes.SL_COMMENT                    :   v.visitSlComment(ast,n);                    break;
+                case GroovyTokenTypes.SPREAD_ARG                    :   v.visitSpreadArg(ast,n);                    break;
+                case GroovyTokenTypes.SPREAD_DOT                    :   v.visitSpreadDot(ast,n);                    break;
+                case GroovyTokenTypes.SPREAD_MAP_ARG                :   v.visitSpreadMapArg(ast,n);                 break;
+                case GroovyTokenTypes.SR                            :   v.visitSr(ast,n);                           break;
+                case GroovyTokenTypes.SR_ASSIGN                     :   v.visitSrAssign(ast,n);                     break;
+                case GroovyTokenTypes.STAR                          :   v.visitStar(ast,n);                         break;
+                case GroovyTokenTypes.STAR_ASSIGN                   :   v.visitStarAssign(ast,n);                   break;
+                case GroovyTokenTypes.STAR_STAR                     :   v.visitStarStar(ast,n);                     break;
+                case GroovyTokenTypes.STAR_STAR_ASSIGN              :   v.visitStarStarAssign(ast,n);               break;
+                case GroovyTokenTypes.STATIC_IMPORT                 :   v.visitStaticImport(ast,n);                 break;
+                case GroovyTokenTypes.STATIC_INIT                   :   v.visitStaticInit(ast,n);                   break;
+                case GroovyTokenTypes.STRICTFP                      :   v.visitStrictfp(ast,n);                     break;
+                case GroovyTokenTypes.STRING_CH                     :   v.visitStringCh(ast,n);                     break;
+                case GroovyTokenTypes.STRING_CONSTRUCTOR            :   v.visitStringConstructor(ast,n);            break;
+                case GroovyTokenTypes.STRING_CTOR_END               :   v.visitStringCtorEnd(ast,n);                break;
+                case GroovyTokenTypes.STRING_CTOR_MIDDLE            :   v.visitStringCtorMiddle(ast,n);             break;
+                case GroovyTokenTypes.STRING_CTOR_START             :   v.visitStringCtorStart(ast,n);              break;
+                case GroovyTokenTypes.STRING_LITERAL                :   v.visitStringLiteral(ast,n);                break;
+                case GroovyTokenTypes.STRING_NL                     :   v.visitStringNl(ast,n);                     break;
+                case GroovyTokenTypes.SUPER_CTOR_CALL               :   v.visitSuperCtorCall(ast,n);                break;
+                case GroovyTokenTypes.TRIPLE_DOT                    :   v.visitTripleDot(ast,n);                    break;
+                case GroovyTokenTypes.TYPE                          :   v.visitType(ast,n);                         break;
+                case GroovyTokenTypes.TYPECAST                      :   v.visitTypecast(ast,n);                     break;
+                case GroovyTokenTypes.TYPE_ARGUMENT                 :   v.visitTypeArgument(ast,n);                 break;
+                case GroovyTokenTypes.TYPE_ARGUMENTS                :   v.visitTypeArguments(ast,n);                break;
+                case GroovyTokenTypes.TYPE_LOWER_BOUNDS             :   v.visitTypeLowerBounds(ast,n);              break;
+                case GroovyTokenTypes.TYPE_PARAMETER                :   v.visitTypeParameter(ast,n);                break;
+                case GroovyTokenTypes.TYPE_PARAMETERS               :   v.visitTypeParameters(ast,n);               break;
+                case GroovyTokenTypes.TYPE_UPPER_BOUNDS             :   v.visitTypeUpperBounds(ast,n);              break;
+                case GroovyTokenTypes.UNARY_MINUS                   :   v.visitUnaryMinus(ast,n);                   break;
+                case GroovyTokenTypes.UNARY_PLUS                    :   v.visitUnaryPlus(ast,n);                    break;
+                case GroovyTokenTypes.UNUSED_CONST                  :   v.visitUnusedConst(ast,n);                  break;
+                case GroovyTokenTypes.UNUSED_DO                     :   v.visitUnusedDo(ast,n);                     break;
+                case GroovyTokenTypes.UNUSED_GOTO                   :   v.visitUnusedGoto(ast,n);                   break;
+                case GroovyTokenTypes.VARIABLE_DEF                  :   v.visitVariableDef(ast,n);                  break;
+                case GroovyTokenTypes.VARIABLE_PARAMETER_DEF        :   v.visitVariableParameterDef(ast,n);         break;
+                case GroovyTokenTypes.VOCAB                         :   v.visitVocab(ast,n);                        break;
+                case GroovyTokenTypes.WILDCARD_TYPE                 :   v.visitWildcardType(ast,n);                 break;
+                case GroovyTokenTypes.WS                            :   v.visitWs(ast,n);                           break;
+
+
+                default                                             :   v.visitDefault(ast,n);                      break;
+            }
+        } else {
+            // the supplied AST was null
+            v.visitDefault(null,n);
+        }
+    }
+    protected abstract void accept(GroovySourceAST currentNode);
+
+    protected void accept_v_FirstChildsFirstChild_v_Child2_Child3_v_Child4_v___v_LastChild(GroovySourceAST t) {
+        openingVisit(t);
+        GroovySourceAST expr2 = t.childAt(0);
+        skip(expr2);
+        accept(expr2.childAt(0));
+        closingVisit(t);
+
+        GroovySourceAST sibling = (GroovySourceAST)expr2.getNextSibling();
+        boolean firstSList = true;
+        while (sibling != null) {
+            if (!firstSList) {
+                subsequentVisit(t);
+            }
+            firstSList = false;
+            accept(sibling);
+            sibling = (GroovySourceAST)sibling.getNextSibling();
+        }
+    }
+
+    protected void accept_v_FirstChildsFirstChild_v_RestOfTheChildren(GroovySourceAST t) {
+        openingVisit(t);
+        GroovySourceAST expr = t.childAt(0);
+        skip(expr);
+        accept(expr.childAt(0));
+        closingVisit(t);
+        acceptSiblings(expr);
+    }
+
+    protected void accept_FirstChild_v_SecondChild(GroovySourceAST t) {
+        accept(t.childAt(0));
+        subsequentVisit(t);
+        accept(t.childAt(1));
+    }
+    protected void accept_FirstChild_v_SecondChild_v(GroovySourceAST t) {
+        accept(t.childAt(0));
+        openingVisit(t);
+        accept(t.childAt(1));
+        closingVisit(t);
+    }
+
+    protected void accept_FirstChild_v_SecondChildsChildren_v(GroovySourceAST t) {
+        accept(t.childAt(0));
+
+        openingVisit(t);
+        GroovySourceAST secondChild = t.childAt(1);
+        if (secondChild != null) {
+            acceptChildren(secondChild);
+        }
+        closingVisit(t);
+    }
+
+    
+    protected void accept_v_FirstChild_SecondChild_v_ThirdChild_v(GroovySourceAST t) {
+        openingVisit(t);
+        accept(t.childAt(0));
+        accept(t.childAt(1));
+        subsequentVisit(t);
+        accept(t.childAt(2));
+        closingVisit(t);
+    }
+
+    protected void accept_FirstChild_v_SecondChild_v_ThirdChild_v(GroovySourceAST t) {
+        accept(t.childAt(0));
+        openingVisit(t);
+        accept(t.childAt(1));
+        subsequentVisit(t);
+        accept(t.childAt(2));
+        closingVisit(t);
+    }
+	
+    protected void accept_FirstSecondAndThirdChild_v_v_ForthChild(GroovySourceAST t) {
+        GroovySourceAST child1 = (GroovySourceAST)t.getFirstChild();
+        if (child1 != null){
+        	accept(child1);
+            GroovySourceAST child2 = (GroovySourceAST)child1.getNextSibling();
+            if (child2 != null) {
+            	accept(child2);
+                GroovySourceAST child3 = (GroovySourceAST)child2.getNextSibling();
+                if (child3 != null) {
+                	accept(child3);
+                	openingVisit(t);
+                	GroovySourceAST child4 = (GroovySourceAST)child3.getNextSibling();
+                    if (child4 != null) {
+                    	subsequentVisit(t);
+                    	accept(child4);
+                    }
+                }
+            }
+        }
+	}
+
+	protected void accept_v_FirstChild_2ndv_SecondChild_v___LastChild_v(GroovySourceAST t) {
+        openingVisit(t);
+        GroovySourceAST child = (GroovySourceAST)t.getFirstChild();
+        if (child != null){
+            accept(child);
+            GroovySourceAST sibling = (GroovySourceAST)child.getNextSibling();
+            if (sibling != null) {
+            	secondVisit(t);
+            	accept(sibling);
+                sibling = (GroovySourceAST)sibling.getNextSibling();
+                while (sibling != null) {
+                    subsequentVisit(t);
+                    accept(sibling);
+                    sibling = (GroovySourceAST)sibling.getNextSibling();
+                }
+            }
+        }
+        closingVisit(t);
+    }
+
+	protected void accept_v_FirstChild_v_SecondChild_v___LastChild_v(GroovySourceAST t) {
+        openingVisit(t);
+        GroovySourceAST child = (GroovySourceAST)t.getFirstChild();
+        if (child != null){
+            accept(child);
+            GroovySourceAST sibling = (GroovySourceAST)child.getNextSibling();
+            while (sibling != null) {
+                subsequentVisit(t);
+                accept(sibling);
+                sibling = (GroovySourceAST)sibling.getNextSibling();
+            }
+        }
+        closingVisit(t);
+    }
+
+    protected void accept_v_FirstChild_v(GroovySourceAST t) {
+        openingVisit(t);
+        accept(t.childAt(0));
+        closingVisit(t);
+    }
+    
+    protected void accept_v_Siblings_v(GroovySourceAST t) {
+        openingVisit(t);
+        acceptSiblings(t);
+        closingVisit(t);
+    }
+
+    protected void accept_v_AllChildren_v_Siblings(GroovySourceAST t) {
+        openingVisit(t);
+        acceptChildren(t);
+        closingVisit(t);
+        acceptSiblings(t);
+    }
+
+    protected void accept_v_AllChildren_v(GroovySourceAST t) {
+        openingVisit(t);
+        acceptChildren(t);
+        closingVisit(t);
+    }
+
+    protected void accept_FirstChild_v_RestOfTheChildren(GroovySourceAST t) {
+        accept(t.childAt(0));
+        openingVisit(t);
+        closingVisit(t);
+        acceptSiblings(t.childAt(0));
+    }
+    protected void accept_FirstChild_v_RestOfTheChildren_v_LastChild(GroovySourceAST t) {
+        int count = 0;
+        accept(t.childAt(0));
+        count++;
+        openingVisit(t);
+        if (t.childAt(0) != null) {
+            GroovySourceAST sibling = (GroovySourceAST)t.childAt(0).getNextSibling();
+            while (sibling != null) {
+                if (count == t.getNumberOfChildren() - 1) {closingVisit(t);}
+                accept(sibling);
+                count++;
+                sibling = (GroovySourceAST)sibling.getNextSibling();
+            }
+        }
+
+
+    }
+    protected void accept_FirstChild_v_RestOfTheChildren_v(GroovySourceAST t) {
+        accept(t.childAt(0));
+        openingVisit(t);
+        acceptSiblings(t.childAt(0));
+        closingVisit(t);
+    }
+    protected void accept_v_FirstChild_v_RestOfTheChildren(GroovySourceAST t) {
+        accept_v_FirstChild_v(t);
+        acceptSiblings(t.childAt(0));
+    }
+
+    protected void accept_v_FirstChild_v_RestOfTheChildren_v(GroovySourceAST t) {
+        openingVisit(t);
+        accept(t.childAt(0));
+        subsequentVisit(t);
+        acceptSiblings(t.childAt(0));
+        closingVisit(t);
+    }
+
+    protected void acceptSiblings(GroovySourceAST t) {
+        if (t != null) {
+            GroovySourceAST sibling = (GroovySourceAST)t.getNextSibling();
+            while (sibling != null) {
+                accept(sibling);
+                sibling = (GroovySourceAST)sibling.getNextSibling();
+            }
+        }
+    }
+
+    protected void acceptChildren(GroovySourceAST t) {
+        if (t != null) {
+            GroovySourceAST child = (GroovySourceAST)t.getFirstChild();
+            if (child != null){
+                accept(child);
+                acceptSiblings(child);
+            }
+        }
+    }
+
+    protected void skip(GroovySourceAST expr) {
+        unvisitedNodes.remove(expr);
+    }
+
+    protected void openingVisit(GroovySourceAST t) {
+        unvisitedNodes.remove(t);
+
+        int n = Visitor.OPENING_VISIT;
+        visitNode(t, n);
+    }
+
+    protected void secondVisit(GroovySourceAST t) {
+        int n = Visitor.SECOND_VISIT;
+        visitNode(t, n);
+    }
+
+    protected void subsequentVisit(GroovySourceAST t) {
+        int n = Visitor.SUBSEQUENT_VISIT;
+        visitNode(t, n);
+    }
+
+    protected void closingVisit(GroovySourceAST t) {
+        int n = Visitor.CLOSING_VISIT;
+        visitNode(t, n);
+    }
+    
+    public AST process(AST t) {
+        GroovySourceAST node = (GroovySourceAST) t;
+        
+        // process each node in turn
+        setUp(node);
+        accept(node);
+        acceptSiblings(node);
+        tearDown(node);
+        return null;
+    }    
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/Visitor.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/Visitor.java
new file mode 100644
index 0000000..94bc68b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/Visitor.java
@@ -0,0 +1,255 @@
+/*
+ * Copyright 2005 Jeremy Rayner
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+
+/**
+ * An interface for visiting a GroovySourceAST node.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+public interface Visitor {
+    int OPENING_VISIT = 1;
+    int SECOND_VISIT = 2; // only used on rare occasions, e.g. the '(' in this snippet...   @Foo  (  a=1, b=2, c=3)
+    int SUBSEQUENT_VISIT = 3;
+    int CLOSING_VISIT = 4;
+
+    void setUp();
+    void visitAbstract(GroovySourceAST t, int visit);
+    void visitAnnotation(GroovySourceAST t, int visit);
+    void visitAnnotations(GroovySourceAST t, int visit);
+    void visitAnnotationArrayInit(GroovySourceAST t, int visit);
+    void visitAnnotationDef(GroovySourceAST t, int visit);
+    void visitAnnotationFieldDef(GroovySourceAST t, int visit);
+    void visitAnnotationMemberValuePair(GroovySourceAST t, int visit);
+    void visitArrayDeclarator(GroovySourceAST t, int visit);
+    void visitAssign(GroovySourceAST t, int visit);
+    void visitAt(GroovySourceAST t, int visit);
+    void visitBand(GroovySourceAST t, int visit);
+    void visitBandAssign(GroovySourceAST t, int visit);
+    void visitBigSuffix(GroovySourceAST t, int visit);
+    void visitBlock(GroovySourceAST t, int visit);
+    void visitBnot(GroovySourceAST t, int visit);
+    void visitBor(GroovySourceAST t, int visit);
+    void visitBorAssign(GroovySourceAST t, int visit);
+    void visitBsr(GroovySourceAST t, int visit);
+    void visitBsrAssign(GroovySourceAST t, int visit);
+    void visitBxor(GroovySourceAST t, int visit);
+    void visitBxorAssign(GroovySourceAST t, int visit);
+    void visitCaseGroup(GroovySourceAST t, int visit);
+    void visitClassDef(GroovySourceAST t, int visit);
+    void visitClosedBlock(GroovySourceAST t, int visit);
+    void visitClosureList(GroovySourceAST t, int visit);
+    void visitClosureOp(GroovySourceAST t, int visit);
+    void visitColon(GroovySourceAST t, int visit);
+    void visitComma(GroovySourceAST t, int visit);
+    void visitCompareTo(GroovySourceAST t, int visit);
+    void visitCtorCall(GroovySourceAST t, int visit);
+    void visitCtorIdent(GroovySourceAST t, int visit);
+    void visitDec(GroovySourceAST t, int visit);
+    void visitDigit(GroovySourceAST t, int visit);
+    void visitDiv(GroovySourceAST t, int visit);
+    void visitDivAssign(GroovySourceAST t, int visit);
+    void visitDollar(GroovySourceAST t, int visit);
+    void visitDot(GroovySourceAST t, int visit);
+    void visitDynamicMember(GroovySourceAST t, int visit);
+    void visitElist(GroovySourceAST t, int visit);
+    void visitEmptyStat(GroovySourceAST t, int visit);
+    void visitEnumConstantDef(GroovySourceAST t, int visit);
+    void visitEnumDef(GroovySourceAST t, int visit);
+    void visitEof(GroovySourceAST t, int visit);
+    void visitEqual(GroovySourceAST t, int visit);
+    void visitEsc(GroovySourceAST t, int visit);
+    void visitExponent(GroovySourceAST t, int visit);
+    void visitExpr(GroovySourceAST t, int visit);
+    void visitExtendsClause(GroovySourceAST t, int visit);
+    void visitFinal(GroovySourceAST t, int visit);
+    void visitFloatSuffix(GroovySourceAST t, int visit);
+    void visitForCondition(GroovySourceAST t, int visit);
+    void visitForEachClause(GroovySourceAST t, int visit);
+    void visitForInit(GroovySourceAST t, int visit);
+    void visitForInIterable(GroovySourceAST t, int visit);
+    void visitForIterator(GroovySourceAST t, int visit);
+    void visitGe(GroovySourceAST t, int visit);
+    void visitGt(GroovySourceAST t, int visit);
+    void visitHexDigit(GroovySourceAST t, int visit);
+    void visitIdent(GroovySourceAST t, int visit);
+    void visitImplementsClause(GroovySourceAST t, int visit);
+    void visitImplicitParameters(GroovySourceAST t, int visit);
+    void visitImport(GroovySourceAST t, int visit);
+    void visitInc(GroovySourceAST t, int visit);
+    void visitIndexOp(GroovySourceAST t, int visit);
+    void visitInstanceInit(GroovySourceAST t, int visit);
+    void visitInterfaceDef(GroovySourceAST t, int visit);
+    void visitLabeledArg(GroovySourceAST t, int visit);
+    void visitLabeledStat(GroovySourceAST t, int visit);
+    void visitLand(GroovySourceAST t, int visit);
+    void visitLbrack(GroovySourceAST t, int visit);
+    void visitLcurly(GroovySourceAST t, int visit);
+    void visitLe(GroovySourceAST t, int visit);
+    void visitLetter(GroovySourceAST t, int visit);
+    void visitListConstructor(GroovySourceAST t, int visit);
+    void visitLiteralAs(GroovySourceAST t, int visit);
+    void visitLiteralAssert(GroovySourceAST t, int visit);
+    void visitLiteralBoolean(GroovySourceAST t, int visit);
+    void visitLiteralBreak(GroovySourceAST t, int visit);
+    void visitLiteralByte(GroovySourceAST t, int visit);
+    void visitLiteralCase(GroovySourceAST t, int visit);
+    void visitLiteralCatch(GroovySourceAST t, int visit);
+    void visitLiteralChar(GroovySourceAST t, int visit);
+    void visitLiteralClass(GroovySourceAST t, int visit);
+    void visitLiteralContinue(GroovySourceAST t, int visit);
+    void visitLiteralDef(GroovySourceAST t, int visit);
+    void visitLiteralDefault(GroovySourceAST t, int visit);
+    void visitLiteralDouble(GroovySourceAST t, int visit);
+    void visitLiteralElse(GroovySourceAST t, int visit);
+    void visitLiteralEnum(GroovySourceAST t, int visit);
+    void visitLiteralExtends(GroovySourceAST t, int visit);
+    void visitLiteralFalse(GroovySourceAST t, int visit);
+    void visitLiteralFinally(GroovySourceAST t, int visit);
+    void visitLiteralFloat(GroovySourceAST t, int visit);
+    void visitLiteralFor(GroovySourceAST t, int visit);
+    void visitLiteralIf(GroovySourceAST t, int visit);
+    void visitLiteralImplements(GroovySourceAST t, int visit);
+    void visitLiteralImport(GroovySourceAST t, int visit);
+    void visitLiteralIn(GroovySourceAST t, int visit);
+    void visitLiteralInstanceof(GroovySourceAST t, int visit);
+    void visitLiteralInt(GroovySourceAST t, int visit);
+    void visitLiteralInterface(GroovySourceAST t, int visit);
+    void visitLiteralLong(GroovySourceAST t, int visit);
+    void visitLiteralNative(GroovySourceAST t, int visit);
+    void visitLiteralNew(GroovySourceAST t, int visit);
+    void visitLiteralNull(GroovySourceAST t, int visit);
+    void visitLiteralPackage(GroovySourceAST t, int visit);
+    void visitLiteralPrivate(GroovySourceAST t, int visit);
+    void visitLiteralProtected(GroovySourceAST t, int visit);
+    void visitLiteralPublic(GroovySourceAST t, int visit);
+    void visitLiteralReturn(GroovySourceAST t, int visit);
+    void visitLiteralShort(GroovySourceAST t, int visit);
+    void visitLiteralStatic(GroovySourceAST t, int visit);
+    void visitLiteralSuper(GroovySourceAST t, int visit);
+    void visitLiteralSwitch(GroovySourceAST t, int visit);
+    void visitLiteralSynchronized(GroovySourceAST t, int visit);
+    void visitLiteralThis(GroovySourceAST t, int visit);
+    void visitLiteralThreadsafe(GroovySourceAST t, int visit);
+    void visitLiteralThrow(GroovySourceAST t, int visit);
+    void visitLiteralThrows(GroovySourceAST t, int visit);
+    void visitLiteralTransient(GroovySourceAST t, int visit);
+    void visitLiteralTrue(GroovySourceAST t, int visit);
+    void visitLiteralTry(GroovySourceAST t, int visit);
+    void visitLiteralVoid(GroovySourceAST t, int visit);
+    void visitLiteralVolatile(GroovySourceAST t, int visit);
+    void visitLiteralWhile(GroovySourceAST t, int visit);
+    void visitLnot(GroovySourceAST t, int visit);
+    void visitLor(GroovySourceAST t, int visit);
+    void visitLparen(GroovySourceAST t, int visit);
+    void visitLt(GroovySourceAST t, int visit);
+    void visitMapConstructor(GroovySourceAST t, int visit);
+    void visitMemberPointer(GroovySourceAST t, int visit);
+    void visitMethodCall(GroovySourceAST t, int visit);
+    void visitMethodDef(GroovySourceAST t, int visit);
+    void visitMinus(GroovySourceAST t, int visit);
+    void visitMinusAssign(GroovySourceAST t, int visit);
+    void visitMlComment(GroovySourceAST t, int visit);
+    void visitMod(GroovySourceAST t, int visit);
+    void visitModifiers(GroovySourceAST t, int visit);
+    void visitModAssign(GroovySourceAST t, int visit);
+    void visitNls(GroovySourceAST t, int visit);
+    void visitNotEqual(GroovySourceAST t, int visit);
+    void visitNullTreeLookahead(GroovySourceAST t, int visit);
+    void visitNumBigDecimal(GroovySourceAST t, int visit);
+    void visitNumBigInt(GroovySourceAST t, int visit);
+    void visitNumDouble(GroovySourceAST t, int visit);
+    void visitNumFloat(GroovySourceAST t, int visit);
+    void visitNumInt(GroovySourceAST t, int visit);
+    void visitNumLong(GroovySourceAST t, int visit);
+    void visitObjblock(GroovySourceAST t, int visit);
+    void visitOneNl(GroovySourceAST t, int visit);
+    void visitOptionalDot(GroovySourceAST t, int visit);
+    void visitPackageDef(GroovySourceAST t, int visit);
+    void visitParameters(GroovySourceAST t, int visit);
+    void visitParameterDef(GroovySourceAST t, int visit);
+    void visitPlus(GroovySourceAST t, int visit);
+    void visitPlusAssign(GroovySourceAST t, int visit);
+    void visitPostDec(GroovySourceAST t, int visit);
+    void visitPostInc(GroovySourceAST t, int visit);
+    void visitQuestion(GroovySourceAST t, int visit);
+    void visitRangeExclusive(GroovySourceAST t, int visit);
+    void visitRangeInclusive(GroovySourceAST t, int visit);
+    void visitRbrack(GroovySourceAST t, int visit);
+    void visitRcurly(GroovySourceAST t, int visit);
+    void visitRegexpCtorEnd(GroovySourceAST t, int visit);
+    void visitRegexpLiteral(GroovySourceAST t, int visit);
+    void visitRegexpSymbol(GroovySourceAST t, int visit);
+    void visitRegexFind(GroovySourceAST t, int visit);
+    void visitRegexMatch(GroovySourceAST t, int visit);
+    void visitRparen(GroovySourceAST t, int visit);
+    void visitSelectSlot(GroovySourceAST t, int visit);
+    void visitSemi(GroovySourceAST t, int visit);
+    void visitShComment(GroovySourceAST t, int visit);
+    void visitSl(GroovySourceAST t, int visit);
+    void visitSlist(GroovySourceAST t, int visit);
+    void visitSlAssign(GroovySourceAST t, int visit);
+    void visitSlComment(GroovySourceAST t, int visit);
+    void visitSpreadArg(GroovySourceAST t, int visit);
+    void visitSpreadDot(GroovySourceAST t, int visit);
+    void visitSpreadMapArg(GroovySourceAST t, int visit);
+    void visitSr(GroovySourceAST t, int visit);
+    void visitSrAssign(GroovySourceAST t, int visit);
+    void visitStar(GroovySourceAST t, int visit);
+    void visitStarAssign(GroovySourceAST t, int visit);
+    void visitStarStar(GroovySourceAST t, int visit);
+    void visitStarStarAssign(GroovySourceAST t, int visit);
+    void visitStaticImport(GroovySourceAST t, int visit);
+    void visitStaticInit(GroovySourceAST t, int visit);
+    void visitStrictfp(GroovySourceAST t, int visit);
+    void visitStringCh(GroovySourceAST t, int visit);
+    void visitStringConstructor(GroovySourceAST t, int visit);
+    void visitStringCtorEnd(GroovySourceAST t, int visit);
+    void visitStringCtorMiddle(GroovySourceAST t, int visit);
+    void visitStringCtorStart(GroovySourceAST t, int visit);
+    void visitStringLiteral(GroovySourceAST t, int visit);
+    void visitStringNl(GroovySourceAST t, int visit);
+    void visitSuperCtorCall(GroovySourceAST t, int visit);
+    void visitTripleDot(GroovySourceAST t, int visit);
+    void visitType(GroovySourceAST t, int visit);
+    void visitTypecast(GroovySourceAST t, int visit);
+    void visitTypeArgument(GroovySourceAST t, int visit);
+    void visitTypeArguments(GroovySourceAST t, int visit);
+    void visitTypeLowerBounds(GroovySourceAST t, int visit);
+    void visitTypeParameter(GroovySourceAST t, int visit);
+    void visitTypeParameters(GroovySourceAST t, int visit);
+    void visitTypeUpperBounds(GroovySourceAST t, int visit);
+    void visitUnaryMinus(GroovySourceAST t, int visit);
+    void visitUnaryPlus(GroovySourceAST t, int visit);
+    void visitUnusedConst(GroovySourceAST t, int visit);
+    void visitUnusedDo(GroovySourceAST t, int visit);
+    void visitUnusedGoto(GroovySourceAST t, int visit);
+    void visitVariableDef(GroovySourceAST t, int visit);
+    void visitVariableParameterDef(GroovySourceAST t, int visit);
+    void visitVocab(GroovySourceAST t, int visit);
+    void visitWildcardType(GroovySourceAST t, int visit);
+    void visitWs(GroovySourceAST t, int visit);
+
+    void visitDefault(GroovySourceAST t,int visit);
+    void tearDown();
+
+    void push(GroovySourceAST t);
+    GroovySourceAST pop();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/VisitorAdapter.java b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/VisitorAdapter.java
new file mode 100644
index 0000000..45e4fdc
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/VisitorAdapter.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.antlr.treewalker;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+
+
+/**
+ * A default implementation of all visitor methods.
+ * If you extend this class, any un-overriden visit methods will
+ * call visitDefault.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+public class VisitorAdapter implements Visitor {
+    public void setUp() {}
+    public void visitAbstract(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitAnnotation(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitAnnotations(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitAnnotationArrayInit(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitAnnotationDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitAnnotationFieldDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitAnnotationMemberValuePair(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitArrayDeclarator(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitAt(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBand(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBandAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBigSuffix(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBlock(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBnot(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBor(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBorAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBsr(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBsrAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBxor(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitBxorAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitCaseGroup(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitClassDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitClosedBlock(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitClosureOp(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitClosureList(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitColon(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitComma(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitCompareTo(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitCtorCall(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitCtorIdent(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitDec(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitDigit(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitDiv(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitDivAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitDollar(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitDot(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitDynamicMember(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitElist(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitEmptyStat(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitEnumConstantDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitEnumDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitEof(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitEqual(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitEsc(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitExponent(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitExpr(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitExtendsClause(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitFinal(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitFloatSuffix(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitForCondition(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitForEachClause(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitForInit(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitForInIterable(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitForIterator(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitGe(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitGt(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitHexDigit(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitIdent(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitImplementsClause(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitImplicitParameters(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitImport(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitInc(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitIndexOp(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitInstanceInit(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitInterfaceDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLabeledArg(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLabeledStat(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLand(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLbrack(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLcurly(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLe(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLetter(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitListConstructor(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralAs(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralAssert(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralBoolean(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralBreak(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralByte(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralCase(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralCatch(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralChar(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralClass(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralContinue(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralDefault(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralDouble(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralElse(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralEnum(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralExtends(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralFalse(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralFinally(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralFloat(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralFor(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralIf(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralImplements(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralImport(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralIn(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralInstanceof(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralInt(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralInterface(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralLong(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralNative(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralNew(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralNull(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralPackage(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralPrivate(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralProtected(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralPublic(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralReturn(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralShort(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralStatic(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralSuper(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralSwitch(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralSynchronized(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralThis(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralThreadsafe(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralThrow(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralThrows(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralTransient(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralTrue(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralTry(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralVoid(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralVolatile(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLiteralWhile(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLnot(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLor(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLparen(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitLt(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitMapConstructor(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitMemberPointer(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitMethodCall(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitMethodDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitMinus(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitMinusAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitMlComment(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitMod(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitModifiers(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitModAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitNls(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitNotEqual(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitNullTreeLookahead(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitNumBigDecimal(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitNumBigInt(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitNumDouble(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitNumFloat(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitNumInt(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitNumLong(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitObjblock(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitOneNl(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitOptionalDot(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitPackageDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitParameters(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitParameterDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitPlus(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitPlusAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitPostDec(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitPostInc(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitQuestion(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitRangeExclusive(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitRangeInclusive(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitRbrack(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitRcurly(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitRegexpCtorEnd(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitRegexpLiteral(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitRegexpSymbol(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitRegexFind(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitRegexMatch(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitRparen(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSelectSlot(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSemi(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitShComment(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSl(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSlist(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSlAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSlComment(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSpreadArg(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSpreadDot(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSpreadMapArg(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSr(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSrAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStar(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStarAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStarStar(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStarStarAssign(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStaticImport(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStaticInit(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStrictfp(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStringCh(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStringConstructor(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStringCtorEnd(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStringCtorMiddle(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStringCtorStart(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStringLiteral(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitStringNl(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitSuperCtorCall(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitTripleDot(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitType(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitTypecast(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitTypeArgument(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitTypeArguments(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitTypeLowerBounds(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitTypeParameter(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitTypeParameters(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitTypeUpperBounds(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitUnaryMinus(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitUnaryPlus(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitUnusedConst(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitUnusedDo(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitUnusedGoto(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitVariableDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitVariableParameterDef(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitVocab(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitWildcardType(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+    public void visitWs(GroovySourceAST t,int visit) {visitDefault(t,visit);}
+
+    public void visitDefault(GroovySourceAST t,int visit) {}
+    public void tearDown() {}
+
+    public void push(GroovySourceAST t) {}
+    public GroovySourceAST pop() {return null;}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/antlr/treewalker/package.html b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/package.html
new file mode 100644
index 0000000..649c7e2
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/antlr/treewalker/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.antlr.treewalker.*</title>
+  </head>
+  <body>
+    <p>Classes for walking the AST.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/ast/ASTNode.java b/groovy/src/main/org/codehaus/groovy/ast/ASTNode.java
new file mode 100644
index 0000000..3a8ef81
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/ASTNode.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+
+/**
+ * Base class for any AST node
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ASTNode {
+
+    private int lineNumber = -1;
+    private int columnNumber = -1;
+    private int lastLineNumber = -1;
+    private int lastColumnNumber = -1;
+
+    public void visit(GroovyCodeVisitor visitor) {
+        throw new RuntimeException("No visit() method implemented for class: " + getClass().getName());
+    }
+
+    public String getText() {
+        return "<not implemented yet for class: " + getClass().getName() + ">";
+    }
+
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    public void setLineNumber(int lineNumber) {
+        this.lineNumber = lineNumber;
+    }
+
+    public int getColumnNumber() {
+        return columnNumber;
+    }
+
+    public void setColumnNumber(int columnNumber) {
+        this.columnNumber = columnNumber;
+    }
+
+    public int getLastLineNumber() {
+        return lastLineNumber;
+    }
+
+    public void setLastLineNumber(int lastLineNumber) {
+        this.lastLineNumber = lastLineNumber;
+    }
+
+    public int getLastColumnNumber() {
+        return lastColumnNumber;
+    }
+
+    public void setLastColumnNumber(int lastColumnNumber) {
+        this.lastColumnNumber = lastColumnNumber;
+    }
+    
+    /**
+     * Sets the source position using another ASTNode.
+     * The sourcePosition consists of a line/column pair for
+     * the start and a line/column pair for the end of the
+     * expression or statement 
+     * 
+     */
+    public void setSourcePosition(ASTNode node) {
+        this.columnNumber = node.getColumnNumber();
+        this.lastLineNumber = node.getLastLineNumber();
+        this.lastColumnNumber = node.getLastColumnNumber();
+        this.lineNumber = node.getLineNumber();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/AnnotatedNode.java b/groovy/src/main/org/codehaus/groovy/ast/AnnotatedNode.java
new file mode 100644
index 0000000..7dfcc57
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/AnnotatedNode.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import java.util.*;
+
+/**
+ * Base class for any AST node which is capable of being annotated
+ *
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @version $Revision$
+ */
+public class AnnotatedNode extends ASTNode {
+    private Map annotations = Collections.EMPTY_MAP;
+    private Map annotationClasses = Collections.EMPTY_MAP;
+    private boolean synthetic;
+    ClassNode declaringClass;
+
+    public AnnotatedNode() {
+    }
+
+    public Map getAnnotations() {
+        return annotations;
+    }
+
+    public AnnotationNode getAnnotations(String name) {
+        return annotations == Collections.EMPTY_MAP ? null : (AnnotationNode) annotations.get(name);
+    }
+    
+    public ClassNode getAnnotationClass(String name) {
+        return annotationClasses == Collections.EMPTY_MAP ? null : (ClassNode) annotationClasses.get(name);
+    }
+
+    public void addAnnotation(String name, AnnotationNode value) {
+        checkInit();
+
+        annotationClasses.put(name,value.getClassNode());
+        AnnotationNode oldValue = (AnnotationNode) annotations.get(name);
+
+        // TODO can we support many annotations of the same name?
+        if (oldValue == null) {
+            annotations.put(name, value);
+        }
+        else {
+            List list = null;
+            if (oldValue instanceof List) {
+                list = (List) oldValue;
+            }
+            else {
+                list = new ArrayList();
+                list.add(oldValue);
+                annotations.put(name, list);
+            }
+            list.add(value);
+        }
+    }
+
+    private void checkInit() {
+        if (annotations == Collections.EMPTY_MAP)
+          annotations = new HashMap();
+        if (annotationClasses == Collections.EMPTY_MAP)
+          annotationClasses = new HashMap();
+    }
+
+    public void addAnnotations(List annotations) {
+        for (Iterator iter = annotations.iterator(); iter.hasNext();) {
+            AnnotationNode node = (AnnotationNode) iter.next();
+            addAnnotation(node.getClassNode().getName(), node);
+        }
+    }
+
+    public boolean isSynthetic() {
+        return synthetic;
+    }
+
+    public void setSynthetic(boolean synthetic) {
+        this.synthetic = synthetic;
+    }
+
+    public ClassNode getDeclaringClass() {
+        return declaringClass;
+    }
+
+    /**
+     * @param declaringClass The declaringClass to set.
+     */
+    public void setDeclaringClass(ClassNode declaringClass) {
+        this.declaringClass = declaringClass;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/AnnotationNode.java b/groovy/src/main/org/codehaus/groovy/ast/AnnotationNode.java
new file mode 100644
index 0000000..212a8ab
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/AnnotationNode.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.groovy.ast.expr.Expression;
+
+
+/**
+ * Represents an annotation which can be attached to interfaces, classes, methods and fields.
+ * 
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>
+ * @version $Revision$
+ */
+public class AnnotationNode extends ASTNode {
+    public static final int TYPE_TARGET = 1;
+    public static final int CONSTRUCTOR_TARGET = 1 << 1;
+    public static final int METHOD_TARGET = 1 << 2;
+    public static final int FIELD_TARGET = 1 << 3;
+    public static final int PARAMETER_TARGET =  1 << 4;
+    public static final int LOCAL_VARIABLE_TARGET = 1 << 5;
+    public static final int ANNOTATION_TARGET = 1 << 6;
+    private static final int ALL_TARGETS = TYPE_TARGET | CONSTRUCTOR_TARGET | METHOD_TARGET
+        | FIELD_TARGET | PARAMETER_TARGET | LOCAL_VARIABLE_TARGET | ANNOTATION_TARGET;
+    
+    private final ClassNode classNode;
+    private Map members = new HashMap();
+    private boolean runtimeRetention= false;
+    private boolean sourceRetention= false;
+    private int allowedTargets = ALL_TARGETS;
+    private boolean valid;
+
+    public AnnotationNode(ClassNode classNode) {
+        this.classNode = classNode;
+    }
+
+    public ClassNode getClassNode() {
+        return classNode;
+    }
+
+    public Map getMembers() {
+        return members;
+    }
+    
+    public Expression getMember(String name) {
+        return (Expression) members.get(name);
+    }
+    
+    public void addMember(String name, Expression value) {
+        Expression oldValue = (Expression) members.get(name);
+        if (oldValue == null) {
+            members.put(name, value);
+        }
+        else {
+            List list = null;
+            if (oldValue instanceof List) {
+                list = (List) oldValue;
+            }
+            else {
+                list = new ArrayList();
+                list.add(oldValue);
+                members.put(name, list);
+            }
+            list.add(value);
+        }
+    }
+
+    public void setMember(String name, Expression value) {
+        members.put(name, value);
+    }
+    
+    public boolean isBuiltIn(){
+        return false;
+    }
+
+    /**
+     * Flag corresponding to <code>RetentionPolicy</code>.
+     * @return <tt>true</tt> if the annotation should be visible at runtime, 
+     *      <tt>false</tt> otherwise
+     */
+    public boolean hasRuntimeRetention() {
+        return this.runtimeRetention;
+    }
+
+    /**
+     * Sets the internal flag of this annotation runtime retention policy.
+     * If the current annotation has 
+     * <code>RetentionPolicy.RUNTIME</code> or if <tt>false</tt>
+     * if the <code>RetentionPolicy.CLASS</code>.
+     * @param flag if <tt>true</tt> then current annotation is marked as having
+     *     <code>RetentionPolicy.RUNTIME</code>. If <tt>false</tt> then
+     *     the annotation has <code>RetentionPolicy.CLASS</code>.
+     */
+    public void setRuntimeRetention(boolean flag) {
+        this.runtimeRetention = flag;
+    }
+    
+    /**
+     * Flag corresponding to <code>RetentionPolicy.SOURCE</code>.
+     * @return <tt>true</tt> if the annotation is only allowed in sources 
+     *      <tt>false</tt> otherwise
+     */
+    public boolean hasSourceRetention() {
+        return this.sourceRetention;
+    }
+
+    /** Sets the internal flag if the current annotation has 
+     * <code>RetentionPolicy.SOURCE</code>.
+     */ 
+    public void setSourceRetention(boolean flag) {
+        this.sourceRetention = flag;
+    }
+
+    public void setAllowedTargets(int bitmap) {
+        this.allowedTargets = bitmap;
+    }
+    
+    public boolean isTargetAllowed(int target) {
+        return (this.allowedTargets & target) == target;
+    }
+    
+    /**
+     * Set if the current annotation is verified and passed all
+     * validations
+     * @param flag
+     */
+    public void setValid(boolean flag) {
+        this.valid = flag;
+    }
+    
+    /**
+     * Returns the state of this annotation (verified and all verification passed).
+     */
+    public boolean isValid() {
+        return this.valid;
+    }
+    
+    public static final String targetToName(int target) {
+        switch(target) {
+            case TYPE_TARGET:
+                return "TYPE";
+            case CONSTRUCTOR_TARGET:
+                return "CONSTRUCTOR";
+            case METHOD_TARGET:
+                return "METHOD";
+            case FIELD_TARGET:
+                return "FIELD";
+            case PARAMETER_TARGET:
+                return "PARAMETER";
+            case LOCAL_VARIABLE_TARGET:
+                return "LOCAL_VARIABLE";
+            case ANNOTATION_TARGET:
+                return "ANNOTATION";
+            default:
+                return "unknown target";
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/ClassCodeExpressionTransformer.java b/groovy/src/main/org/codehaus/groovy/ast/ClassCodeExpressionTransformer.java
new file mode 100644
index 0000000..7840966
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/ClassCodeExpressionTransformer.java
@@ -0,0 +1,150 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.ast;

+

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+

+import org.codehaus.groovy.ast.expr.*;

+import org.codehaus.groovy.ast.stmt.AssertStatement;

+import org.codehaus.groovy.ast.stmt.CaseStatement;

+import org.codehaus.groovy.ast.stmt.DoWhileStatement;

+import org.codehaus.groovy.ast.stmt.ExpressionStatement;

+import org.codehaus.groovy.ast.stmt.ForStatement;

+import org.codehaus.groovy.ast.stmt.IfStatement;

+import org.codehaus.groovy.ast.stmt.ReturnStatement;

+import org.codehaus.groovy.ast.stmt.Statement;

+import org.codehaus.groovy.ast.stmt.SwitchStatement;

+import org.codehaus.groovy.ast.stmt.SynchronizedStatement;

+import org.codehaus.groovy.ast.stmt.ThrowStatement;

+import org.codehaus.groovy.ast.stmt.WhileStatement;

+

+/**

+ * Visitor to transform expressions in a whole class. 

+ * Transformed Expressions are usually not visited. 

+ *

+ * @author Jochen Theodorou

+ */

+public abstract class ClassCodeExpressionTransformer extends ClassCodeVisitorSupport implements ExpressionTransformer {

+    

+    protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {

+        Parameter[] paras = node.getParameters();

+        for (int i=0; i<paras.length; i++) {

+            Parameter p = paras[i];

+            if (p.hasInitialExpression()) {

+                Expression init = p.getInitialExpression();

+                p.setInitialExpression(transform(init));

+            }

+        }

+        super.visitConstructorOrMethod(node,isConstructor);

+    }

+

+    public void visitSwitch(SwitchStatement statement) {

+        Expression exp = statement.getExpression();

+        statement.setExpression(transform(exp));

+        List list = statement.getCaseStatements();

+        for (Iterator iter = list.iterator(); iter.hasNext(); ) {

+            CaseStatement caseStatement = (CaseStatement) iter.next();

+            caseStatement.visit(this);

+        }

+        statement.getDefaultStatement().visit(this);

+    }

+

+    public void visitField(FieldNode node) {

+        visitAnnotations(node);

+        Expression init = node.getInitialExpression();

+        node.setInitialValueExpression(transform(init));

+    }

+    

+    public void visitProperty(PropertyNode node) {

+        visitAnnotations(node);

+        Statement statement = node.getGetterBlock();

+        visitClassCodeContainer(statement);

+        

+        statement = node.getSetterBlock();

+        visitClassCodeContainer(statement);

+    }

+    

+    public void visitIfElse(IfStatement ifElse) {

+        ifElse.setBooleanExpression((BooleanExpression) (transform(ifElse.getBooleanExpression())));

+        ifElse.getIfBlock().visit(this);

+        ifElse.getElseBlock().visit(this);

+    }

+

+    public Expression transform(Expression exp) {

+        if (exp==null) return null;

+        return exp.transformExpression(this);

+    }

+        

+    public void visitAnnotations(AnnotatedNode node) {

+        Map annotionMap = node.getAnnotations();

+        if (annotionMap.isEmpty()) return;

+        Iterator it = annotionMap.values().iterator();

+        while (it.hasNext()) {

+            AnnotationNode an = (AnnotationNode) it.next();

+            //skip builtin properties

+            if (an.isBuiltIn()) continue;

+            for (Iterator iter = an.getMembers().entrySet().iterator(); iter.hasNext();) {

+                Map.Entry member = (Map.Entry) iter.next();

+                Expression memberValue = (Expression) member.getValue();

+                member.setValue(transform(memberValue));

+            }

+        }

+    }

+

+    public void visitReturnStatement(ReturnStatement statement) {

+       statement.setExpression(transform(statement.getExpression()));

+    }

+

+    public void visitAssertStatement(AssertStatement as) {

+        as.setBooleanExpression((BooleanExpression) (transform(as.getBooleanExpression())));

+        as.setMessageExpression(transform(as.getMessageExpression()));

+    }

+

+    public void visitCaseStatement(CaseStatement statement) {

+    	statement.setExpression(transform(statement.getExpression()));

+    	statement.getCode().visit(this);

+    }

+

+    public void visitDoWhileLoop(DoWhileStatement loop) {

+        loop.setBooleanExpression((BooleanExpression) (transform(loop.getBooleanExpression())));

+        super.visitDoWhileLoop(loop);

+    }

+

+    public void visitForLoop(ForStatement forLoop) {

+        forLoop.setCollectionExpression(transform(forLoop.getCollectionExpression()));

+        super.visitForLoop(forLoop);

+    }

+

+    public void visitSynchronizedStatement(SynchronizedStatement sync) {

+        sync.setExpression(transform(sync.getExpression()));

+        super.visitSynchronizedStatement(sync);

+    }

+

+    public void visitThrowStatement(ThrowStatement ts) {

+        ts.setExpression(transform(ts.getExpression()));

+    }

+

+    public void visitWhileLoop(WhileStatement loop) {

+    	loop.setBooleanExpression((BooleanExpression) transform(loop.getBooleanExpression()));

+    	super.visitWhileLoop(loop);

+    }

+

+    public void visitExpressionStatement(ExpressionStatement es) {

+        es.setExpression(transform(es.getExpression()));

+    }    

+}

diff --git a/groovy/src/main/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java b/groovy/src/main/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java
new file mode 100644
index 0000000..d6e5c4c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/ClassCodeVisitorSupport.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.BreakStatement;
+import org.codehaus.groovy.ast.stmt.CaseStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.ContinueStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.SwitchStatement;
+import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
+import org.codehaus.groovy.ast.stmt.ThrowStatement;
+import org.codehaus.groovy.ast.stmt.TryCatchStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+public abstract class ClassCodeVisitorSupport extends CodeVisitorSupport implements GroovyClassVisitor {
+    
+    public void visitClass(ClassNode node) {
+        visitAnnotations(node);
+        node.visitContents(this);
+        List list = node.getObjectInitializerStatements();
+        for (Iterator iter = list.iterator(); iter.hasNext();) {
+            Statement element = (Statement) iter.next();
+            element.visit(this);
+        }
+    }
+    
+    public void visitAnnotations(AnnotatedNode node) {
+        Map annotionMap = node.getAnnotations();
+        if (annotionMap.isEmpty()) return;
+        Iterator it = annotionMap.values().iterator(); 
+        while (it.hasNext()) {
+            AnnotationNode an = (AnnotationNode) it.next();
+            //skip builtin properties
+            if (an.isBuiltIn()) continue;
+            for (Iterator iter = an.getMembers().entrySet().iterator(); iter.hasNext();) {
+                Map.Entry member = (Map.Entry) iter.next();
+                Expression memberValue = (Expression) member.getValue();
+                memberValue.visit(this);
+            }  
+        }
+    }
+        
+    protected void visitClassCodeContainer(Statement code) {
+        if (code != null) code.visit(this);
+    }
+
+    protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+        visitAnnotations(node);
+        Statement code = node.getCode();
+        
+        visitClassCodeContainer(code);
+    }
+    
+    public void visitConstructor(ConstructorNode node) {
+        visitConstructorOrMethod(node,true);        
+    }
+
+    public void visitMethod(MethodNode node) {
+        visitConstructorOrMethod(node,false);
+    }
+
+    public void visitField(FieldNode node) {
+        visitAnnotations(node);
+        Expression init = node.getInitialExpression();
+        if (init != null) init.visit(this);
+    }
+    
+    public void visitProperty(PropertyNode node) {
+        visitAnnotations(node);
+        Statement statement = node.getGetterBlock();
+        visitClassCodeContainer(statement);
+        
+        statement = node.getSetterBlock();
+        visitClassCodeContainer(statement);
+        
+        Expression init = node.getInitialExpression();
+        if (init != null) init.visit(this);
+    }
+
+    protected void addError(String msg, ASTNode expr) {
+        int line = expr.getLineNumber();
+        int col = expr.getColumnNumber();
+        SourceUnit source = getSourceUnit();
+        source.getErrorCollector().addErrorAndContinue(
+          new SyntaxErrorMessage(new SyntaxException(msg + '\n', line, col), source)
+        );
+    }
+    
+    protected abstract SourceUnit getSourceUnit();
+    
+    protected void visitStatement(Statement statement) {}
+    
+    public void visitAssertStatement(AssertStatement statement) {
+        visitStatement(statement);
+        super.visitAssertStatement(statement);
+    }
+    
+    public void visitBlockStatement(BlockStatement block) {
+        visitStatement(block);
+        super.visitBlockStatement(block);
+    }
+    
+    public void visitBreakStatement(BreakStatement statement) {
+        visitStatement(statement);
+        super.visitBreakStatement(statement);
+    }
+    
+    public void visitCaseStatement(CaseStatement statement) {
+        visitStatement(statement);
+        super.visitCaseStatement(statement);
+    }
+    
+    public void visitCatchStatement(CatchStatement statement) {
+        visitStatement(statement);
+        super.visitCatchStatement(statement);
+    }
+    
+    public void visitContinueStatement(ContinueStatement statement) {
+        visitStatement(statement);
+        super.visitContinueStatement(statement);
+    }
+    
+    public void visitDoWhileLoop(DoWhileStatement loop) {
+        visitStatement(loop);
+        super.visitDoWhileLoop(loop);
+    }
+    
+    public void visitExpressionStatement(ExpressionStatement statement) {
+        visitStatement(statement);
+        super.visitExpressionStatement(statement);
+    }
+    
+    public void visitForLoop(ForStatement forLoop) {
+        visitStatement(forLoop);
+        super.visitForLoop(forLoop);
+    }
+    
+    public void visitIfElse(IfStatement ifElse) {
+        visitStatement(ifElse);
+        super.visitIfElse(ifElse);
+    }
+    
+    public void visitReturnStatement(ReturnStatement statement) {
+        visitStatement(statement);
+        super.visitReturnStatement(statement);
+    }
+    
+    public void visitSwitch(SwitchStatement statement) {
+        visitStatement(statement);
+        super.visitSwitch(statement);
+    }
+    
+    public void visitSynchronizedStatement(SynchronizedStatement statement) {
+        visitStatement(statement);
+        super.visitSynchronizedStatement(statement);
+    }
+    
+    public void visitThrowStatement(ThrowStatement statement) {
+        visitStatement(statement);
+        super.visitThrowStatement(statement);
+    }
+    
+    public void visitTryCatchFinally(TryCatchStatement statement) {
+        visitStatement(statement);
+        super.visitTryCatchFinally(statement);
+    }
+    
+    public void visitWhileLoop(WhileStatement loop) {
+        visitStatement(loop);
+        super.visitWhileLoop(loop);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/ClassHelper.java b/groovy/src/main/org/codehaus/groovy/ast/ClassHelper.java
new file mode 100644
index 0000000..842c6bc
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/ClassHelper.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.ast;
+
+import groovy.lang.Closure;
+import groovy.lang.GString;
+import groovy.lang.MetaClass;
+import groovy.lang.Range;
+import groovy.lang.Reference;
+import groovy.lang.Script;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.codehaus.groovy.runtime.GeneratedClosure;
+import org.codehaus.groovy.vmplugin.VMPluginFactory;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * This class is a Helper for ClassNode and classes handling ClassNodes.
+ * It does contain a set of predefined ClassNodes for the most used 
+ * types and some code for cached ClassNode creation and basic 
+ * ClassNode handling 
+ * 
+ * @author Jochen Theodorou
+ */
+public class ClassHelper {
+
+    private static final Class[] classes = new Class[] {
+        Object.class, Boolean.TYPE, Character.TYPE, Byte.TYPE, Short.TYPE,
+        Integer.TYPE, Long.TYPE, Double.TYPE, Float.TYPE, Void.TYPE,
+        Closure.class, GString.class, List.class, Map.class, Range.class,
+        Pattern.class, Script.class, String.class,  Boolean.class, 
+        Character.class, Byte.class, Short.class, Integer.class, Long.class,
+        Double.class, Float.class, BigDecimal.class, BigInteger.class, Void.class,
+        Reference.class, Class.class, MetaClass.class,     
+    };
+
+    private static final String[] primitiveClassNames = new String[] {
+        "", "boolean", "char", "byte", "short", 
+        "int", "long", "double", "float", "void"
+    };
+    
+    public static final ClassNode 
+        DYNAMIC_TYPE = new ClassNode(Object.class),  OBJECT_TYPE = DYNAMIC_TYPE,
+        VOID_TYPE = new ClassNode(Void.TYPE),        CLOSURE_TYPE = new ClassNode(Closure.class),
+        GSTRING_TYPE = new ClassNode(GString.class), LIST_TYPE = makeWithoutCaching(List.class),
+        MAP_TYPE = new ClassNode(Map.class),         RANGE_TYPE = new ClassNode(Range.class),
+        PATTERN_TYPE = new ClassNode(Pattern.class), STRING_TYPE = new ClassNode(String.class),
+        SCRIPT_TYPE = new ClassNode(Script.class),   REFERENCE_TYPE = makeWithoutCaching(Reference.class),
+        
+        boolean_TYPE = new ClassNode(boolean.class),     char_TYPE = new ClassNode(char.class),
+        byte_TYPE = new ClassNode(byte.class),           int_TYPE = new ClassNode(int.class),
+        long_TYPE = new ClassNode(long.class),           short_TYPE = new ClassNode(short.class),
+        double_TYPE = new ClassNode(double.class),       float_TYPE = new ClassNode(float.class),
+        Byte_TYPE = new ClassNode(Byte.class),           Short_TYPE = new ClassNode(Short.class),
+        Integer_TYPE = new ClassNode(Integer.class),     Long_TYPE = new ClassNode(Long.class),
+        Character_TYPE = new ClassNode(Character.class), Float_TYPE = new ClassNode(Float.class),
+        Double_TYPE = new ClassNode(Double.class),       Boolean_TYPE = new ClassNode(Boolean.class),
+        BigInteger_TYPE =  new ClassNode(java.math.BigInteger.class),
+        BigDecimal_TYPE = new ClassNode(java.math.BigDecimal.class),
+        void_WRAPPER_TYPE = new ClassNode(Void.class),   
+        
+        CLASS_Type = new ClassNode(Class.class),        METACLASS_TYPE = new ClassNode(MetaClass.class),
+        GENERATED_CLOSURE_Type = new ClassNode(GeneratedClosure.class),
+        Enum_Type = new ClassNode("java.lang.Enum",0,OBJECT_TYPE);
+        
+    static {
+        Enum_Type.isPrimaryNode = false;
+    }
+    
+    private static ClassNode[] types = new ClassNode[] {
+        OBJECT_TYPE,
+        boolean_TYPE, char_TYPE, byte_TYPE, short_TYPE,
+        int_TYPE, long_TYPE, double_TYPE, float_TYPE,
+        VOID_TYPE, CLOSURE_TYPE, GSTRING_TYPE,
+        LIST_TYPE, MAP_TYPE, RANGE_TYPE, PATTERN_TYPE,
+        SCRIPT_TYPE, STRING_TYPE, Boolean_TYPE, Character_TYPE,
+        Byte_TYPE, Short_TYPE, Integer_TYPE, Long_TYPE,
+        Double_TYPE, Float_TYPE, BigDecimal_TYPE, BigInteger_TYPE, 
+        void_WRAPPER_TYPE, REFERENCE_TYPE, CLASS_Type, METACLASS_TYPE,
+        GENERATED_CLOSURE_Type, Enum_Type
+    };
+
+    
+    private static ClassNode[] numbers = new ClassNode[] {
+        char_TYPE, byte_TYPE, short_TYPE, int_TYPE, long_TYPE, 
+        double_TYPE, float_TYPE, Short_TYPE, Byte_TYPE, Character_TYPE,
+        Integer_TYPE, Float_TYPE, Long_TYPE, Double_TYPE, BigInteger_TYPE,
+        BigDecimal_TYPE
+    };
+
+    protected static final ClassNode[] EMPTY_TYPE_ARRAY = {};
+    
+    public static final String OBJECT = "java.lang.Object";    
+    
+    
+    /**
+     * Creates an array of ClassNodes using an array of classes.
+     * For each of the given classes a new ClassNode will be 
+     * created
+     * @see #make(Class)
+     * @param classes an array of classes used to create the ClassNodes
+     * @return an array of ClassNodes
+     */
+    public static ClassNode[] make(Class[] classes) {
+    	ClassNode[] cns = new ClassNode[classes.length];
+    	for (int i=0; i<cns.length; i++) {
+    		cns[i] = make(classes[i]);
+    	}
+    	
+    	return cns;
+    }
+    
+    /**
+     * Creates a ClassNode using a given class.
+     * A new ClassNode object is only created if the class
+     * is not one of the predefined ones
+     * 
+     * @param c class used to created the ClassNode
+     * @return ClassNode instance created from the given class
+     */
+    public static ClassNode make(Class c) {
+        return make(c,true);
+    }
+    
+    public static ClassNode make(Class c, boolean includeGenerics) {
+        for (int i=0; i<classes.length; i++) {
+            if (c==classes[i]) return types[i];
+        }
+        if (c.isArray()) {
+            ClassNode cn = make(c.getComponentType(),includeGenerics);
+            return cn.makeArray();
+        }
+        return makeWithoutCaching(c,includeGenerics);
+    }
+    
+    public static ClassNode makeWithoutCaching(Class c){
+        return makeWithoutCaching(c,true);
+    }
+    
+    public static ClassNode makeWithoutCaching(Class c, boolean includeGenerics){
+        ClassNode t = new ClassNode(c);
+        if (includeGenerics) VMPluginFactory.getPlugin().setGenericsTypes(t);
+        return t;
+    }
+    
+    
+    /**
+     * Creates a ClassNode using a given class.
+     * Unlike make(String) this method will not use the cache
+     * to create the ClassNode. This means the ClassNode created
+     * from this method using the same name will have a different
+     * references
+     * 
+     * @see #make(String)
+     * @param name of the class the ClassNode is representing
+     */
+    public static ClassNode makeWithoutCaching(String name) { 
+        ClassNode cn = new ClassNode(name,Opcodes.ACC_PUBLIC,OBJECT_TYPE);
+        cn.isPrimaryNode = false;
+        return cn;
+    }
+    
+    /**
+     * Creates a ClassNode using a given class.
+     * If the name is one of the predefined ClassNodes then the 
+     * corresponding ClassNode instance will be returned. If the
+     * is null of of length 0 the dynamic type is returned
+     * 
+     * @param name of the class the ClassNode is representing
+     */
+    public static ClassNode make(String name) {
+        if (name == null || name.length() == 0) return DYNAMIC_TYPE;
+        
+        for (int i=0; i<primitiveClassNames.length; i++) {
+            if (primitiveClassNames[i].equals(name)) return types[i];
+        }
+        
+        for (int i=0; i<classes.length; i++) {
+            String cname = classes[i].getName();
+            if (name.equals(cname)) return types[i];
+        }        
+        return makeWithoutCaching(name);
+    }
+    
+    /**
+     * Creates a ClassNode containing the wrapper of a ClassNode 
+     * of primitive type. Any ClassNode representing a primitive
+     * type should be created using the predefined types used in
+     * class. The method will check the parameter for known 
+     * references of ClassNode representing a primitive type. If
+     * Reference is found, then a ClassNode will be contained that
+     * represents the wrapper class. For exmaple for boolean, the 
+     * wrapper class is java.lang.Boolean.
+     * 
+     * If the parameter is no primitve type, the redirected 
+     * ClassNode will be returned 
+     *   
+     * @see #make(Class)
+     * @see #make(String)
+     * @param cn the ClassNode containing a possible primitive type
+     */
+    public static ClassNode getWrapper(ClassNode cn) {
+        cn = cn.redirect();
+        if (!isPrimitiveType(cn)) return cn;
+        if (cn==boolean_TYPE) {
+            return Boolean_TYPE;
+        } else if (cn==byte_TYPE) {
+            return Byte_TYPE;
+        } else if (cn==char_TYPE) {
+            return Character_TYPE;
+        } else if (cn==short_TYPE) {
+            return Short_TYPE;
+        } else if (cn==int_TYPE) {
+            return Integer_TYPE;
+        } else if (cn==long_TYPE) {
+            return Long_TYPE;
+        } else if (cn==float_TYPE) {
+            return Float_TYPE;
+        } else if (cn==double_TYPE) {
+            return Double_TYPE;
+        } else if (cn==VOID_TYPE) {
+        	return void_WRAPPER_TYPE;
+        }
+        else {
+            return cn;
+        }
+    }
+    
+    /**
+     * Test to determine if a ClasNode is a primitve type. 
+     * Note: this only works for ClassNodes created using a
+     * predefined ClassNode
+     * 
+     * @see #make(Class)
+     * @see #make(String)
+     * @param cn the ClassNode containing a possible primitive type
+     * @return true if the ClassNode is a primitve type
+     */
+    public static boolean isPrimitiveType(ClassNode cn) {
+        return  cn == boolean_TYPE ||
+                cn == char_TYPE ||
+                cn == byte_TYPE ||
+                cn == short_TYPE ||
+                cn == int_TYPE ||
+                cn == long_TYPE ||
+                cn == float_TYPE ||
+                cn == double_TYPE ||
+                cn == VOID_TYPE;
+    }
+
+    public static ClassNode makeReference() {
+        return make(Reference.class);
+    }
+
+    public static boolean isUnresolvedEnum(ClassNode node) {
+        if (Enum_Type.isResolved()) return false;
+        if (node.isResolved()) return false;
+        ClassNode superClass = node.getSuperClass();
+        if (superClass==null) return false;
+        return superClass.redirect()==Enum_Type;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/ClassNode.java b/groovy/src/main/org/codehaus/groovy/ast/ClassNode.java
new file mode 100644
index 0000000..eee1ec0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/ClassNode.java
@@ -0,0 +1,1073 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import groovy.lang.GroovyObject;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.EmptyStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Represents a class in the AST.<br/>
+ * A ClassNode should be created using the methods in ClassHelper. 
+ * This ClassNode may be used to represent a class declaration or
+ * any other type. This class uses a proxy meschanism allowing to
+ * create a class for a plain name at ast creation time. In another 
+ * phase of the compiler the real ClassNode for the plain name may be
+ * found. To avoid the need of exchanging this ClassNode with an 
+ * instance of the correct ClassNode the correct ClassNode is set as 
+ * redirect. Most method calls are then redirected to that ClassNode.
+ * <br>
+ * <b>Note:</b> the proxy mechanism is only allowed for classes being marked
+ * as primary ClassNode which means they represent no actual class. 
+ * The redirect itself can be any type of ClassNode
+ * <br>
+ * To descirbe generic type signature see {@link #getGenericsTypes()} and
+ * {@link #setGenericsTypes(GenericsType[])}. These emthods are not proxied,
+ * they describe the type signature used at the point of declaration or the
+ * type signatures provided by the class. If the type signatures provided
+ * by the class are needed, then a call to {@link #redirect()} will help.
+ *
+ * @see org.codehaus.groovy.ast.ClassHelper
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Jochen Theodorou
+ * @version $Revision$
+ */
+public class ClassNode extends AnnotatedNode implements Opcodes {
+
+    private static class MapOfLists {
+        private Map map = new HashMap();
+        public List get(Object key) {
+            return (List) map.get(key);
+        }
+        public List getNotNull(Object key) {
+            List ret = get(key);
+            if (ret==null) ret = Collections.EMPTY_LIST;
+            return ret;
+        }
+        public void put(Object key, Object value) {
+            if (map.containsKey(key)) {
+                get(key).add(value);
+            } else {
+                ArrayList list = new ArrayList(2);
+                list.add(value);
+                map.put(key, list);
+            }
+        }        
+    }
+    
+    public static ClassNode[] EMPTY_ARRAY = new ClassNode[0];
+    
+    public static ClassNode THIS = new ClassNode(Object.class);
+    public static ClassNode SUPER = new ClassNode(Object.class);
+    
+    private String name;
+    private final int modifiers;
+    private ClassNode[] interfaces;
+    private MixinNode[] mixins;
+    private List constructors = new ArrayList();
+    private List  objectInitializers = new ArrayList();
+    private MapOfLists methods;
+    private List methodsList;
+    private List fields = new ArrayList();
+    private List properties = new ArrayList();
+    private Map fieldIndex = new HashMap();
+    private ModuleNode module;
+    private CompileUnit compileUnit;
+    private boolean staticClass = false;
+    private boolean scriptBody = false;
+    private boolean script;
+    private ClassNode superClass;
+    boolean isPrimaryNode;
+    
+    // use this to synchronize access for the lazy intit
+    protected Object lazyInitLock = new Object();
+
+    // clazz!=null when resolved
+    protected Class clazz;
+    // only false when this classNode is constructed from a class 
+    private boolean lazyInitDone=true;
+    // not null if if the ClassNode is an array 
+    private ClassNode componentType = null;
+    // if not null this instance is handled as proxy 
+    // for the redirect
+    private ClassNode redirect=null; 
+    // flag if the classes or its members are annotated
+    private boolean annotated;
+    
+    // type spec for generics
+    private GenericsType[] genericsTypes=null;
+    private boolean usesGenerics=false;
+    
+    // if set to true the name getGenericsTypes consists
+    // of 1 element describing the name of the placeholder
+    private boolean placeholder;
+    
+    /**
+     * Returns the ClassNode this ClassNode is redirecting to.
+     */
+    public ClassNode redirect(){
+        if (redirect==null) return this;
+        return redirect.redirect();
+    }
+    
+    /**
+     * Sets this instance as proxy for the given ClassNode. 
+     * @param cn the class to redirect to. If set to null the redirect will be removed
+     */
+    public void setRedirect(ClassNode cn) {
+        if (isPrimaryNode) throw new GroovyBugError("tried to set a redirect for a primary ClassNode ("+getName()+"->"+cn.getName()+").");
+        if (cn!=null) cn = cn.redirect();
+        if (cn==this) return;
+        redirect = cn;
+    }
+    
+    /**
+     * Returns a ClassNode representing an array of the class
+     * represented by this ClassNode
+     */
+    public ClassNode makeArray() {
+        if (redirect!=null) return redirect().makeArray();
+        ClassNode cn;
+        if (clazz!=null) {
+            Class ret = Array.newInstance(clazz,0).getClass();
+            // don't use the ClassHelper here!
+            cn = new ClassNode(ret,this);
+        } else {
+            cn = new ClassNode(this);
+        }
+        return cn;
+    }
+    
+    /**
+     * Returns if this instance is a primary ClassNode
+     */
+    public boolean isPrimaryClassNode(){
+    	return redirect().isPrimaryNode || (componentType!= null && componentType.isPrimaryClassNode());
+    }
+    
+    /**
+     * Constructor used by makeArray() if no real class is available
+     */
+    private ClassNode(ClassNode componentType) {
+        this(componentType.getName()+"[]", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        this.componentType = componentType.redirect();
+        isPrimaryNode=false;
+    }
+    
+    /**
+     * Constructor used by makeArray() if a real class is available
+     */
+    private ClassNode(Class c, ClassNode componentType) {
+        this(c);
+        this.componentType = componentType;
+        isPrimaryNode=false;
+    }
+    
+    /**
+     * Creates a ClassNode from a real class. The resulting 
+     * ClassNode will be no primary ClassNode.
+     */
+    public ClassNode(Class c) {
+        this(c.getName(), c.getModifiers(), null, null ,MixinNode.EMPTY_ARRAY);
+        clazz=c;
+        lazyInitDone=false;
+        CompileUnit cu = getCompileUnit();
+        if (cu!=null) cu.addClass(this);
+        isPrimaryNode=false;
+    }    
+    
+    /**
+     * The complete class structure will be initialized only when really
+     * needed to avoid having too much objects during compilation
+     */
+    private void lazyClassInit() {       
+        synchronized (lazyInitLock) {
+            if (lazyInitDone) return;
+            
+            Field[] fields = clazz.getDeclaredFields();
+            for (int i=0;i<fields.length;i++){
+                addField(fields[i].getName(),fields[i].getModifiers(),this,null);
+            }
+            Method[] methods = clazz.getDeclaredMethods();
+            for (int i=0;i<methods.length;i++){
+                Method m = methods[i];
+                MethodNode mn = new MethodNode(m.getName(), m.getModifiers(), ClassHelper.make(m.getReturnType()), createParameters(m.getParameterTypes()), ClassHelper.make(m.getExceptionTypes()), null);
+                addMethod(mn);
+            }
+            Constructor[] constructors = clazz.getDeclaredConstructors();
+            for (int i=0;i<constructors.length;i++){
+                Constructor ctor = constructors[i];
+                addConstructor(ctor.getModifiers(),createParameters(ctor.getParameterTypes()),ClassHelper.make(ctor.getExceptionTypes()),null);
+            }
+            Class sc = clazz.getSuperclass();
+            if (sc!=null) superClass = ClassHelper.make(sc);
+            buildInterfaceTypes(clazz);       
+            lazyInitDone=true;
+        }
+    }
+    
+    private void buildInterfaceTypes(Class c) {
+        Class[] interfaces = c.getInterfaces();
+        ClassNode[] ret = new ClassNode[interfaces.length];
+        for (int i=0;i<interfaces.length;i++){
+            ret[i] = ClassHelper.make(interfaces[i]);
+        }
+        this.interfaces = ret;
+    }
+    
+    
+    // added to track the enclosing method for local inner classes
+    private MethodNode enclosingMethod = null;
+
+    public MethodNode getEnclosingMethod() {
+        return redirect().enclosingMethod;
+    }
+
+    public void setEnclosingMethod(MethodNode enclosingMethod) {
+        redirect().enclosingMethod = enclosingMethod;
+    }
+
+
+    /**
+     * @param name       is the full name of the class
+     * @param modifiers  the modifiers,
+     * @param superClass the base class name - use "java.lang.Object" if no direct
+     *                   base class
+     * @see org.objectweb.asm.Opcodes
+     */
+    public ClassNode(String name, int modifiers, ClassNode superClass) {
+        this(name, modifiers, superClass, EMPTY_ARRAY, MixinNode.EMPTY_ARRAY);
+    }
+
+    /**
+     * @param name       is the full name of the class
+     * @param modifiers  the modifiers,
+     * @param superClass the base class name - use "java.lang.Object" if no direct
+     *                   base class
+     * @see org.objectweb.asm.Opcodes
+     */
+    public ClassNode(String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) {
+        this.name = name;
+        this.modifiers = modifiers;
+        this.superClass = superClass;
+        this.interfaces = interfaces;
+        this.mixins = mixins;
+        isPrimaryNode = true;
+        if (superClass!=null) {
+            usesGenerics = superClass.isUsingGenerics();
+        }
+        if (!usesGenerics && interfaces!=null) {
+            for (int i = 0; i < interfaces.length; i++) {
+                usesGenerics = usesGenerics || interfaces[i].isUsingGenerics();
+            }
+        }
+        this.methods = new MapOfLists();
+        this.methodsList = new ArrayList();
+    }
+
+    
+    /**
+     * Sets the superclass of this ClassNode
+     */
+    public void setSuperClass(ClassNode superClass) {
+        redirect().superClass = superClass;
+    }
+
+    /**
+     * Returns a list containing FieldNode objects for
+     * each field in the class represented by this ClassNode
+     */
+    public List getFields() {
+        if (!lazyInitDone) {
+            lazyClassInit();
+        }
+        if (redirect!=null) return redirect().getFields();
+        return fields;
+    }
+
+    /**
+     * Returns an array of ClassNodes representing the
+     * interfaces the class implements
+     */
+    public ClassNode[] getInterfaces() {
+        if (!lazyInitDone) {
+            lazyClassInit();
+        }
+        if (redirect!=null) return redirect().getInterfaces();
+        return interfaces;
+    }
+
+    public MixinNode[] getMixins() {
+        return redirect().mixins;
+    }
+
+    /**
+     * Returns a list containing MethodNode objects for
+     * each method in the class represented by this ClassNode
+     */    
+    public List getMethods() {
+        if (!lazyInitDone) lazyClassInit();
+        if (redirect!=null) return redirect().getMethods();
+        return methodsList;
+    }
+
+    /**
+     * Returns a list containing MethodNode objects for
+     * each abstract method in the class represented by 
+     * this ClassNode
+     */   
+    public List getAbstractMethods() {
+        
+        HashSet abstractNodes = new HashSet();
+        // let us collect the abstract super classes and stop at the
+        // first non abstract super class. If such a class still 
+        // contains abstract methods, then loading that class will fail.
+        // No need to be extra careful here for that.
+        ClassNode parent = this.redirect();
+        do {
+            abstractNodes.add(parent);
+            ClassNode[] interfaces = parent.getInterfaces();
+            for (int i = 0; i < interfaces.length; i++) {
+                abstractNodes.add(interfaces[i].redirect());
+            }
+            parent = parent.getSuperClass().redirect();
+        } while (parent!=null && ((parent.getModifiers() & Opcodes.ACC_ABSTRACT) != 0));
+        
+        List result = new ArrayList();
+        for (Iterator methIt = getAllDeclaredMethods().iterator(); methIt.hasNext();) {
+            MethodNode method = (MethodNode) methIt.next();
+            // add only abstract methods from abtract classes that
+            // are not overwritten
+            if ( abstractNodes.contains(method.getDeclaringClass().redirect()) && 
+                 (method.getModifiers() & Opcodes.ACC_ABSTRACT) != 0
+               ) {
+                result.add(method);
+            }
+        }
+        if (result.isEmpty()) {
+            return null;
+        }
+        else {
+            return result;
+        }
+    }
+
+    public List getAllDeclaredMethods() {
+        return new ArrayList(getDeclaredMethodsMap().values());
+    }
+
+
+    protected Map getDeclaredMethodsMap() {
+        // Start off with the methods from the superclass.
+        ClassNode parent = getSuperClass();
+        Map result = null;
+        if (parent != null) {
+            result = parent.getDeclaredMethodsMap();
+        }
+        else {
+            result = new HashMap();
+        }
+
+        // add in unimplemented abstract methods from the interfaces
+        ClassNode[] interfaces = getInterfaces();
+        for (int i = 0; i < interfaces.length; i++) {
+            ClassNode iface = interfaces[i];
+            Map ifaceMethodsMap = iface.getDeclaredMethodsMap();
+            for (Iterator iter = ifaceMethodsMap.keySet().iterator(); iter.hasNext();) {
+                String methSig = (String) iter.next();
+                if (!result.containsKey(methSig)) {
+                    MethodNode methNode = (MethodNode) ifaceMethodsMap.get(methSig);
+                    result.put(methSig, methNode);
+                }
+            }
+        }
+
+        // And add in the methods implemented in this class.
+        for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
+            MethodNode method = (MethodNode) iter.next();
+            String sig = method.getTypeDescriptor();
+            result.put(sig, method);
+        }
+        return result;
+    }
+
+    public String getName() {
+        return redirect().name;
+    }
+    
+    public String setName(String name) {
+        return redirect().name=name;
+    }
+
+    public int getModifiers() {
+        return redirect().modifiers;
+    }
+
+    public List getProperties() {
+        return redirect().properties;
+    }
+
+    public List getDeclaredConstructors() {
+        if (!lazyInitDone) {
+            lazyClassInit();
+        }
+        return redirect().constructors;
+    }
+
+    public ModuleNode getModule() {
+        return redirect().module;
+    }
+
+    public void setModule(ModuleNode module) {
+        redirect().module = module;
+        if (module != null) {
+            redirect().compileUnit = module.getUnit();
+        }
+    }
+
+    public void addField(FieldNode node) {
+        node.setDeclaringClass(redirect());
+        node.setOwner(redirect());
+        redirect().fields.add(node);
+        redirect().fieldIndex.put(node.getName(), node);
+    }
+
+    public void addProperty(PropertyNode node) {
+        node.setDeclaringClass(redirect());
+        FieldNode field = node.getField();
+        addField(field);
+
+        redirect().properties.add(node);
+    }
+
+    public PropertyNode addProperty(String name,
+                                    int modifiers,
+                                    ClassNode type,
+                                    Expression initialValueExpression,
+                                    Statement getterBlock,
+                                    Statement setterBlock) {
+    	for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
+            PropertyNode pn = (PropertyNode) iter.next();
+            if (pn.getName().equals(name)) return pn;
+        }
+        PropertyNode node =
+                new PropertyNode(name, modifiers, type, redirect(), initialValueExpression, getterBlock, setterBlock);
+        addProperty(node);
+        return node;
+    }
+
+    public void addConstructor(ConstructorNode node) {
+        node.setDeclaringClass(this);
+        redirect().constructors.add(node);
+    }
+
+    public ConstructorNode addConstructor(int modifiers, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
+        ConstructorNode node = new ConstructorNode(modifiers, parameters, exceptions, code);
+        addConstructor(node);
+        return node;
+    }
+
+    public void addMethod(MethodNode node) {
+        node.setDeclaringClass(this);
+        redirect().methodsList.add(node);
+        redirect().methods.put(node.getName(), node);
+    }
+
+    /**
+     * IF a method with the given name and parameters is already defined then it is returned
+     * otherwise the given method is added to this node. This method is useful for
+     * default method adding like getProperty() or invokeMethod() where there may already
+     * be a method defined in a class and  so the default implementations should not be added
+     * if already present.
+     */
+    public MethodNode addMethod(String name,
+                                int modifiers,
+                                ClassNode returnType,
+                                Parameter[] parameters,
+                                ClassNode[] exceptions,
+                                Statement code) {
+        MethodNode other = getDeclaredMethod(name, parameters);
+        // lets not add duplicate methods
+        if (other != null) {
+            return other;
+        }
+        MethodNode node = new MethodNode(name, modifiers, returnType, parameters, exceptions, code);
+        addMethod(node);
+        return node;
+    }
+
+    /**
+     * Adds a synthetic method as part of the compilation process
+     */
+    public MethodNode addSyntheticMethod(String name,
+                                         int modifiers,
+                                         ClassNode returnType,
+                                         Parameter[] parameters,
+                                         ClassNode[] exceptions,
+                                         Statement code) {
+        MethodNode answer = addMethod(name, modifiers, returnType, parameters, exceptions, code);
+        answer.setSynthetic(true);
+        return answer;
+    }
+
+    public FieldNode addField(String name, int modifiers, ClassNode type, Expression initialValue) {
+        FieldNode node = new FieldNode(name, modifiers, type, redirect(), initialValue);
+        addField(node);
+        return node;
+    }
+
+    public void addInterface(ClassNode type) {
+        // lets check if it already implements an interface
+        boolean skip = false;
+        ClassNode[] interfaces = redirect().interfaces;
+        for (int i = 0; i < interfaces.length; i++) {
+            if (type.equals(interfaces[i])) {
+                skip = true;
+            }
+        }
+        if (!skip) {
+            ClassNode[] newInterfaces = new ClassNode[interfaces.length + 1];
+            System.arraycopy(interfaces, 0, newInterfaces, 0, interfaces.length);
+            newInterfaces[interfaces.length] = type;
+            redirect().interfaces = newInterfaces;
+        }
+    }
+    
+    public boolean equals(Object o) {
+        if (redirect!=null) return redirect().equals(o);
+        ClassNode cn = (ClassNode) o;        
+        return (cn.getName().equals(getName()));
+    }
+
+    public void addMixin(MixinNode mixin) {
+        // lets check if it already uses a mixin
+        MixinNode[] mixins = redirect().mixins;
+        boolean skip = false;
+        for (int i = 0; i < mixins.length; i++) {
+            if (mixin.equals(mixins[i])) {
+                skip = true;
+            }
+        }
+        if (!skip) {
+            MixinNode[] newMixins = new MixinNode[mixins.length + 1];
+            System.arraycopy(mixins, 0, newMixins, 0, mixins.length);
+            newMixins[mixins.length] = mixin;
+            redirect().mixins = newMixins;
+        }
+    }
+
+    public FieldNode getField(String name) {
+        return (FieldNode) redirect().fieldIndex.get(name);
+    }
+
+    /**
+     * @return the field node on the outer class or null if this is not an
+     *         inner class
+     */
+    public FieldNode getOuterField(String name) {
+        return null;
+    }
+
+    /**
+     * Helper method to avoid casting to inner class
+     */
+    public ClassNode getOuterClass() {
+        return null;
+    }
+    
+    public void addObjectInitializerStatements(Statement statements) {
+        objectInitializers.add(statements);
+    }
+    
+    public List getObjectInitializerStatements() {
+        return objectInitializers;
+    }
+
+    public void addStaticInitializerStatements(List staticStatements, boolean fieldInit) {
+        MethodNode method = null;
+        List declaredMethods = getDeclaredMethods("<clinit>");
+        if (declaredMethods.isEmpty()) {
+            method =
+                    addMethod("<clinit>", ACC_STATIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement());
+            method.setSynthetic(true);
+        }
+        else {
+            method = (MethodNode) declaredMethods.get(0);
+        }
+        BlockStatement block = null;
+        Statement statement = method.getCode();
+        if (statement == null) {
+            block = new BlockStatement();
+        }
+        else if (statement instanceof BlockStatement) {
+            block = (BlockStatement) statement;
+        }
+        else {
+            block = new BlockStatement();
+            block.addStatement(statement);
+        }
+        
+        // while anything inside a static initializer block is appended 
+        // we don't want to append in the case we have a initialization
+        // expression of a static field. In that case we want to add
+        // before the other statements
+        if (!fieldInit) {
+            block.addStatements(staticStatements);
+        } else {
+            List blockStatements = block.getStatements();
+            staticStatements.addAll(blockStatements);
+            blockStatements.clear();
+            blockStatements.addAll(staticStatements);
+        }
+    }
+
+    /**
+     * This methods returns a list of all methods of the given name
+     * defined in the current class
+     * @return the method list
+     * @see #getMethods(String)
+     */
+    public List getDeclaredMethods(String name) {
+        if (!lazyInitDone) lazyClassInit();
+        if (redirect!=null) return redirect().getDeclaredMethods(name);
+        return methods.getNotNull(name);
+    }
+
+    /**
+     * This methods creates a list of all methods with this name of the
+     * current class and of all super classes
+     * @return the methods list
+     * @see #getDeclaredMethods(String)
+     */
+    public List getMethods(String name) {
+        List answer = new ArrayList(getDeclaredMethods(name));
+        ClassNode parent = getSuperClass();
+        if (parent!=null) answer.addAll(parent.getMethods(name));
+        return answer;
+    }
+
+    /**
+     * @return the method matching the given name and parameters or null
+     */
+    public MethodNode getDeclaredMethod(String name, Parameter[] parameters) {
+        List list = getDeclaredMethods(name);
+        for (Iterator iter = list.iterator(); iter.hasNext();) {
+            MethodNode method = (MethodNode) iter.next();
+            if (parametersEqual(method.getParameters(), parameters)) {
+                return method;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * @return true if this node is derived from the given class node
+     */
+    public boolean isDerivedFrom(ClassNode type) {
+        if (type.equals(ClassHelper.OBJECT_TYPE)) return true;
+        ClassNode node = this;
+        while (node != null) {
+            if (type.equals(node)) {
+                return true;
+            }
+            node = node.getSuperClass();
+        }
+        return false;
+    }
+
+    /**
+     * @return true if this class is derived from a groovy object
+     *         i.e. it implements GroovyObject
+     */
+    public boolean isDerivedFromGroovyObject() {
+        return implementsInterface(GroovyObject.class.getName());
+    }
+
+    /**
+     * @param name the fully qualified name of the interface
+     * @return true if this class or any base class implements the given interface
+     */
+    public boolean implementsInterface(String name) {
+        ClassNode node = redirect();
+        do {
+            if (node.declaresInterface(name)) {
+                return true;
+            }
+            node = node.getSuperClass();
+        }
+        while (node != null);
+        return false;
+    }
+
+    /**
+     * @param name the fully qualified name of the interface
+     * @return true if this class declares that it implements the given interface
+     */
+    public boolean declaresInterface(String name) {
+        ClassNode[] interfaces = redirect().getInterfaces();
+        int size = interfaces.length;
+        for (int i = 0; i < size; i++) {
+            if (interfaces[i].getName().equals(name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return the ClassNode of the super class of this type
+     */
+    public ClassNode getSuperClass() {
+        if (!lazyInitDone && !isResolved()) {
+            throw new GroovyBugError("Classnode#getSuperClass for "+getName()+" called before class resolving");
+        }
+        ClassNode sn = redirect().getUnresolvedSuperClass();
+        if (sn!=null) sn=sn.redirect();
+        return sn;
+    }
+    
+    public ClassNode getUnresolvedSuperClass() {
+        return getUnresolvedSuperClass(true);
+    }
+
+    public ClassNode getUnresolvedSuperClass(boolean useRedirect) {
+        if (!useRedirect) return superClass;
+        if (!lazyInitDone) {
+            lazyClassInit();
+        }
+        return redirect().superClass;
+    }
+    
+    /**
+     * Factory method to create a new MethodNode via reflection
+     */
+    protected MethodNode createMethodNode(Method method) {
+        Parameter[] parameters = createParameters(method.getParameterTypes());
+        return new MethodNode(method.getName(), method.getModifiers(), ClassHelper.make(method.getReturnType()), parameters, ClassHelper.make(method.getExceptionTypes()), EmptyStatement.INSTANCE);
+    }
+
+    /**
+     * @param types
+     */
+    protected Parameter[] createParameters(Class[] types) {
+        Parameter[] parameters = Parameter.EMPTY_ARRAY;
+        int size = types.length;
+        if (size > 0) {
+            parameters = new Parameter[size];
+            for (int i = 0; i < size; i++) {
+                parameters[i] = createParameter(types[i], i);
+            }
+        }
+        return parameters;
+    }
+
+    protected Parameter createParameter(Class parameterType, int idx) {
+        return new Parameter(ClassHelper.make(parameterType), "param" + idx);
+    }
+
+    public CompileUnit getCompileUnit() {
+        if (redirect!=null) return redirect().getCompileUnit();
+        if (compileUnit == null && module != null) {
+            compileUnit = module.getUnit();
+        }
+        return compileUnit;
+    }
+    
+    protected void setCompileUnit(CompileUnit cu) {
+        if (redirect!=null) redirect().setCompileUnit(cu);
+        if (compileUnit!= null) compileUnit = cu;
+    }
+
+    /**
+     * @return true if the two arrays are of the same size and have the same contents
+     */
+    protected boolean parametersEqual(Parameter[] a, Parameter[] b) {
+        if (a.length == b.length) {
+            boolean answer = true;
+            for (int i = 0; i < a.length; i++) {
+                if (!a[i].getType().equals(b[i].getType())) {
+                    answer = false;
+                    break;
+                }
+            }
+            return answer;
+        }
+        return false;
+    }
+
+    /**
+     * @return the package name of this class
+     */
+    public String getPackageName() {
+        int idx = getName().lastIndexOf('.');
+        if (idx > 0) {
+            return getName().substring(0, idx);
+        }
+        return null;
+    }
+
+    public String getNameWithoutPackage() {
+        int idx = getName().lastIndexOf('.');
+        if (idx > 0) {
+            return getName().substring(idx + 1);
+        }
+        return getName();
+    }
+
+    public void visitContents(GroovyClassVisitor visitor) {
+        
+        // now lets visit the contents of the class
+        for (Iterator iter = getProperties().iterator(); iter.hasNext();) {
+            PropertyNode pn = (PropertyNode) iter.next();
+            visitor.visitProperty(pn);
+        }
+
+        for (Iterator iter = getFields().iterator(); iter.hasNext();) {
+            FieldNode fn = (FieldNode) iter.next();
+            visitor.visitField(fn);
+        }
+
+        for (Iterator iter = getDeclaredConstructors().iterator(); iter.hasNext();) {
+            ConstructorNode cn = (ConstructorNode) iter.next();
+            visitor.visitConstructor(cn);
+        }
+
+        for (Iterator iter = getMethods().iterator(); iter.hasNext();) {
+            MethodNode mn = (MethodNode) iter.next();
+            visitor.visitMethod(mn);
+        }
+    }
+
+    public MethodNode getGetterMethod(String getterName) {
+        for (Iterator iter = getDeclaredMethods(getterName).iterator(); iter.hasNext();) {
+            MethodNode method = (MethodNode) iter.next();
+            if (getterName.equals(method.getName())
+                    && ClassHelper.VOID_TYPE!=method.getReturnType()
+                    && method.getParameters().length == 0) {
+                return method;
+            }
+        }
+        ClassNode parent = getSuperClass(); 
+        if (parent!=null) return parent.getGetterMethod(getterName); 
+        return null;
+    }
+
+    public MethodNode getSetterMethod(String setterName) {
+        for (Iterator iter = getDeclaredMethods(setterName).iterator(); iter.hasNext();) {
+            MethodNode method = (MethodNode) iter.next();
+            if (setterName.equals(method.getName())
+                    && ClassHelper.VOID_TYPE==method.getReturnType()
+                    && method.getParameters().length == 1) {
+                return method;
+            }
+        }
+        ClassNode parent = getSuperClass(); 
+        if (parent!=null) return parent.getSetterMethod(setterName); 
+        return null;
+    }
+
+    /**
+     * Is this class delcared in a static method (such as a closure / inner class declared in a static method)
+     */
+    public boolean isStaticClass() {
+        return redirect().staticClass;
+    }
+
+    public void setStaticClass(boolean staticClass) {
+        redirect().staticClass = staticClass;
+    }
+
+    /**
+     * @return Returns true if this inner class or closure was declared inside a script body
+     */
+    public boolean isScriptBody() {
+        return redirect().scriptBody;
+    }
+
+    public void setScriptBody(boolean scriptBody) {
+        redirect().scriptBody = scriptBody;
+    }
+
+    public boolean isScript() {
+        return redirect().script || isDerivedFrom(ClassHelper.SCRIPT_TYPE);
+    }
+
+    public void setScript(boolean script) {
+        redirect().script = script;
+    }
+
+    public String toString() {
+        String ret = getName(); 
+        if (genericsTypes!=null) {
+            ret += " <";
+            for (int i = 0; i < genericsTypes.length; i++) {
+                if (i!=0) ret+=", ";
+                ret += genericsTypes[i];
+            }
+            ret += ">";
+        }
+        if (redirect!=null) {
+            ret += " -> "+redirect().toString();
+        }
+        return ret;
+    }
+
+    /**
+     * Returns true if the given method has a possibly matching method with the given name and arguments
+     */
+    public boolean hasPossibleMethod(String name, Expression arguments) {
+        int count = 0;
+
+        if (arguments instanceof TupleExpression) {
+            TupleExpression tuple = (TupleExpression) arguments;
+            // TODO this won't strictly be true when using list expansion in argument calls
+            count = tuple.getExpressions().size();
+        }
+        ClassNode node = this;
+        do {
+            for (Iterator iter = getDeclaredMethods(name).iterator(); iter.hasNext();) {
+                MethodNode method = (MethodNode) iter.next();
+                if (method.getParameters().length == count) {
+                    return true;
+                }
+            }
+            node = node.getSuperClass();
+        }
+        while (node != null);
+        return false;
+    }
+    
+    /**
+     * Returns true if the given method has a possibly matching static method with the given name and arguments
+     */
+    public boolean hasPossibleStaticMethod(String name, Expression arguments) {
+        int count = 0;
+
+        if (arguments instanceof TupleExpression) {
+            TupleExpression tuple = (TupleExpression) arguments;
+            // TODO this won't strictly be true when using list expansion in argument calls
+            count = tuple.getExpressions().size();
+        }
+        for (Iterator iter = getDeclaredMethods(name).iterator(); iter.hasNext();) {
+            MethodNode method = (MethodNode) iter.next();
+            if (method.getParameters().length == count && method.isStatic()) {
+                return true;
+            }
+            // handle varargs case
+            if (method.isStatic() && method.getParameters().length > 0 &&
+                method.getParameters()[method.getParameters().length - 1].getType().isArray()) {
+                if (count >= method.getParameters().length - 1) return true;
+            }
+        }
+        return false;
+    }
+
+    public boolean isInterface(){
+        return (getModifiers() & Opcodes.ACC_INTERFACE) > 0; 
+    }
+    
+    public boolean isResolved(){
+        return redirect().clazz!=null || (componentType != null && componentType.isResolved());
+    }
+    
+    public boolean isArray(){
+        return componentType!=null;
+    }
+    
+    public ClassNode getComponentType() {
+        return componentType;
+    }
+    
+    public Class getTypeClass(){
+        Class c = redirect().clazz;
+        if (c!=null) return c;
+        ClassNode component = redirect().componentType;
+        if (component!=null && component.isResolved()){
+            ClassNode cn = component.makeArray();
+            setRedirect(cn);
+            return redirect().clazz;
+        }
+        throw new GroovyBugError("ClassNode#getTypeClass for "+getName()+" is called before the type class is set ");
+    }
+    
+    public boolean hasPackageName(){
+        return redirect().name.indexOf('.')>0;
+    }
+
+    /**
+     * Marks if the current class uses annotations or not
+     * @param flag
+     */
+    public void setAnnotated(boolean flag) {
+        this.annotated = flag;
+    }
+    
+    public boolean isAnnotated() {
+        return this.annotated;
+    }
+
+    public GenericsType[] getGenericsTypes() {
+        return genericsTypes;
+    }
+
+    public void setGenericsTypes(GenericsType[] genericsTypes) {
+        usesGenerics = usesGenerics || genericsTypes!=null;
+        this.genericsTypes = genericsTypes;
+    }
+
+    public void setGenericsPlaceHolder(boolean b) {
+        usesGenerics = usesGenerics || b;
+        placeholder = b;
+    }
+    
+    public boolean isGenericsPlaceHolder() {
+        return placeholder;
+    }
+    
+    public boolean isUsingGenerics() {
+        return usesGenerics;
+    }
+    
+    public void setUsingGenerics(boolean b) {
+        usesGenerics = b;
+    }
+    
+    public ClassNode getPlainNodeReference() {
+        ClassNode n = new ClassNode(getName(),getModifiers(),getSuperClass(),null,null);
+        n.isPrimaryNode = false;
+        n.setRedirect(this.redirect);
+        return n;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/CodeVisitorSupport.java b/groovy/src/main/org/codehaus/groovy/ast/CodeVisitorSupport.java
new file mode 100644
index 0000000..b457013
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/CodeVisitorSupport.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.BreakStatement;
+import org.codehaus.groovy.ast.stmt.CaseStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.ContinueStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.SwitchStatement;
+import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
+import org.codehaus.groovy.ast.stmt.ThrowStatement;
+import org.codehaus.groovy.ast.stmt.TryCatchStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+
+/**
+ * Abstract base class for any GroovyCodeVisitory which by default
+ * just walks the code and expression tree
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class CodeVisitorSupport implements GroovyCodeVisitor {
+
+    public void visitBlockStatement(BlockStatement block) {
+        List statements = block.getStatements();
+        for (Iterator iter = statements.iterator(); iter.hasNext(); ) {
+            Statement statement = (Statement) iter.next();
+            statement.visit(this);
+        }
+    }
+
+    public void visitForLoop(ForStatement forLoop) {
+        forLoop.getCollectionExpression().visit(this);
+        forLoop.getLoopBlock().visit(this);
+    }
+
+    public void visitWhileLoop(WhileStatement loop) {
+        loop.getBooleanExpression().visit(this);
+        loop.getLoopBlock().visit(this);
+    }
+
+    public void visitDoWhileLoop(DoWhileStatement loop) {
+        loop.getLoopBlock().visit(this);
+        loop.getBooleanExpression().visit(this);
+    }
+
+    public void visitIfElse(IfStatement ifElse) {
+        ifElse.getBooleanExpression().visit(this);
+        ifElse.getIfBlock().visit(this);
+        ifElse.getElseBlock().visit(this);
+    }
+
+    public void visitExpressionStatement(ExpressionStatement statement) {
+        statement.getExpression().visit(this);
+    }
+
+    public void visitReturnStatement(ReturnStatement statement) {
+        statement.getExpression().visit(this);
+    }
+
+    public void visitAssertStatement(AssertStatement statement) {
+        statement.getBooleanExpression().visit(this);
+        statement.getMessageExpression().visit(this);
+    }
+
+    public void visitTryCatchFinally(TryCatchStatement statement) {
+        statement.getTryStatement().visit(this);
+        List list = statement.getCatchStatements();
+        for (Iterator iter = list.iterator(); iter.hasNext(); ) {
+            CatchStatement catchStatement = (CatchStatement) iter.next();
+            catchStatement.visit(this);
+        }
+        statement.getFinallyStatement().visit(this);
+    }
+
+    public void visitSwitch(SwitchStatement statement) {
+        statement.getExpression().visit(this);
+        List list = statement.getCaseStatements();
+        for (Iterator iter = list.iterator(); iter.hasNext(); ) {
+            CaseStatement caseStatement = (CaseStatement) iter.next();
+            caseStatement.visit(this);
+        }
+        statement.getDefaultStatement().visit(this);
+    }
+
+    public void visitCaseStatement(CaseStatement statement) {
+        statement.getExpression().visit(this);
+        statement.getCode().visit(this);
+    }
+
+    public void visitBreakStatement(BreakStatement statement) {
+    }
+
+    public void visitContinueStatement(ContinueStatement statement) {
+    }
+
+    public void visitSynchronizedStatement(SynchronizedStatement statement) {
+        statement.getExpression().visit(this);
+        statement.getCode().visit(this);
+    }
+
+    public void visitThrowStatement(ThrowStatement statement) {
+        statement.getExpression().visit(this);
+    }
+
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        call.getObjectExpression().visit(this);
+        call.getMethod().visit(this);
+        call.getArguments().visit(this);
+    }
+
+    public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
+        call.getArguments().visit(this);
+    }
+
+    public void visitConstructorCallExpression(ConstructorCallExpression call) {
+        call.getArguments().visit(this);
+    }
+
+    public void visitBinaryExpression(BinaryExpression expression) {
+        expression.getLeftExpression().visit(this);
+        expression.getRightExpression().visit(this);
+    }
+
+    public void visitTernaryExpression(TernaryExpression expression) {
+        expression.getBooleanExpression().visit(this);
+        expression.getTrueExpression().visit(this);
+        expression.getFalseExpression().visit(this);
+    }
+    
+    public void visitShortTernaryExpression(ElvisOperatorExpression expression) {
+        visitTernaryExpression(expression);
+    }
+
+    public void visitPostfixExpression(PostfixExpression expression) {
+        expression.getExpression().visit(this);
+    }
+
+    public void visitPrefixExpression(PrefixExpression expression) {
+        expression.getExpression().visit(this);
+    }
+
+    public void visitBooleanExpression(BooleanExpression expression) {
+		expression.getExpression().visit(this);
+	}
+
+	public void visitNotExpression(NotExpression expression) {
+		expression.getExpression().visit(this);
+	}
+
+    public void visitClosureExpression(ClosureExpression expression) {
+        expression.getCode().visit(this);
+    }
+    
+    public void visitTupleExpression(TupleExpression expression) {
+        visitListOfExpressions(expression.getExpressions());
+    }
+
+    public void visitListExpression(ListExpression expression) {
+        visitListOfExpressions(expression.getExpressions());
+    }
+
+    public void visitArrayExpression(ArrayExpression expression) {
+        visitListOfExpressions(expression.getExpressions());
+        visitListOfExpressions(expression.getSizeExpression());
+    }
+    
+    public void visitMapExpression(MapExpression expression) {
+        visitListOfExpressions(expression.getMapEntryExpressions());
+        
+    }
+
+    public void visitMapEntryExpression(MapEntryExpression expression) {
+        expression.getKeyExpression().visit(this);
+        expression.getValueExpression().visit(this);
+        
+    }
+
+    public void visitRangeExpression(RangeExpression expression) {
+        expression.getFrom().visit(this);
+        expression.getTo().visit(this);
+    }
+
+    public void visitSpreadExpression(SpreadExpression expression) {
+        expression.getExpression().visit(this);
+    }
+ 
+    public void visitSpreadMapExpression(SpreadMapExpression expression) {
+        expression.getExpression().visit(this);
+    }
+
+    public void visitMethodPointerExpression(MethodPointerExpression expression) {
+        expression.getExpression().visit(this);
+        expression.getMethodName().visit(this);
+    }
+
+    public void visitUnaryMinusExpression(UnaryMinusExpression expression) {
+        expression.getExpression().visit(this);
+    }
+    
+    public void visitUnaryPlusExpression(UnaryPlusExpression expression) {
+        expression.getExpression().visit(this);
+    }
+
+    public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) {
+        expression.getExpression().visit(this);
+    }
+    
+    public void visitCastExpression(CastExpression expression) {
+        expression.getExpression().visit(this);
+    }
+
+    public void visitConstantExpression(ConstantExpression expression) {
+    }
+
+    public void visitClassExpression(ClassExpression expression) {
+    }
+
+    public void visitVariableExpression(VariableExpression expression) {
+    }
+
+    public void visitDeclarationExpression(DeclarationExpression expression) {
+        visitBinaryExpression(expression);
+    }
+    
+    public void visitPropertyExpression(PropertyExpression expression) {
+    	expression.getObjectExpression().visit(this);
+    	expression.getProperty().visit(this);
+    }
+
+    public void visitAttributeExpression(AttributeExpression expression) {
+    	expression.getObjectExpression().visit(this);
+    	expression.getProperty().visit(this);
+    }
+
+    public void visitFieldExpression(FieldExpression expression) {
+    }
+
+    public void visitRegexExpression(RegexExpression expression) {
+    }
+
+    public void visitGStringExpression(GStringExpression expression) {
+        visitListOfExpressions(expression.getStrings());
+        visitListOfExpressions(expression.getValues());
+    }
+
+    protected void visitListOfExpressions(List list) {
+        if (list==null) return;
+        for (Iterator iter = list.iterator(); iter.hasNext(); ) {
+            Expression expression = (Expression) iter.next();
+            if (expression instanceof SpreadExpression) {
+                Expression spread = ((SpreadExpression) expression).getExpression();
+                spread.visit(this);
+            } else {
+                expression.visit(this);
+            }
+        }
+    }
+    
+    public void visitCatchStatement(CatchStatement statement) {
+    	statement.getCode().visit(this);
+    }
+    
+    public void visitArgumentlistExpression(ArgumentListExpression ale) {
+    	visitTupleExpression(ale);
+    }
+    
+    public void visitClosureListExpression(ClosureListExpression cle) {
+        visitListOfExpressions(cle.getExpressions());
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/CompileUnit.java b/groovy/src/main/org/codehaus/groovy/ast/CompileUnit.java
new file mode 100644
index 0000000..674397a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/CompileUnit.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import groovy.lang.GroovyClassLoader;
+
+import java.security.CodeSource;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+/**
+ * Represents the entire contents of a compilation step which consists of one
+ * or more {@link ModuleNode}instances
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan </a>
+ * @version $Revision$
+ */
+public class CompileUnit {
+
+    private final List modules = new ArrayList();
+    private Map classes = new HashMap();
+    private CompilerConfiguration config;
+    private GroovyClassLoader classLoader;
+    private CodeSource codeSource;
+    private Map classesToCompile = new HashMap();
+    private Map classNameToSource = new HashMap();
+    
+    public CompileUnit(GroovyClassLoader classLoader, CompilerConfiguration config) {
+    	this(classLoader, null, config);
+    }
+    
+    public CompileUnit(GroovyClassLoader classLoader, CodeSource codeSource, CompilerConfiguration config) {
+        this.classLoader = classLoader;
+        this.config = config;
+        this.codeSource = codeSource;
+    }
+
+    public List getModules() {
+        return modules;
+    }
+
+    public void addModule(ModuleNode node) {
+        // node==null means a compilation error prevented
+        // groovy from building an ast
+        if (node==null) return;
+        modules.add(node);
+        node.setUnit(this);
+        addClasses(node.getClasses());
+    }
+
+    /**
+     * @return the ClassNode for the given qualified name or returns null if
+     *         the name does not exist in the current compilation unit
+     *         (ignoring the .class files on the classpath)
+     */
+    public ClassNode getClass(String name) {
+        ClassNode cn = (ClassNode) classes.get(name);
+        if (cn!=null) return cn;
+        return (ClassNode) classesToCompile.get(name);
+    }
+
+    /**
+     * @return a list of all the classes in each module in the compilation unit
+     */
+    public List getClasses() {
+        List answer = new ArrayList();
+        for (Iterator iter = modules.iterator(); iter.hasNext();) {
+            ModuleNode module = (ModuleNode) iter.next();
+            answer.addAll(module.getClasses());
+        }
+        return answer;
+    }
+
+    public CompilerConfiguration getConfig() {
+        return config;
+    }
+
+    public GroovyClassLoader getClassLoader() {
+        return classLoader;
+    }
+    
+    public CodeSource getCodeSource() {
+    	return codeSource;
+    }
+
+    /**
+     * Appends all of the fully qualified class names in this
+     * module into the given map
+     */
+    void addClasses(List classList) {
+        for (Iterator iter = classList.iterator(); iter.hasNext();) {
+            addClass((ClassNode) iter.next());
+        }
+    }
+    
+    /**
+     *  Adds a class to the unit.
+     */
+    public void addClass(ClassNode node) {
+    	node = node.redirect();
+        String name = node.getName();
+        ClassNode stored = (ClassNode) classes.get(name);
+        if (stored != null && stored != node) {
+            // we have a duplicate class!
+            // One possibility for this is, that we delcared a script and a 
+            // class in the same file and named the class like the file
+            SourceUnit nodeSource = node.getModule().getContext();
+            SourceUnit storedSource = stored.getModule().getContext();
+            String txt = "Invalid duplicate class definition of class "+node.getName()+" : ";
+            if (nodeSource==storedSource) {
+                // same class in same source
+                txt += "The source "+nodeSource.getName()+" contains at last two defintions of the class "+node.getName()+".\n";
+                if (node.isScriptBody() || stored.isScriptBody()) {
+                    txt += "One of the classes is a explicit generated class using the class statement, the other is a class generated from"+
+                           " the script body based on the file name. Solutions are to change the file name or to change the class name.\n";
+                }
+            } else {
+                txt += "The sources "+nodeSource.getName()+" and "+storedSource.getName()+" are containing both a class of the name "+node.getName()+".\n";
+            }
+            nodeSource.getErrorCollector().addErrorAndContinue(
+                    new SyntaxErrorMessage(new SyntaxException(txt, node.getLineNumber(), node.getColumnNumber()), nodeSource)
+            );
+        }
+        classes.put(name, node);
+        
+        if (classesToCompile.containsKey(name)) {
+            ClassNode cn = (ClassNode) classesToCompile.get(name);
+            cn.setRedirect(node);
+            classesToCompile.remove(name);
+        }        
+    }
+     
+    /**
+     * this emthod actually does not compile a class. It's only
+     * a marker that this type has to be compiled by the CompilationUnit
+     * at the end of a parse step no node should be be left.
+     */
+    public void addClassNodeToCompile(ClassNode node, SourceUnit location) {
+        classesToCompile.put(node.getName(),node);
+        classNameToSource.put(node.getName(),location);
+    }
+    
+    public SourceUnit getScriptSourceLocation(String className) {
+        return (SourceUnit) classNameToSource.get(className);
+    }
+
+    public boolean hasClassNodeToCompile(){
+        return !classesToCompile.isEmpty();
+    }
+    
+    public Iterator iterateClassNodeToCompile(){
+        return classesToCompile.keySet().iterator();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/ConstructorNode.java b/groovy/src/main/org/codehaus/groovy/ast/ConstructorNode.java
new file mode 100644
index 0000000..0861b61
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/ConstructorNode.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+
+
+/**
+ * Represents a constructor declaration
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ConstructorNode extends MethodNode {
+    
+    public ConstructorNode(int modifiers, Statement code) {
+        this(modifiers, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, code);
+    }
+    
+    public ConstructorNode(int modifiers, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
+        super("<init>",modifiers,ClassHelper.VOID_TYPE,parameters,exceptions,code);
+        
+        VariableScope scope = new VariableScope();
+        for (int i = 0; i < parameters.length; i++) {
+            scope.putDeclaredVariable(parameters[i]);
+        }
+        this.setVariableScope(scope);
+    }
+    
+    public boolean firstStatementIsSpecialConstructorCall() {
+        Statement code = getFirstStatement();
+        if (code == null || !(code instanceof ExpressionStatement)) return false;
+
+        Expression expression = ((ExpressionStatement) code).getExpression();
+        if (!(expression instanceof ConstructorCallExpression)) return false;
+        ConstructorCallExpression cce = (ConstructorCallExpression) expression;
+        return cce.isSpecialCall();
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/DynamicVariable.java b/groovy/src/main/org/codehaus/groovy/ast/DynamicVariable.java
new file mode 100644
index 0000000..84e952e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/DynamicVariable.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import org.codehaus.groovy.ast.expr.Expression;
+
+public class DynamicVariable implements Variable {
+
+    private String name;
+    private boolean closureShare = false;
+    private boolean staticContext = false;
+    
+    public DynamicVariable(String name, boolean context) {
+        this.name = name;
+        staticContext = context;
+    }
+    
+    public ClassNode getType() {
+        return ClassHelper.DYNAMIC_TYPE;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Expression getInitialExpression() {
+        return null;
+    }
+
+    public boolean hasInitialExpression() {
+        return false;
+    }
+
+    public boolean isInStaticContext() {
+        return staticContext;
+    }
+
+    public boolean isDynamicTyped() {
+        return true;
+    }
+
+    public boolean isClosureSharedVariable() {
+        return closureShare;
+    }
+
+    public void setClosureSharedVariable(boolean inClosure) {
+        closureShare = inClosure;        
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/FieldNode.java b/groovy/src/main/org/codehaus/groovy/ast/FieldNode.java
new file mode 100644
index 0000000..ee3c727
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/FieldNode.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import java.lang.reflect.Field;
+
+import org.codehaus.groovy.ast.expr.Expression;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Represents a field (member variable)
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class FieldNode extends AnnotatedNode implements Opcodes, Variable {
+
+    private String name;
+    private int modifiers;
+    private ClassNode type;
+    private ClassNode owner;
+    private Expression initialValueExpression;
+    private boolean dynamicTyped;
+    private boolean holder;
+    private boolean closureShare = false;
+
+    public static FieldNode newStatic(Class theClass, String name) throws SecurityException, NoSuchFieldException {
+        Field field = theClass.getField(name);
+        ClassNode fldType = ClassHelper.make(field.getType());
+        return new FieldNode(name, ACC_PUBLIC | ACC_STATIC, fldType, ClassHelper.make(theClass), null);
+    }
+
+    public FieldNode(String name, int modifiers, ClassNode type, ClassNode owner, Expression initialValueExpression) {
+        this.name = name;
+        this.modifiers = modifiers;
+        this.type = type;
+        if (this.type==ClassHelper.DYNAMIC_TYPE && initialValueExpression!=null) this.setType(initialValueExpression.getType());
+        this.setType(type);
+        this.owner = owner;
+        this.initialValueExpression = initialValueExpression;
+    }
+
+    public Expression getInitialExpression() {
+        return initialValueExpression;
+    }
+
+    public int getModifiers() {
+        return modifiers;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public ClassNode getType() {
+        return type;
+    }
+
+    public void setType(ClassNode type) {
+        this.type = type;
+        dynamicTyped |= type==ClassHelper.DYNAMIC_TYPE;
+    }
+    
+    public ClassNode getOwner() {
+        return owner;
+    }
+
+    public boolean isHolder() {
+        return holder;
+    }
+
+    public void setHolder(boolean holder) {
+        this.holder = holder;
+    }
+
+    public boolean isDynamicTyped() {
+        return dynamicTyped;
+    }
+
+    public void setModifiers(int modifiers) {
+        this.modifiers = modifiers;
+    }
+
+    /**
+     * @return true if the field is static
+     */
+    public boolean isStatic() {
+        return (modifiers & ACC_STATIC) != 0;
+    }
+	/**
+	 * @param owner The owner to set.
+	 */
+	public void setOwner(ClassNode owner) {
+		this.owner = owner;
+	}
+
+    public boolean hasInitialExpression() {
+        return initialValueExpression!=null;
+    }
+
+    public boolean isInStaticContext() {
+        return isStatic();
+    }
+    public Expression getInitialValueExpression() {
+        return initialValueExpression;
+    }
+    public void setInitialValueExpression(Expression initialValueExpression) {
+        this.initialValueExpression = initialValueExpression;
+    }
+
+    public boolean isClosureSharedVariable() {
+        return false;
+    }
+    
+    public void setClosureSharedVariable(boolean inClosure) {
+        closureShare = inClosure;        
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/GenericsType.java b/groovy/src/main/org/codehaus/groovy/ast/GenericsType.java
new file mode 100644
index 0000000..8072e82
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/GenericsType.java
@@ -0,0 +1,108 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.ast;

+

+/**

+ * This class is used to describe generic type signatures

+ * for ClassNodes. 

+ * @author Jochen Theodorou

+ * @see ClassNode

+ */

+public class GenericsType extends ASTNode {

+    private final ClassNode[] upperBounds;

+    private final ClassNode lowerBound;

+    private ClassNode type;

+    private String name;

+    private boolean placeholder;

+    private boolean resolved;

+    private boolean wildcard;

+    

+    public GenericsType(ClassNode type, ClassNode[] upperBounds, ClassNode lowerBound) {

+        this.type = type;

+        this.name = type.getName();

+        this.upperBounds = upperBounds;

+        this.lowerBound = lowerBound;

+        placeholder = false;

+        resolved = false;

+    }

+    

+    public GenericsType(ClassNode basicType) {

+        this(basicType,null,null);

+    }

+

+    public ClassNode getType() {

+        return type;

+    }

+    

+    public void setType(ClassNode type) {

+        this.type = type;

+    }

+    

+    public String toString() {

+        String ret = name;

+        if (upperBounds!=null) {

+            ret += " extends ";

+            for (int i = 0; i < upperBounds.length; i++) {

+                ret += upperBounds[i].toString();

+                if (i+1<upperBounds.length) ret += " & ";

+            }

+        } else if (lowerBound!=null) {

+            ret += " super "+lowerBound;

+        }

+        return ret;

+    }

+

+    public ClassNode[] getUpperBounds() {

+        return upperBounds;

+    }

+    

+    public String getName(){

+        return name;

+    }

+

+    public boolean isPlaceholder() {

+        return placeholder;

+    }

+

+    public void setPlaceholder(boolean placeholder) {

+        this.placeholder = placeholder;

+    }

+    

+    public boolean isResolved() {

+        return resolved || placeholder;

+    }

+    

+    public void setResolved(boolean res) {

+        resolved = res;

+    }

+

+    public void setName(String name) {

+        this.name = name;

+    }

+

+    public boolean isWildcard() {

+        return wildcard;

+    }

+

+    public void setWildcard(boolean wildcard) {

+        this.wildcard = wildcard;

+    }

+    

+    public ClassNode getLowerBound() {

+        return lowerBound;

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/ast/GroovyClassVisitor.java b/groovy/src/main/org/codehaus/groovy/ast/GroovyClassVisitor.java
new file mode 100644
index 0000000..3b9733e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/GroovyClassVisitor.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+/**
+ * An implementation of the visitor pattern for working with ASTNodes
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public interface GroovyClassVisitor {
+
+    void visitClass(ClassNode node);
+    void visitConstructor(ConstructorNode node);
+    void visitMethod(MethodNode node);
+    void visitField(FieldNode node);
+    void visitProperty(PropertyNode node);
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/GroovyCodeVisitor.java b/groovy/src/main/org/codehaus/groovy/ast/GroovyCodeVisitor.java
new file mode 100644
index 0000000..bf19ed7
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/GroovyCodeVisitor.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.ast;
+
+import org.codehaus.groovy.ast.expr.*;
+
+import org.codehaus.groovy.ast.stmt.*;
+
+/**
+ * An implementation of the visitor pattern for working with ASTNodes
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+
+public interface GroovyCodeVisitor {
+
+    // statements
+
+    //-------------------------------------------------------------------------
+
+    void visitBlockStatement(BlockStatement statement);
+
+    void visitForLoop(ForStatement forLoop);
+
+    void visitWhileLoop(WhileStatement loop);
+
+    void visitDoWhileLoop(DoWhileStatement loop);
+
+    void visitIfElse(IfStatement ifElse);
+
+    void visitExpressionStatement(ExpressionStatement statement);
+
+    void visitReturnStatement(ReturnStatement statement);
+
+    void visitAssertStatement(AssertStatement statement);
+
+    void visitTryCatchFinally(TryCatchStatement finally1);
+
+    void visitSwitch(SwitchStatement statement);
+
+    void visitCaseStatement(CaseStatement statement);
+
+    void visitBreakStatement(BreakStatement statement);
+
+    void visitContinueStatement(ContinueStatement statement);
+
+    void visitThrowStatement(ThrowStatement statement);
+
+    void visitSynchronizedStatement(SynchronizedStatement statement);
+    
+    void visitCatchStatement(CatchStatement statement);
+
+    // expressions
+
+    //-------------------------------------------------------------------------
+
+    void visitMethodCallExpression(MethodCallExpression call);
+
+    void visitStaticMethodCallExpression(StaticMethodCallExpression expression);
+
+    void visitConstructorCallExpression(ConstructorCallExpression expression);
+
+    void visitTernaryExpression(TernaryExpression expression);
+    
+    void visitShortTernaryExpression(ElvisOperatorExpression expression);
+
+    void visitBinaryExpression(BinaryExpression expression);
+
+    void visitPrefixExpression(PrefixExpression expression);
+
+    void visitPostfixExpression(PostfixExpression expression);
+
+    void visitBooleanExpression(BooleanExpression expression);
+
+    void visitClosureExpression(ClosureExpression expression);
+
+    void visitTupleExpression(TupleExpression expression);
+
+    void visitMapExpression(MapExpression expression);
+
+    void visitMapEntryExpression(MapEntryExpression expression);
+
+    void visitListExpression(ListExpression expression);
+
+    void visitRangeExpression(RangeExpression expression);
+
+    void visitPropertyExpression(PropertyExpression expression);
+
+    void visitAttributeExpression(AttributeExpression attributeExpression);
+
+    void visitFieldExpression(FieldExpression expression);
+
+    void visitMethodPointerExpression(MethodPointerExpression expression);
+
+    void visitConstantExpression(ConstantExpression expression);
+
+    void visitClassExpression(ClassExpression expression);
+
+    void visitVariableExpression(VariableExpression expression);
+
+    void visitDeclarationExpression(DeclarationExpression expression);
+
+    void visitRegexExpression(RegexExpression expression);
+
+    void visitGStringExpression(GStringExpression expression);
+
+    void visitArrayExpression(ArrayExpression expression);
+
+    void visitSpreadExpression(SpreadExpression expression);
+
+    void visitSpreadMapExpression(SpreadMapExpression expression);
+
+    void visitNotExpression(NotExpression expression);
+
+    void visitUnaryMinusExpression(UnaryMinusExpression expression);
+
+    void visitUnaryPlusExpression(UnaryPlusExpression expression);
+
+    void visitBitwiseNegationExpression(BitwiseNegationExpression expression);
+
+    void visitCastExpression(CastExpression expression);
+
+    void visitArgumentlistExpression(ArgumentListExpression expression);
+
+    void visitClosureListExpression(ClosureListExpression closureListExpression);
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/ast/ImportNode.java b/groovy/src/main/org/codehaus/groovy/ast/ImportNode.java
new file mode 100644
index 0000000..ea2a65d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/ImportNode.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Represents an import statement of a single class
+ * 
+ * author Jochen Theodorou
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ImportNode extends AnnotatedNode implements Opcodes {
+
+    private final ClassNode type;
+    private final String alias;
+    
+    public ImportNode(ClassNode type, String alias) {
+        this.type = type;
+        this.alias = alias;
+    }
+    
+    /**
+     * @return the text display of this import
+     */
+    public String getText() {
+        if (alias == null || alias.length() == 0) {
+            return "import " + type.getName();
+        }
+        else {
+            return "import " + type.getName() + " as " + alias;
+        }
+    }
+    
+    public String getAlias() {
+        return alias;
+    }
+
+    public ClassNode getType() {
+        return type;
+    }
+    
+    public String getClassName() {
+    	return type.getName();
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/InnerClassNode.java b/groovy/src/main/org/codehaus/groovy/ast/InnerClassNode.java
new file mode 100644
index 0000000..6541b3f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/InnerClassNode.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+/**
+ * Represents an inner class declaration
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class InnerClassNode extends ClassNode {
+
+    private ClassNode outerClass;
+
+    /**
+     * @param name is the full name of the class
+     * @param modifiers the modifiers, @see org.objectweb.asm.Opcodes
+     * @param superClass the base class name - use "java.lang.Object" if no direct base class
+     */
+    public InnerClassNode(ClassNode outerClass, String name, int modifiers, ClassNode superClass) {
+        this(outerClass, name, modifiers, superClass, ClassHelper.EMPTY_TYPE_ARRAY, MixinNode.EMPTY_ARRAY);
+    }
+
+    /**
+     * @param name is the full name of the class
+     * @param modifiers the modifiers, @see org.objectweb.asm.Opcodes
+     * @param superClass the base class name - use "java.lang.Object" if no direct base class
+     */
+    public InnerClassNode(ClassNode outerClass, String name, int modifiers, ClassNode superClass, ClassNode[] interfaces, MixinNode[] mixins) {
+        super(name, modifiers, superClass, interfaces, mixins);
+        this.outerClass = outerClass;
+    }
+
+    public ClassNode getOuterClass() {
+        return outerClass;
+    }
+
+    /**
+     * @return the field node on the outer class or null if this is not an inner class
+     */
+    public FieldNode getOuterField(String name) {
+        return outerClass.getField(name);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/MethodNode.java b/groovy/src/main/org/codehaus/groovy/ast/MethodNode.java
new file mode 100644
index 0000000..2e544fb
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/MethodNode.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.objectweb.asm.Opcodes;
+
+import java.util.List;
+
+/**
+ * Represents a method declaration
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MethodNode extends AnnotatedNode implements Opcodes {
+
+    private final String name;
+    private int modifiers;
+    private ClassNode returnType;
+    private Parameter[] parameters;
+    private boolean hasDefaultValue = false;
+    private Statement code;
+    private boolean dynamicReturnType;
+    private VariableScope variableScope;
+    private final ClassNode[] exceptions;
+    
+    // type spec for generics
+    private GenericsType[] genericsTypes=null;
+
+    public MethodNode(String name, int modifiers, ClassNode returnType, Parameter[] parameters, ClassNode[] exceptions, Statement code) {
+        this.name = name;
+        this.modifiers = modifiers;
+        this.code = code;
+        this.returnType = returnType;
+        if (returnType==null) this.returnType = ClassHelper.OBJECT_TYPE; 
+        VariableScope scope = new VariableScope();
+        setVariableScope(scope);
+        setParameters(parameters);
+        
+        this.exceptions = exceptions;
+    }
+
+    /**
+     * The type descriptor for a method node is a string containing the name of the method, its return type,
+     * and its parameter types in a canonical form. For simplicity, I'm using the format of a Java declaration
+     * without parameter names, and with $dynamic as the type for any dynamically typed values.
+     */
+    // TODO: add test case for type descriptor
+    public String getTypeDescriptor() {
+        StringBuffer buf = new StringBuffer(name.length()+parameters.length*10);
+        // buf.append(dynamicReturnType ? "$dynamic" : cleanupTypeName(returnType));
+        //
+        buf.append(returnType.getName()); // br  to replace the above. Dynamic type returns Object.
+        //
+        buf.append(' ');
+        buf.append(name);
+        buf.append('(');
+        for (int i = 0; i < parameters.length; i++) {
+            if (i > 0) {
+                buf.append(", ");
+            }
+            Parameter param = parameters[i];
+            buf.append(param.getType().getName());
+        }
+        buf.append(')');
+        return buf.toString();
+    }
+ 
+    public boolean isVoidMethod() {
+        return returnType==ClassHelper.VOID_TYPE;
+    }
+
+    public Statement getCode() {
+        return code;
+    }
+
+    public void setCode(Statement code) {
+        this.code = code;
+    }
+
+    public int getModifiers() {
+        return modifiers;
+    }
+
+    public void setModifiers(int modifiers) {
+        this.modifiers = modifiers;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Parameter[] getParameters() {
+        return parameters;
+    }
+
+    public void setParameters(Parameter[] parameters) {
+        VariableScope scope = new VariableScope();
+        this.parameters = parameters;
+        if (parameters != null && parameters.length > 0) {
+            for (int i = 0; i < parameters.length; i++) {
+                Parameter para = parameters[i];
+                if (para.hasInitialExpression()) {
+                    this.hasDefaultValue = true;
+                }
+                para.setInStaticContext(isStatic());
+                scope.putDeclaredVariable(para);
+            }
+        }
+        setVariableScope(scope);
+    }
+    
+    public ClassNode getReturnType() {
+        return returnType;
+    }
+
+    public VariableScope getVariableScope() {
+        return variableScope;
+    }
+
+    public void setVariableScope(VariableScope variableScope) {
+        this.variableScope = variableScope;
+        variableScope.setInStaticContext(isStatic());
+    }
+
+    public boolean isDynamicReturnType() {
+        return dynamicReturnType;
+    }
+
+    public boolean isAbstract() {
+        return (modifiers & ACC_ABSTRACT) != 0;
+    }
+
+    public boolean isStatic() {
+        return (modifiers & ACC_STATIC) != 0;
+    }
+
+    public boolean isPublic() {
+        return (modifiers & ACC_PUBLIC) != 0;
+    }
+
+    public boolean isProtected() {
+        return (modifiers & ACC_PROTECTED) != 0;
+    }
+
+    public boolean hasDefaultValue() {
+        return this.hasDefaultValue;
+    }
+
+    public String toString() {
+        return super.toString() + "[name: " + name + "]";
+    }
+
+    public void setReturnType(ClassNode returnType) {
+        this.returnType = returnType;
+    }
+
+    public ClassNode[] getExceptions() {
+        return exceptions;
+    }
+    
+    public Statement getFirstStatement(){
+        if (code == null) return null;
+        Statement first = code;
+        while (first instanceof BlockStatement) {
+            List list = ((BlockStatement) first).getStatements();
+            if (list.isEmpty()) {
+                first=null;
+            } else {
+                first = (Statement) list.get(0);
+            }
+        }
+        return first;
+    }
+    
+    public GenericsType[] getGenericsTypes() {
+        return genericsTypes;
+    }
+
+    public void setGenericsTypes(GenericsType[] genericsTypes) {
+        this.genericsTypes = genericsTypes;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/MixinNode.java b/groovy/src/main/org/codehaus/groovy/ast/MixinNode.java
new file mode 100644
index 0000000..b6a6a8b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/MixinNode.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+/**
+ * Represents a mixin which can be applied to any ClassNode to implement mixins
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MixinNode extends ClassNode {
+
+    public static final MixinNode[] EMPTY_ARRAY = {};
+    
+    /**
+     * @param name is the full name of the class
+     * @param modifiers the modifiers, @see org.objectweb.asm.Opcodes
+     * @param superType the base class name - use "java.lang.Object" if no direct base class
+     */
+    public MixinNode(String name, int modifiers, ClassNode superType) {
+        this(name, modifiers, superType, ClassHelper.EMPTY_TYPE_ARRAY);
+    }
+
+    /**
+     * @param name is the full name of the class
+     * @param modifiers the modifiers, @see org.objectweb.asm.Opcodes
+     * @param superType the base class name - use "java.lang.Object" if no direct base class
+     */
+    public MixinNode(String name, int modifiers, ClassNode superType, ClassNode[] interfaces) {
+        super(name, modifiers, superType, interfaces, MixinNode.EMPTY_ARRAY);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/ModuleNode.java b/groovy/src/main/org/codehaus/groovy/ast/ModuleNode.java
new file mode 100644
index 0000000..fc68034
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/ModuleNode.java
@@ -0,0 +1,328 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import groovy.lang.Binding;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.objectweb.asm.Opcodes;
+
+import java.io.File;
+import java.util.*;
+
+/**
+ * Represents a module, which consists typically of a class declaration
+ * but could include some imports, some statements and multiple classes
+ * intermixed with statements like scripts in Python or Ruby
+ *
+ * @author Jochen Theodorou
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ModuleNode extends ASTNode implements Opcodes {
+
+    private BlockStatement statementBlock = new BlockStatement();
+    List classes = new LinkedList();
+    private List methods = new ArrayList();
+    private List imports = new ArrayList();
+    private List importPackages = new ArrayList();
+    private Map importIndex = new HashMap();
+    private Map staticImportAliases = new HashMap();
+    private Map staticImportFields = new LinkedHashMap();
+    private Map staticImportClasses = new LinkedHashMap();
+    private CompileUnit unit;
+    private String packageName;
+    private String description;
+    private boolean createClassForStatements = true;
+    private transient SourceUnit context;
+    private boolean importsResolved = false;
+    private static final String[] EMPTY_STRING_ARRAY = new String[] { /* class names, not qualified */ };
+
+
+    public ModuleNode (SourceUnit context ) {
+        this.context = context;
+    }
+
+    public ModuleNode (CompileUnit unit) {
+        this.unit = unit;
+    }
+
+    public BlockStatement getStatementBlock() {
+        return statementBlock;
+    }
+
+    public List getMethods() {
+        return methods;
+    }
+
+    public List getClasses() {
+        if (createClassForStatements && (!statementBlock.isEmpty() || !methods.isEmpty())) {
+            ClassNode mainClass = createStatementsClass();
+            createClassForStatements = false;
+            classes.add(0, mainClass);
+            mainClass.setModule(this);
+            addToCompileUnit(mainClass);
+        }
+        return classes;
+    }
+
+    public List getImports() {
+        return imports;
+    }
+
+    public List getImportPackages() {
+        return importPackages;
+    }
+
+    /**
+     * @return the class name for the given alias or null if none is available
+     */
+    public ClassNode getImport(String alias) {
+        return (ClassNode) importIndex.get(alias);
+    }
+
+    public void addImport(String alias, ClassNode type) {
+        imports.add(new ImportNode(type, alias));
+        importIndex.put(alias, type);
+    }
+
+    public String[]  addImportPackage(String packageName) {
+        importPackages.add(packageName);
+        return EMPTY_STRING_ARRAY;
+    }
+
+    public void addStatement(Statement node) {
+        statementBlock.addStatement(node);
+    }
+
+    public void addClass(ClassNode node) {
+        classes.add(node);
+        node.setModule(this);
+        addToCompileUnit(node);
+    }
+
+    /**
+     * @param node
+     */
+    private void addToCompileUnit(ClassNode node) {
+        // register the new class with the compile unit
+        if (unit != null) {
+            unit.addClass(node);
+        }
+    }
+
+    public void addMethod(MethodNode node) {
+        methods.add(node);
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public void setPackageName(String packageName) {
+        this.packageName = packageName;
+    }
+    
+    public boolean hasPackageName(){
+        return this.packageName != null;
+    }
+
+    public SourceUnit getContext() {
+        return context;
+    }
+
+    /**
+     * @return the underlying character stream description
+     */
+    public String getDescription() {
+        if( context != null )
+        {
+            return context.getName();
+        }
+        else
+        {
+            return this.description;
+        }
+    }
+
+    public void setDescription(String description) {
+        // DEPRECATED -- context.getName() is now sufficient
+        this.description = description;
+    }
+
+    public CompileUnit getUnit() {
+        return unit;
+    }
+
+    void setUnit(CompileUnit unit) {
+        this.unit = unit;
+    }
+
+    protected ClassNode createStatementsClass() {
+        String name = getPackageName();
+        if (name == null) {
+            name = "";
+        }
+        // now lets use the file name to determine the class name
+        if (getDescription() == null) {
+            throw new RuntimeException("Cannot generate main(String[]) class for statements when we have no file description");
+        }
+        name += extractClassFromFileDescription();
+
+        String baseClassName = null;
+        if (unit != null) baseClassName = unit.getConfig().getScriptBaseClass();
+        ClassNode baseClass = null;
+        if (baseClassName!=null) {
+            baseClass = ClassHelper.make(baseClassName);
+        }
+        if (baseClass == null) {
+            baseClass = ClassHelper.SCRIPT_TYPE;
+        }
+        ClassNode classNode = new ClassNode(name, ACC_PUBLIC, baseClass);
+        classNode.setScript(true);
+        classNode.setScriptBody(true);
+
+        // return new Foo(new ShellContext(args)).run()
+        classNode.addMethod(
+            new MethodNode(
+                "main",
+                ACC_PUBLIC | ACC_STATIC,
+                ClassHelper.VOID_TYPE,
+                new Parameter[] { new Parameter(ClassHelper.STRING_TYPE.makeArray(), "args")},
+                ClassNode.EMPTY_ARRAY,
+                new ExpressionStatement(
+                    new MethodCallExpression(
+                        new ClassExpression(ClassHelper.make(InvokerHelper.class)),
+                        "runScript",
+                        new ArgumentListExpression(
+                                new ClassExpression(classNode),
+                                new VariableExpression("args"))))));
+
+        classNode.addMethod(
+            new MethodNode("run", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statementBlock));
+
+        classNode.addConstructor(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement());
+        Statement stmt = new ExpressionStatement(
+                        new MethodCallExpression(
+                            new VariableExpression("super"),
+            				"setBinding",
+            				new ArgumentListExpression(
+                                        new VariableExpression("context"))));
+
+        classNode.addConstructor(
+            ACC_PUBLIC,
+            new Parameter[] { new Parameter(ClassHelper.make(Binding.class), "context")},
+			ClassNode.EMPTY_ARRAY,
+            stmt);
+
+        for (Iterator iter = methods.iterator(); iter.hasNext();) {
+            MethodNode node = (MethodNode) iter.next();
+            int modifiers = node.getModifiers();
+            if ((modifiers & ACC_ABSTRACT) != 0) {
+                throw new RuntimeException(
+                    "Cannot use abstract methods in a script, they are only available inside classes. Method: "
+                        + node.getName());
+            }
+            // br: the old logic seems to add static to all def f().... in a script, which makes enclosing
+            // inner classes (including closures) in a def function difficult. Comment it out.
+            node.setModifiers(modifiers /*| ACC_STATIC*/);
+
+            classNode.addMethod(node);
+        }
+        return classNode;
+    }
+
+    protected String extractClassFromFileDescription() {
+        // lets strip off everything after the last .
+        String answer = getDescription();
+        int idx = answer.lastIndexOf('.');
+        if (idx > 0) {
+            answer = answer.substring(0, idx);
+        }
+        // new lets trip the path separators
+        idx = answer.lastIndexOf('/');
+        if (idx >= 0) {
+            answer = answer.substring(idx + 1);
+        }
+        idx = answer.lastIndexOf(File.separatorChar);
+        if (idx >= 0) {
+            answer = answer.substring(idx + 1);
+        }
+        return answer;
+    }
+
+    public boolean isEmpty() {
+        return classes.isEmpty() && statementBlock.getStatements().isEmpty();
+    }
+    
+    public void sortClasses(){
+    	if (isEmpty()) return;
+    	List classes = getClasses();
+    	LinkedList sorted = new LinkedList();
+    	int level=1;
+    	while (!classes.isEmpty()) {
+	    	for (Iterator cni = classes.iterator(); cni.hasNext();) {
+				ClassNode cn = (ClassNode) cni.next();
+				ClassNode sn = cn;
+				for (int i=0; sn!=null && i<level; i++) sn = sn.getSuperClass();
+				if (sn!=null && sn.isPrimaryClassNode()) continue;
+				cni.remove();
+				sorted.addLast(cn);
+			}
+	    	level++;
+    	}
+    	this.classes = sorted;
+    }
+
+    public boolean hasImportsResolved() {
+        return importsResolved;
+    }
+
+    public void setImportsResolved(boolean importsResolved) {
+        this.importsResolved = importsResolved;
+    }
+
+    public Map getStaticImportAliases() {
+        return staticImportAliases;
+    }
+
+    public Map getStaticImportClasses() {
+        return staticImportClasses;
+    }
+
+    public Map getStaticImportFields() {
+        return staticImportFields;
+    }
+
+    public void addStaticMethodOrField(ClassNode type, String fieldName, String alias) {
+        staticImportAliases.put(alias, type);
+        staticImportFields.put(alias, fieldName);
+    }
+
+    public void addStaticImportClass(String name, ClassNode type) {
+        staticImportClasses.put(name, type);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/Parameter.java b/groovy/src/main/org/codehaus/groovy/ast/Parameter.java
new file mode 100644
index 0000000..b967f65
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/Parameter.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import org.codehaus.groovy.ast.expr.*;
+
+
+/**
+ * Represents a parameter on a constructor or method call. The type name is
+ * optional - it defaults to java.lang.Object if unknown.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class Parameter extends AnnotatedNode implements Variable {
+
+    public static final Parameter[] EMPTY_ARRAY = {
+    };
+
+    private ClassNode type;
+    private final String name;
+    private boolean dynamicTyped;
+    private Expression defaultValue;
+    private boolean hasDefaultValue;
+    private boolean inStaticContext;
+    private boolean closureShare=false;
+
+    public Parameter(ClassNode type, String name) {
+        this.name = name;
+        this.setType(type);
+        this.hasDefaultValue = false;
+    }
+    
+    public Parameter(ClassNode type, String name, Expression defaultValue) {
+        this(type,name);
+        this.defaultValue = defaultValue;
+        this.hasDefaultValue = true;
+    }
+
+    public String toString() {
+        return super.toString() + "[name:" + name + ((type == null) ? "" : " type: " + type.getName()) + ", hasDefaultValue: " + this.hasInitialExpression() + "]";
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public ClassNode getType() {
+        return type;
+    }
+
+    public void setType(ClassNode type) {
+        this.type = type;
+        dynamicTyped |= type==ClassHelper.DYNAMIC_TYPE;
+    }
+    
+    public boolean hasInitialExpression() {
+        return this.hasDefaultValue;
+    }
+    
+    /**
+     * @return the default value expression for this parameter or null if
+     * no default value is specified
+     */
+    public Expression getInitialExpression() {
+        return defaultValue;
+    }
+    
+    public void setInitialExpression(Expression init) {
+        defaultValue = init;
+        if (defaultValue==null) hasDefaultValue=false;
+    }
+    
+    public boolean isInStaticContext() {
+        return inStaticContext;
+    }
+    
+    public void setInStaticContext(boolean inStaticContext) {
+        this.inStaticContext = inStaticContext;
+    }
+
+    public boolean isDynamicTyped() {
+        return dynamicTyped;
+    }
+
+    public boolean isClosureSharedVariable() {
+        return closureShare;
+    }
+
+    public void setClosureSharedVariable(boolean inClosure) {
+        closureShare = inClosure;        
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/PropertyNode.java b/groovy/src/main/org/codehaus/groovy/ast/PropertyNode.java
new file mode 100644
index 0000000..f039e23
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/PropertyNode.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.*;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Represents a property (member variable, a getter and setter)
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class PropertyNode extends AnnotatedNode implements Opcodes,Variable {
+
+    private final FieldNode field;
+    private final Statement getterBlock;
+    private final Statement setterBlock;
+    private final int modifiers;
+    private boolean closureShare = false;
+
+    public PropertyNode(
+        String name, int modifiers, ClassNode type, ClassNode owner,
+        Expression initialValueExpression, Statement getterBlock,
+        Statement setterBlock)
+    {
+        this(new FieldNode(name, modifiers & ACC_STATIC, type, owner, initialValueExpression), modifiers, getterBlock, setterBlock);
+    }
+
+    public PropertyNode(FieldNode field, int modifiers, Statement getterBlock, Statement setterBlock) {
+        this.field = field;
+        this.modifiers = modifiers;
+        this.getterBlock = getterBlock;
+        this.setterBlock = setterBlock;
+    }
+
+    public Statement getGetterBlock() {
+        return getterBlock;
+    }
+
+    public Expression getInitialExpression() {
+        return field.getInitialExpression();
+    }
+
+    public int getModifiers() {
+        return modifiers;
+    }
+
+    public String getName() {
+        return field.getName();
+    }
+
+    public Statement getSetterBlock() {
+        return setterBlock;
+    }
+
+    public ClassNode getType() {
+        return field.getType();
+    }
+
+    public void setType(ClassNode t) {
+        field.setType(t);
+    }
+    
+    public FieldNode getField() {
+        return field;
+    }
+
+    public boolean isPrivate() {
+        return (modifiers & ACC_PRIVATE) != 0;
+    }
+    
+    public boolean isStatic() {
+        return (modifiers & ACC_STATIC) != 0;
+    }
+
+    public boolean hasInitialExpression() {
+        return field.hasInitialExpression();
+    }
+
+    public boolean isInStaticContext() {
+        return field.isInStaticContext();
+    }
+
+    public boolean isDynamicTyped() {
+        return field.isDynamicTyped();
+    }
+
+    public boolean isClosureSharedVariable() {
+        return false;
+    }
+    
+    public void setClosureSharedVariable(boolean inClosure) {
+        closureShare = inClosure;        
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/Variable.java b/groovy/src/main/org/codehaus/groovy/ast/Variable.java
new file mode 100644
index 0000000..5349699
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/Variable.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import org.codehaus.groovy.ast.expr.Expression;
+
+/**
+ * interface to mark a AstNode as Variable. Typically these are 
+ * VariableExpression, FieldNode, PropertyNode and Parameter
+ * 
+ * @author Jochen Theodorou
+ */
+public interface Variable {
+    
+    /**
+     * the type of the variable
+     */
+    ClassNode getType();
+    
+    /**
+     * the name of the variable
+     */
+    String getName();
+    
+    /**
+     * expression used to initialize the variable or null of there
+     * is no initialization.
+     */
+    Expression getInitialExpression();
+    
+    /**
+     * returns true if there is an initialization expression
+     */
+    boolean hasInitialExpression();
+    
+    /**
+     * returns true if this variable is used in a static context.
+     * A static context is any static initializer block, when this variable
+     * is declared as static or when this variable is used in a static method 
+     */
+    boolean isInStaticContext();
+
+    boolean isDynamicTyped();
+    boolean isClosureSharedVariable();
+    void setClosureSharedVariable(boolean inClosure);
+    
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/VariableScope.java b/groovy/src/main/org/codehaus/groovy/ast/VariableScope.java
new file mode 100644
index 0000000..2c618c6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/VariableScope.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Represents a variable scope. This is primarily used to determine variable sharing
+ * across method and closure boundaries.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Jochen Theodorou
+ * @version $Revision$
+ */
+public class VariableScope  {
+    private Map declaredVariables = Collections.EMPTY_MAP;
+    private Map referencedLocalVariables = Collections.EMPTY_MAP;
+    private Map referencedClassVariables = Collections.EMPTY_MAP;
+ 
+    private boolean inStaticContext = false;
+    private boolean resolvesDynamic = false; 
+    private ClassNode clazzScope;
+    private VariableScope parent;
+
+    public VariableScope() {
+    }
+    public VariableScope(VariableScope parent) {
+        this.parent = parent;
+    }
+
+    public Variable getDeclaredVariable(String name) {
+        return (Variable) declaredVariables.get(name);
+    }
+
+    public boolean isReferencedLocalVariable(String name) {
+        return referencedLocalVariables.containsKey(name);
+    }
+    
+    public boolean isReferencedClassVariable(String name) {
+        return referencedClassVariables.containsKey(name);
+    }
+    public VariableScope getParent() {
+        return parent;
+    }
+
+    public boolean isInStaticContext() {
+        return inStaticContext;
+    }
+
+    public void setInStaticContext(boolean inStaticContext) {
+        this.inStaticContext = inStaticContext;
+    }
+
+    public boolean isResolvingDynamic() {
+        return resolvesDynamic;
+    }
+
+    public void setDynamicResolving(boolean resolvesDynamic) {
+        this.resolvesDynamic = resolvesDynamic;
+    }
+
+    public void setClassScope(ClassNode node) {
+        this.clazzScope = node;
+    }
+    
+    public ClassNode getClassScope(){
+        return clazzScope;
+    }
+    
+    public boolean isClassScope(){
+        return clazzScope!=null;
+    }
+    
+    public boolean isRoot() {
+        return parent==null;
+    }
+    
+    public VariableScope copy() {
+        VariableScope copy = new VariableScope();
+        copy.clazzScope = clazzScope;
+        if (declaredVariables.size() > 0) {
+          copy.declaredVariables = new HashMap();
+          copy.declaredVariables.putAll(declaredVariables);
+        }
+        copy.inStaticContext = inStaticContext;
+        copy.parent = parent;
+        if (referencedClassVariables.size() > 0) {
+            copy.referencedClassVariables = new HashMap();
+            copy.referencedClassVariables.putAll(referencedClassVariables);
+        }
+        if (referencedLocalVariables.size() > 0) {
+            copy.referencedLocalVariables = new HashMap();
+            copy.referencedLocalVariables.putAll(referencedLocalVariables);
+        }
+        copy.resolvesDynamic = resolvesDynamic;
+        return copy;
+    }
+
+    public void putDeclaredVariable(Variable var) {
+        if (declaredVariables == Collections.EMPTY_MAP)
+          declaredVariables = new HashMap();
+        declaredVariables.put(var.getName(), var);
+    }
+
+    public Iterator getReferencedLocalVariablesIterator() {
+        return referencedLocalVariables.values().iterator();
+    }
+
+    public int getReferencedLocalVariablesCount() {
+        return referencedLocalVariables.size();
+    }
+
+    public Variable getReferencedLocalVariable(String name) {
+        return (Variable) referencedLocalVariables.get(name);
+    }
+
+    public void putReferencedLocalVariable(Variable var) {
+        if (referencedLocalVariables == Collections.EMPTY_MAP)
+          referencedLocalVariables = new HashMap();
+        referencedLocalVariables.put(var.getName(), var);
+    }
+
+    public void putReferencedClassVariable(Variable var) {
+        if (referencedClassVariables == Collections.EMPTY_MAP)
+          referencedClassVariables = new HashMap();
+        referencedClassVariables.put(var.getName(), var);
+    }
+
+    public Variable getReferencedClassVariable(String name) {
+        return (Variable) referencedClassVariables.get(name); 
+    }
+
+    public Object removeReferencedClassVariable(String name) {
+        if (referencedClassVariables == Collections.EMPTY_MAP)
+          return null;
+        else
+          return referencedClassVariables.remove(name);
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java
new file mode 100644
index 0000000..9b2b8f2
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java
@@ -0,0 +1,45 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.ast.expr;

+

+import java.util.Iterator;

+import java.util.Map;

+

+import org.codehaus.groovy.ast.AnnotationNode;

+import org.codehaus.groovy.ast.GroovyCodeVisitor;

+

+

+/**

+ * Represents an annotation "constant" that may appear in annotation attributes 

+ * (mainly used as a marker).

+ * 

+ * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>

+ * @version $Revision: 3264 $

+ */

+public class AnnotationConstantExpression extends ConstantExpression {

+    public AnnotationConstantExpression(AnnotationNode node) {

+        super(node);

+        setType(node.getClassNode());

+    }

+    

+    public void visit(GroovyCodeVisitor visitor) {

+        AnnotationNode node = (AnnotationNode) getValue();

+        Map attrs = node.getMembers();

+        for(Iterator it = attrs.values().iterator(); it.hasNext(); ) {

+            ((Expression) it.next()).visit(visitor);

+        }

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/ArgumentListExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/ArgumentListExpression.java
new file mode 100644
index 0000000..1054414
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/ArgumentListExpression.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import java.util.List;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.Parameter;
+
+/**
+ * Represents one or more arguments being passed into a method
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ArgumentListExpression extends TupleExpression {
+
+    public static final Object[] EMPTY_ARRAY = {
+    };
+    
+    public static final ArgumentListExpression EMPTY_ARGUMENTS = new ArgumentListExpression();
+
+    public ArgumentListExpression() {
+    }
+
+    public ArgumentListExpression(List expressions) {
+        super(expressions);
+    }
+
+    public ArgumentListExpression(Expression[] expressions) {
+        super(expressions);
+    }
+
+    public ArgumentListExpression(Parameter[] parameters) {
+        for (int i = 0; i < parameters.length; i++) {
+            Parameter parameter = parameters[i];
+            addExpression(new VariableExpression(parameter.getName()));
+        }
+    }
+    
+    public ArgumentListExpression(Expression expr) {
+        super(expr);
+    }
+
+    public ArgumentListExpression(Expression expr1, Expression expr2) {
+        super(expr1, expr2);
+    }
+
+    public ArgumentListExpression(Expression expr1, Expression expr2, Expression expr3) {
+        super(expr1, expr2, expr3);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new ArgumentListExpression(transformExpressions(getExpressions(), transformer));
+        ret.setSourcePosition(this);
+        return ret;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitArgumentlistExpression(this);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/ArrayExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/ArrayExpression.java
new file mode 100644
index 0000000..6c9c449
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/ArrayExpression.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents an array object construction either using a fixed size
+ * or an initializer expression
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ArrayExpression extends Expression {
+    private List expressions;
+    private List sizeExpression;
+
+    private ClassNode elementType;
+    
+    private static ClassNode makeArray(ClassNode base, List sizeExpression) {
+    	ClassNode ret = base.makeArray();
+    	if (sizeExpression==null) return ret;
+    	int size = sizeExpression.size();
+    	for (int i=1; i<size; i++) {
+    		ret = ret.makeArray();
+    	}
+    	return ret;
+    }
+    
+    public ArrayExpression(ClassNode elementType, List expressions, List sizeExpression) {
+        //expect to get the elementType
+        super.setType(makeArray(elementType,sizeExpression));
+        if (expressions==null) expressions=Collections.EMPTY_LIST;
+        this.elementType = elementType;
+        this.expressions = expressions;
+        this.sizeExpression = sizeExpression;
+        
+        for (Iterator iter = expressions.iterator(); iter.hasNext();) {
+            Object item = iter.next();
+            if (item!=null && !(item instanceof Expression)) {
+                throw new ClassCastException("Item: " + item + " is not an Expression");
+            }
+        }
+        if (sizeExpression!=null) {
+	        for (Iterator iter = sizeExpression.iterator(); iter.hasNext();) {
+	            Object item = iter.next();
+	            if (!(item instanceof Expression)) {
+	                throw new ClassCastException("Item: " + item + " is not an Expression");
+	            }
+	        }
+        }
+    }
+    
+    
+    /**
+     * Creates an array using an initializer expression
+     */
+    public ArrayExpression(ClassNode elementType, List expressions) {
+        this(elementType,expressions,null);
+    }
+
+    public void addExpression(Expression expression) {
+        expressions.add(expression);
+    }
+
+    public List getExpressions() {
+        return expressions;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitArrayExpression(this);
+    }
+
+    public boolean isDynamic() {
+        return false;
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+    	List exprList = transformExpressions(expressions, transformer);
+    	List sizes = null;
+    	if (sizeExpression!=null) sizes = transformExpressions(sizeExpression,transformer);
+        Expression ret = new ArrayExpression(elementType, exprList, sizes);
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public Expression getExpression(int i) {
+        Object object = expressions.get(i);
+        return (Expression) object;
+    }
+
+    public ClassNode getElementType() {
+        return elementType;
+    }
+    
+    public String getText() {
+        StringBuffer buffer = new StringBuffer("[");
+        boolean first = true;
+        for (Iterator iter = expressions.iterator(); iter.hasNext();) {
+            if (first) {
+                first = false;
+            }
+            else {
+                buffer.append(", ");
+            }
+
+            buffer.append(((Expression) iter.next()).getText());
+        }
+        buffer.append("]");
+        return buffer.toString();
+    }
+
+    public List getSizeExpression() {
+        return sizeExpression;
+    }
+
+    public String toString() {
+        return super.toString() + expressions;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/AttributeExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/AttributeExpression.java
new file mode 100644
index 0000000..0bfe9af
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/AttributeExpression.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+
+/**
+ * Represents an attribute access (accessing the field of a class) such as the expression "foo.@bar".
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class AttributeExpression extends PropertyExpression {
+
+    public AttributeExpression(Expression objectExpression, Expression property) {
+        super(objectExpression, property, false);
+    }
+
+    public AttributeExpression(Expression objectExpression, Expression property, boolean safe) {
+        super(objectExpression, property, safe);
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitAttributeExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new AttributeExpression(transformer.transform(getObjectExpression()),transformer.transform(getProperty()),isSafe());
+        ret.setSourcePosition(this);
+        return ret;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/BinaryExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/BinaryExpression.java
new file mode 100644
index 0000000..05330c4
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/BinaryExpression.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+
+/**
+ * Represents two expressions and an operation
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class BinaryExpression extends Expression {
+    
+    private Expression leftExpression;
+    private Expression rightExpression;
+    private final Token operation;
+    
+    public BinaryExpression(Expression leftExpression,
+                            Token operation,
+                            Expression rightExpression) {
+        this.leftExpression = leftExpression;
+        this.operation = operation;
+        this.rightExpression = rightExpression;
+    }
+
+    public String toString() {
+        return super.toString() +"[" + leftExpression + operation + rightExpression + "]";
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitBinaryExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new BinaryExpression(transformer.transform(leftExpression), operation, transformer.transform(rightExpression));
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public Expression getLeftExpression() {
+        return leftExpression;
+    }
+
+    public void setLeftExpression(Expression leftExpression) {
+        this.leftExpression = leftExpression;
+    }
+
+    public void setRightExpression(Expression rightExpression) {
+        this.rightExpression = rightExpression;
+    }
+
+    public Token getOperation() {
+        return operation;
+    }
+
+    public Expression getRightExpression() {
+        return rightExpression;
+    }
+
+    public String getText() {
+        if (operation.getType() == Types.LEFT_SQUARE_BRACKET) {
+            return leftExpression.getText() + "[" + rightExpression.getText() + "]";
+        }
+        return "(" + leftExpression.getText() + " " + operation.getText() + " " + rightExpression.getText() + ")";
+    }
+    
+    
+   /**
+    *  Creates an assignment expression in which the specified expression
+    *  is written into the specified variable name.   
+    */
+    
+    public static BinaryExpression newAssignmentExpression( Variable variable, Expression rhs ) {
+    	VariableExpression lhs = new VariableExpression( variable );
+    	Token         operator = Token.newPlaceholder( Types.ASSIGN );
+    
+    	return new BinaryExpression( lhs, operator, rhs );
+    }
+
+
+    /**
+     *  Creates variable initialization expression in which the specified expression
+     *  is written into the specified variable name.   
+     */
+     
+     public static BinaryExpression newInitializationExpression( String variable, ClassNode type, Expression rhs ) {
+     	VariableExpression lhs = new VariableExpression( variable );
+     
+     	if( type != null ) {
+     	    lhs.setType(type);
+     	}
+     
+     	Token operator = Token.newPlaceholder( Types.ASSIGN );
+     
+        return new BinaryExpression( lhs, operator, rhs );
+     }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/BitwiseNegationExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/BitwiseNegationExpression.java
new file mode 100644
index 0000000..2e0cf79
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/BitwiseNegationExpression.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * @author phk
+ */
+public class BitwiseNegationExpression extends Expression {
+
+    private Expression expression;
+	
+    public BitwiseNegationExpression(Expression expression) {
+        this.expression = expression;
+    }
+
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitBitwiseNegationExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new BitwiseNegationExpression(transformer.transform(expression));
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public String getText() {
+		return expression.getText();
+	}
+
+    public ClassNode getType() {
+        return expression.getType();
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/BooleanExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/BooleanExpression.java
new file mode 100644
index 0000000..10618ef
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/BooleanExpression.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a boolean expression
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class BooleanExpression extends Expression {
+    private final Expression expression;
+
+    public BooleanExpression(Expression expression) {
+        this.expression = expression;
+        setType(ClassHelper.boolean_TYPE); // for consistancy with AsmClassGenerator. see AsmClassGenerator.visitBooleanExpression.  
+    }
+    
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitBooleanExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new BooleanExpression(transformer.transform(expression));
+        ret.setSourcePosition(this);
+        return ret;    }
+    
+    public String getText() {
+        return expression.getText();
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/CastExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/CastExpression.java
new file mode 100644
index 0000000..61ceedb
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/CastExpression.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a type cast expression
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class CastExpression extends Expression {
+    
+    private final Expression expression;
+    private boolean ignoreAutoboxing=false;
+    private boolean coerce = false;
+
+    public static CastExpression asExpression(ClassNode type, Expression expression) {
+        CastExpression answer = new CastExpression(type, expression);
+        answer.setCoerce(true);
+        return answer;
+    }
+
+    public CastExpression(ClassNode type, Expression expression) {
+        this(type,expression,false);
+    }
+
+    public CastExpression(ClassNode type, Expression expression, boolean ignoreAutoboxing) {
+        super.setType(type);
+        this.expression = expression;
+        this.ignoreAutoboxing = ignoreAutoboxing;
+    }
+    
+    public boolean isIgnoringAutoboxing(){
+        return ignoreAutoboxing;
+    }
+
+    public boolean isCoerce() {
+        return coerce;
+    }
+
+    public void setCoerce(boolean coerce) {
+        this.coerce = coerce;
+    }
+
+    public String toString() {
+        return super.toString() +"[(" + getType().getName() + ") " + expression + "]";
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitCastExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        CastExpression ret =  new CastExpression(getType(), transformer.transform(expression));
+        ret.setSourcePosition(this);
+        ret.setCoerce(this.isCoerce());
+        return ret;
+    }
+    
+    public String getText() {
+        return "(" + getType() + ") " + expression.getText();
+    }
+ 
+    public Expression getExpression() {
+        return expression;
+    }
+    
+    public void setType(ClassNode t) {
+        super.setType(t);
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/ClassExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/ClassExpression.java
new file mode 100644
index 0000000..8bc4634
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/ClassExpression.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents access to a Java/Groovy class in an expression, such
+ * as when invoking a static method or accessing a static type
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ClassExpression extends Expression {
+
+    public ClassExpression(ClassNode type) {
+        super.setType(type);
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitClassExpression(this);
+    }
+    
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        return this;
+    }
+    
+    public String getText() {
+        return getType().getName();
+    }
+
+    public String toString() {
+       return super.toString() + "[type: " + getType().getName() + "]";
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/ClosureExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/ClosureExpression.java
new file mode 100644
index 0000000..9b25d39
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/ClosureExpression.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+
+/**
+ * Represents a closure creation expression such as { statement; } 
+ * or { i : statement; } or { i, x, String y: statement }
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ClosureExpression extends Expression {
+    
+    private Parameter[] parameters;
+    private Statement code;
+    private VariableScope variableScope;
+    
+    public ClosureExpression(Parameter[] parameters, Statement code) {
+        this.parameters = parameters;
+        this.code = code;
+        super.setType(ClassHelper.CLOSURE_TYPE);
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitClosureExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        return this;
+    }
+    
+    public String toString() {
+        return super.toString() + InvokerHelper.toString(parameters) + "{ " + code + " }";
+    }
+
+    public Statement getCode() {
+        return code;
+    }
+
+    public Parameter[] getParameters() {
+        return parameters;
+    }
+
+    public boolean isParameterSpecified() {
+        return parameters != null && parameters.length > 0;
+    }
+    
+    public VariableScope getVariableScope() {
+        return variableScope;
+    }
+
+    public void setVariableScope(VariableScope variableScope) {
+        this.variableScope = variableScope;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/ClosureListExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/ClosureListExpression.java
new file mode 100644
index 0000000..5c182f8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/ClosureListExpression.java
@@ -0,0 +1,86 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.ast.expr;

+

+import java.util.ArrayList;

+import java.util.Iterator;

+import java.util.List;

+

+import org.codehaus.groovy.ast.GroovyCodeVisitor;

+import org.codehaus.groovy.ast.VariableScope;

+

+/**

+ * This class rerpresents a list of expressions used to 

+ * create closures. Example:

+ * <code>

+ * def foo = (1;2;;)

+ * </code>

+ * The right side is a ClosureListExpression consisting of

+ * two ConstantExpressions for the values 1 and 2, and two

+ * EmptyStatement entries. The ClosureListExpression defines a new 

+ * variable scope. All created Closures share this scope.

+ * 

+ * 

+ * @author Jochen Theodorou

+ */

+public class ClosureListExpression extends ListExpression {

+

+    private VariableScope scope;

+    

+    public ClosureListExpression(List expressions) {

+        super(expressions);

+        scope = new VariableScope();

+    }

+    

+    public ClosureListExpression() {

+        this(new ArrayList(3));

+    }

+    

+    public void visit(GroovyCodeVisitor visitor) {

+        visitor.visitClosureListExpression(this);

+    }

+    

+    public Expression transformExpression(ExpressionTransformer transformer) {

+        Expression ret = new ClosureListExpression(transformExpressions(getExpressions(), transformer));

+        ret.setSourcePosition(this);

+        return ret;       

+    }

+    

+    public void setVariableScope(VariableScope scope) {

+        this.scope = scope;

+    }

+    

+    public VariableScope getVariableScope() {

+        return scope;

+    }

+    

+    public String getText() {

+        StringBuffer buffer = new StringBuffer("(");

+        boolean first = true;

+        for (Iterator iter = getExpressions().iterator(); iter.hasNext(); ) {

+            if (first) {

+                first = false;

+            } else {

+                buffer.append("; ");

+            }

+            

+            buffer.append(((Expression)iter.next()).getText());

+        }

+        buffer.append(")");

+        return buffer.toString();

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/ConstantExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/ConstantExpression.java
new file mode 100644
index 0000000..97945c9
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/ConstantExpression.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a constant expression such as null, true, false
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ConstantExpression extends Expression {
+    public static final ConstantExpression VOID = new ConstantExpression(Void.class);
+    public static final ConstantExpression NULL = new ConstantExpression(null);
+    public static final ConstantExpression TRUE = new ConstantExpression(Boolean.TRUE);
+    public static final ConstantExpression FALSE = new ConstantExpression(Boolean.FALSE);
+    public static final ConstantExpression EMPTY_STRING = new ConstantExpression("");
+    //public static final Expression EMPTY_ARRAY = new PropertyExpression(new ClassExpression(ArgumentListExpression.class.getName()), "EMPTY_ARRAY");
+    public static final ConstantExpression EMTPY_EXPRESSION = new ConstantExpression(null);
+    
+    private Object value;
+    
+    public ConstantExpression(Object value) {
+        this.value = value;
+        if (this.value != null)
+            setType(ClassHelper.make(value.getClass()));
+    }
+
+    public String toString() {
+        return "ConstantExpression[" + value + "]";
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitConstantExpression(this);
+    }
+
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        return this;
+    }
+
+    /**
+     * @return the value of this constant expression
+     */    
+    public Object getValue() {
+        return value;
+    }
+
+    public String getText() {
+        return (value == null) ? "null" : value.toString();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/ConstructorCallExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/ConstructorCallExpression.java
new file mode 100644
index 0000000..671dc2a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/ConstructorCallExpression.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * A constructor call
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Jochen Theodorou
+ * @version $Revision$
+ */
+public class ConstructorCallExpression extends Expression {
+
+    private final Expression arguments;
+
+    public ConstructorCallExpression(ClassNode type, Expression arguments) {
+        super.setType(type);
+        if (!(arguments instanceof TupleExpression)){
+            this.arguments = new TupleExpression(arguments);
+        } else {
+            this.arguments = arguments;
+        }
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitConstructorCallExpression(this);
+    }
+    
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression args = transformer.transform(arguments);
+        Expression ret = new ConstructorCallExpression(getType(), args);
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public Expression getArguments() {
+        return arguments;
+    }
+
+    public String getText() {
+        String text = null;
+        if (isSuperCall()) {
+            text = "super ";
+        } else if (isThisCall()) {
+            text = "this ";
+        } else {
+            text = "new "+ getType().getName();
+        }
+        return text + arguments.getText();
+    }
+
+    public String toString() {
+        return super.toString() + "[type: " + getType() + " arguments: " + arguments + "]";
+    }
+    
+    public boolean isSuperCall() {
+        return getType()==ClassNode.SUPER;
+    }
+    
+    public boolean isSpecialCall(){
+        return isThisCall() || isSuperCall();
+    }
+    
+    public boolean isThisCall() {
+        return getType()==ClassNode.THIS;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/DeclarationExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/DeclarationExpression.java
new file mode 100644
index 0000000..99f84f8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/DeclarationExpression.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.syntax.Token;
+
+/**
+ * Represents a local variable name declaration, an expression like 
+ * "def foo" or with type "String foo".
+ * 
+ * @author Jochen Theodorou
+ * @version $Revision$
+ */
+public class DeclarationExpression extends BinaryExpression {
+    
+    public DeclarationExpression(VariableExpression left, Token operation, Expression right) {
+        super(left,operation,right);
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitDeclarationExpression(this);
+    }
+    
+    public VariableExpression getVariableExpression() {
+        return (VariableExpression) this.getLeftExpression();
+    }
+    
+    public void setLeftExpression(Expression leftExpression) {
+        super.setLeftExpression((VariableExpression) leftExpression);
+    }
+    
+    
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret =  new DeclarationExpression((VariableExpression) transformer.transform(getLeftExpression()), getOperation(), transformer.transform(getRightExpression()));
+        ret.setSourcePosition(this);
+        return ret;        
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/ElvisOperatorExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/ElvisOperatorExpression.java
new file mode 100644
index 0000000..c2f3939
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/ElvisOperatorExpression.java
@@ -0,0 +1,67 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.ast.expr;

+

+import org.codehaus.groovy.ast.GroovyCodeVisitor;

+

+/**

+ * Represents a short ternary expression x ?: y, which is equal 

+ * to 

+ * <pre>

+ * def truePart = x

+ * def booleanPart = truePart as boolean

+ * booleanPart? truePart : y

+ * </pre>

+ * Even if x is no atomic expression, x will be evaluated only 

+ * once. Example:

+ * <pre>

+ * class Foo { 

+ *   def index=0 

+ *   def getX(){ index++; return index }

+ * }

+ * def foo = new Foo()

+ * def result = foo.x ?: "false case" 

+ * assert foo.index == 1

+ * assert result == 1 

+ * <pre>

+ * 

+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>

+ * @since 1.1

+ */

+public class ElvisOperatorExpression extends TernaryExpression {

+

+    public ElvisOperatorExpression(Expression base, Expression falseExpression) {

+        super(getBool(base), base, falseExpression);

+    }

+    

+    private static BooleanExpression getBool(Expression base) {

+       BooleanExpression be = new BooleanExpression(base);

+       be.setSourcePosition(base);

+       return be;

+    }

+

+    public void visit(GroovyCodeVisitor visitor) {

+        visitor.visitShortTernaryExpression(this);

+    }

+    

+    public Expression transformExpression(ExpressionTransformer transformer) {

+        Expression ret = new ElvisOperatorExpression(

+                transformer.transform(getTrueExpression()),

+                transformer.transform(getFalseExpression()));

+        ret.setSourcePosition(this);

+        return ret; 

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/EmptyExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/EmptyExpression.java
new file mode 100644
index 0000000..bc1f841
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/EmptyExpression.java
@@ -0,0 +1,40 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.ast.expr;

+

+import org.codehaus.groovy.ast.GroovyCodeVisitor;

+

+/**

+ * This class is a place holder for an empty expression. 

+ * Empty expression are used in closures lists like (;). During

+ * class Generation this expression should be either ignored or

+ * reaplce with a null value.

+ *   

+ * @author Jochen Theodorou

+ * @see org.codehaus.groovy.ast.stmt.EmptyStatement

+ */

+public class EmptyExpression extends Expression {

+    public static final EmptyExpression INSTANCE = new EmptyExpression();

+

+    public Expression transformExpression(ExpressionTransformer transformer) {

+        return this;

+    }

+

+    public void visit(GroovyCodeVisitor visitor) {

+        return;

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/Expression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/Expression.java
new file mode 100644
index 0000000..36225e5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/Expression.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+
+/**
+ * Represents a base class for expressions which evaluate as an object
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class Expression extends ASTNode {
+
+    private ClassNode type=ClassHelper.DYNAMIC_TYPE;
+    
+    /**
+     * Return a copy of the expression calling the transformer on any nested expressions 
+     * @param transformer
+     */
+    public abstract Expression transformExpression(ExpressionTransformer transformer);
+
+    /**
+     * Transforms the list of expressions
+     * @return a new list of transformed expressions
+     */
+    protected List transformExpressions(List expressions, ExpressionTransformer transformer) {
+        List list = new ArrayList(expressions.size());
+        for (Iterator iter = expressions.iterator(); iter.hasNext(); ) {
+            list.add(transformer.transform((Expression) iter.next()));
+        }
+        return list;
+    }
+    
+    public ClassNode getType() {
+        return type;
+    }
+    
+    public void setType(ClassNode t) {
+        type=t;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/ExpressionTransformer.java b/groovy/src/main/org/codehaus/groovy/ast/expr/ExpressionTransformer.java
new file mode 100644
index 0000000..2742e09
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/ExpressionTransformer.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+
+/**
+ * Provides a way to transform expressions
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public interface ExpressionTransformer {
+    
+    /** 
+     * Transforms the given expression into another expression
+     */
+    Expression transform(Expression expression);
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/FieldExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/FieldExpression.java
new file mode 100644
index 0000000..b694136
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/FieldExpression.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a field access such as the expression "this.foo".
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class FieldExpression extends Expression {
+
+    private final FieldNode field;
+    
+    public FieldExpression(FieldNode field) {
+        this.field = field;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitFieldExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        return this;
+    }
+    
+    public String getFieldName() {
+        return field.getName();
+    }
+
+    public FieldNode getField() {
+        return field;
+    }
+
+    public String getText() {
+        return "this." + field.getName();
+    }
+
+    public boolean isDynamicTyped() {
+        return field.isDynamicTyped();
+    }
+
+    public void setType(ClassNode type) {
+        super.setType(type);
+        field.setType(type);
+    }
+    
+    public ClassNode getType() {
+        return field.getType();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/GStringExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/GStringExpression.java
new file mode 100644
index 0000000..64e4d45
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/GStringExpression.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a String expression which contains embedded values inside
+ * it such as "hello there ${user} how are you" which is expanded lazily
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class GStringExpression extends Expression {
+
+    private String verbatimText;
+    private List strings = new ArrayList();
+    private List values = new ArrayList();
+    
+    public GStringExpression(String verbatimText) {
+        this.verbatimText = verbatimText;
+        super.setType(ClassHelper.GSTRING_TYPE);
+    }
+
+    public GStringExpression(String verbatimText, List strings, List values) {
+        this.verbatimText = verbatimText;
+        this.strings = strings;
+        this.values = values;
+        super.setType(ClassHelper.GSTRING_TYPE);
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitGStringExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new GStringExpression(
+                verbatimText,
+                transformExpressions(strings, transformer),
+                transformExpressions(values, transformer));
+        ret.setSourcePosition(this);
+        return ret;        
+    }
+
+    public String toString() {
+        return super.toString() + "[strings: " + strings + " values: " + values + "]";
+    }
+
+    public String getText() {
+        return verbatimText;
+    }
+
+    public List getStrings() {
+        return strings;
+    }
+
+    public List getValues() {
+        return values;
+    }
+
+    public void addString(ConstantExpression text) {
+        if (text == null) {
+            throw new NullPointerException("Cannot add a null text expression");
+        }
+        strings.add(text);
+    }
+
+    public void addValue(Expression value) {
+        // If the first thing is an value, then we need a dummy empty string in front of it so that when we
+        // toString it they come out in the correct order.
+        if (strings.size() == 0)
+            strings.add(ConstantExpression.EMPTY_STRING);
+        values.add(value);
+    }
+
+    public Expression getValue(int idx) {
+        return (Expression) values.get(idx);
+    }
+
+    public boolean isConstantString() {
+        return values.isEmpty();
+    }
+
+    public Expression asConstantString() {
+        StringBuffer buffer = new StringBuffer();
+        for (Iterator iter = strings.iterator(); iter.hasNext();) {
+            ConstantExpression expression = (ConstantExpression) iter.next();
+            Object value = expression.getValue();
+            if (value != null) {
+                buffer.append(value);
+            }
+        }
+        return new ConstantExpression(buffer.toString());
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/ListExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/ListExpression.java
new file mode 100644
index 0000000..31d55f5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/ListExpression.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a list expression [1, 2, 3] which creates a mutable List
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ListExpression extends Expression {
+    private List expressions;
+
+    public ListExpression() {
+        this(new ArrayList());
+    }
+    
+    public ListExpression(List expressions) {
+        this.expressions = expressions;
+        //TODO: get the type's of the expressions to specify the
+        // list type to List<X> if possible.
+        setType(ClassHelper.LIST_TYPE);
+    }
+    
+    public void addExpression(Expression expression) {
+        expressions.add(expression);
+    }
+    
+    public List getExpressions() {
+        return expressions;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitListExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new ListExpression(transformExpressions(getExpressions(), transformer));
+        ret.setSourcePosition(this);
+        return ret;       
+    }
+
+    public Expression getExpression(int i) {
+        return (Expression) expressions.get(i);
+    }
+
+    public String getText() {
+        StringBuffer buffer = new StringBuffer("[");
+        boolean first = true;
+        for (Iterator iter = expressions.iterator(); iter.hasNext(); ) {
+            if (first) {
+                first = false;
+            }
+            else {
+                buffer.append(", ");
+            }
+            
+            buffer.append(((Expression)iter.next()).getText());
+        }
+        buffer.append("]");
+        return buffer.toString();
+    }
+
+    public String toString() {
+        return super.toString() + expressions;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/MapEntryExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/MapEntryExpression.java
new file mode 100644
index 0000000..8752aff
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/MapEntryExpression.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+
+/**
+ * Represents an entry inside a map expression such as 1 : 2.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MapEntryExpression extends Expression {
+    private Expression keyExpression;
+    private Expression valueExpression;
+
+    public MapEntryExpression(Expression keyExpression, Expression valueExpression) {
+        this.keyExpression = keyExpression;
+        this.valueExpression = valueExpression;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitMapEntryExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new MapEntryExpression(transformer.transform(keyExpression), transformer.transform(valueExpression));
+        ret.setSourcePosition(this);
+        return ret;        
+    }
+
+    public String toString() {
+        return super.toString() + "(key: " + keyExpression + ", value: " + valueExpression + ")";
+    }
+
+    public Expression getKeyExpression() {
+        return keyExpression;
+    }
+
+    public Expression getValueExpression() {
+        return valueExpression;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/MapExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/MapExpression.java
new file mode 100644
index 0000000..34fa41d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/MapExpression.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a map expression [1 : 2, "a" : "b", x : y] which creates a mutable Map
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MapExpression extends Expression {
+    private final List mapEntryExpressions;
+
+    public MapExpression() {
+        this(new ArrayList());
+    }
+
+    public MapExpression(List mapEntryExpressions) {
+        this.mapEntryExpressions = mapEntryExpressions;
+        //TODO: get the type's of the expressions to specify the
+        // map type to Map<X> if possible.
+        setType(ClassHelper.MAP_TYPE);
+    }
+
+    public void addMapEntryExpression(MapEntryExpression expression) {
+        mapEntryExpressions.add(expression);
+    }
+
+    public List getMapEntryExpressions() {
+        return mapEntryExpressions;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitMapExpression(this);
+    }
+
+    public boolean isDynamic() {
+        return false;
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new MapExpression(transformExpressions(getMapEntryExpressions(), transformer));
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public String toString() {
+        return super.toString() + mapEntryExpressions;
+    }
+
+    public String getText() {
+        StringBuffer sb = new StringBuffer(32);
+        sb.append("[");
+        int size = mapEntryExpressions.size();
+        MapEntryExpression mapEntryExpression = null;
+        if (size > 0) {
+            mapEntryExpression = (MapEntryExpression) mapEntryExpressions.get(0);
+            sb.append(mapEntryExpression.getKeyExpression().getText() + ":" + mapEntryExpression.getValueExpression().getText());
+            for (int i = 1; i < size; i++) {
+                mapEntryExpression = (MapEntryExpression) mapEntryExpressions.get(i);
+                sb.append(", " + mapEntryExpression.getKeyExpression().getText() + ":" + mapEntryExpression.getValueExpression().getText());
+                if (sb.length() > 120 && i < size - 1) {
+                    sb.append(", ... ");
+                    break;
+                }
+            }
+        }
+        sb.append("]");
+        return sb.toString();
+    }
+
+    public void addMapEntryExpression(Expression keyExpression, Expression valueExpression) {
+        addMapEntryExpression(new MapEntryExpression(keyExpression, valueExpression));
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/MethodCallExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/MethodCallExpression.java
new file mode 100644
index 0000000..b5ae53e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/MethodCallExpression.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import groovy.lang.MetaMethod;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * A method call on an object or class
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MethodCallExpression extends Expression {
+
+    private Expression objectExpression;
+    private Expression method;
+    private Expression arguments;
+    private boolean spreadSafe = false;
+    private boolean safe = false;
+    private boolean implicitThis;
+    
+    public static final Expression NO_ARGUMENTS = new TupleExpression();
+
+    public MetaMethod getMetaMethod() {
+        return metaMethod;
+    }
+
+    private MetaMethod metaMethod = null;
+
+    public MethodCallExpression(Expression objectExpression, String method, Expression arguments) {
+        this(objectExpression,new ConstantExpression(method),arguments);
+    }
+    
+    public MethodCallExpression(Expression objectExpression, Expression method, Expression arguments) {
+        this.objectExpression = objectExpression;
+        this.method = method;
+        this.arguments = arguments;
+        //TODO: set correct type here
+        // if setting type and a methodcall is the last expression in a method,
+        // then the method will return null if the method itself is not void too!
+        // (in bytecode after call: aconst_null, areturn)
+        this.setType(ClassHelper.DYNAMIC_TYPE);
+        this.setImplicitThis(true);
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitMethodCallExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        MethodCallExpression answer =
+            new MethodCallExpression(transformer.transform(objectExpression), transformer.transform(method), transformer.transform(arguments));
+        answer.setSafe(safe);
+        answer.setSourcePosition(this);
+        return answer;
+    }
+
+    public Expression getArguments() {
+        return arguments;
+    }
+
+    public void setArguments(Expression arguments)
+    {
+      this.arguments = arguments;
+    }
+
+    public Expression getMethod() {
+        return method;
+    }
+
+    public void setMethod(Expression method)
+    {
+      this.method = method;
+    }
+
+  /**
+     * This method returns the method name as String if it is no dynamic
+     * calculated method name, but a constant.
+     */
+    public String getMethodAsString() {
+        if (! (method instanceof ConstantExpression)) return null;
+        ConstantExpression constant = (ConstantExpression) method;
+        return constant.getText();
+    }
+
+    public void setObjectExpression(Expression objectExpression)
+    {
+      this.objectExpression = objectExpression;
+    }
+
+    public Expression getObjectExpression() {
+        return objectExpression;
+    }
+
+    public String getText() {
+        return objectExpression.getText() + "." + method.getText() + arguments.getText();
+    }
+
+    /**
+     * @return is this a safe method call, i.e. if true then if the source object is null
+     * then this method call will return null rather than throwing a null pointer exception
+     */
+    public boolean isSafe() {
+        return safe;
+    }
+
+    public void setSafe(boolean safe) {
+        this.safe = safe;
+    }
+
+    public boolean isSpreadSafe() {
+        return spreadSafe;
+    }
+
+    public void setSpreadSafe(boolean value) {
+        spreadSafe = value;
+    }
+
+    /**
+     * @return true if no object expression was specified otherwise if 
+     * some expression was specified for the object on which to evaluate
+     * the method then return false
+     */
+    public boolean isImplicitThis() {
+        return implicitThis;
+    }
+
+    public void setImplicitThis(boolean implicitThis) {
+        this.implicitThis = implicitThis;
+    }
+
+    public String toString() {
+        return super.toString()
+            + "[object: "
+            + objectExpression
+            + " method: "
+            + method
+            + " arguments: "
+            + arguments
+            + "]";
+    }
+
+    public void setMetaMethod(MetaMethod mmeth) {
+        this.metaMethod = mmeth;
+        super.setType(ClassHelper.make(mmeth.getReturnType()));
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/MethodPointerExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/MethodPointerExpression.java
new file mode 100644
index 0000000..6e44b01
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/MethodPointerExpression.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import groovy.lang.Closure;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a method pointer on an object such as
+ * foo.&bar which means find the method pointer on foo for the method called "bar"
+ * which is equivalent to
+ * <code>
+ * foo.metaClass.getMethodPointer(foo, "bar")
+ * 
+ * @version $Revision$
+ */
+public class MethodPointerExpression extends Expression {
+
+    private Expression expression;
+    private Expression methodName;
+
+    public MethodPointerExpression(Expression expression, Expression methodName) {
+        this.expression = expression;
+        this.methodName = methodName;
+    }
+
+    public Expression getExpression() {
+	if (expression == null)
+	    return VariableExpression.THIS_EXPRESSION;
+	else
+	    return expression;
+    }
+
+    public Expression getMethodName() {
+        return methodName;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitMethodPointerExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret;
+        Expression mname = transformer.transform(methodName);
+        if (expression == null) {
+	        ret = new MethodPointerExpression(VariableExpression.THIS_EXPRESSION, mname);
+        } else {
+	        ret = new MethodPointerExpression(transformer.transform(expression), mname);
+        }
+        ret.setSourcePosition(this);
+        return ret;        
+    }
+
+    public String getText() {
+        if (expression == null) {
+            return "&" + methodName;
+        } else {
+            return expression.getText() + ".&" + methodName.getText();
+        }
+    }
+
+    public ClassNode getType() {
+        return ClassHelper.CLOSURE_TYPE;
+    }
+
+    public boolean isDynamic() {
+        return false;
+    }
+
+    public Class getTypeClass() {
+        return Closure.class;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/NamedArgumentListExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/NamedArgumentListExpression.java
new file mode 100644
index 0000000..0dbe425
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/NamedArgumentListExpression.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import java.util.List;
+
+/**
+ * Represents one or more arguments being passed into a method by name
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class NamedArgumentListExpression extends MapExpression {
+
+    public NamedArgumentListExpression() {
+    }
+    
+    public NamedArgumentListExpression(List mapEntryExpressions) {
+        super(mapEntryExpressions);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new NamedArgumentListExpression(transformExpressions(getMapEntryExpressions(), transformer)); 
+        ret.setSourcePosition(this);
+        return ret;        
+    }
+    
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/NotExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/NotExpression.java
new file mode 100644
index 0000000..456f158
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/NotExpression.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * @author sam
+ */
+public class NotExpression extends BooleanExpression {
+
+	public NotExpression(Expression expression) {
+		super(expression);
+	}
+
+	public void visit(GroovyCodeVisitor visitor) {
+		visitor.visitNotExpression(this);
+	}
+
+    public boolean isDynamic() {
+        return false;
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new NotExpression(transformer.transform(getExpression())); 
+        ret.setSourcePosition(this);
+        return ret;
+	}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/PostfixExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/PostfixExpression.java
new file mode 100644
index 0000000..2d61b98
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/PostfixExpression.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.syntax.Token;
+
+/**
+ * Represents a postfix expression like foo++ or bar++
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class PostfixExpression extends Expression {
+
+    private Token operation;
+    private Expression expression;
+
+    public PostfixExpression(Expression expression, Token operation) {
+        this.operation = operation;
+        this.expression = expression;
+    }
+
+    public String toString() {
+        return super.toString() + "[" + expression + operation + "]";
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitPostfixExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new PostfixExpression(transformer.transform(expression), operation); 
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public void setExpression(Expression expression) {
+        this.expression = expression;
+    }
+
+    public Token getOperation() {
+        return operation;
+    }
+
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public String getText() {
+        return "(" + expression.getText() + operation.getText() + ")";
+    }
+
+    public ClassNode getType() {
+        return expression.getType();
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/PrefixExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/PrefixExpression.java
new file mode 100644
index 0000000..f9adf2c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/PrefixExpression.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.syntax.Token;
+
+/**
+ * Represents a prefix expression like ++foo or --bar
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class PrefixExpression extends Expression {
+
+    private Token operation;
+    private Expression expression;
+
+    public PrefixExpression(Token operation, Expression expression) {
+        this.operation = operation;
+        this.expression = expression;
+    }
+
+    public String toString() {
+        return super.toString() + "[" + operation + expression + "]";
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitPrefixExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new PrefixExpression(operation, transformer.transform(expression)); 
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public void setExpression(Expression expression) {
+        this.expression = expression;
+    }
+
+    public Token getOperation() {
+        return operation;
+    }
+
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public String getText() {
+        return "(" + operation.getText() + expression.getText() + ")";
+    }
+
+    public ClassNode getType() {
+        return expression.getType();
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/PropertyExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/PropertyExpression.java
new file mode 100644
index 0000000..4fc468b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/PropertyExpression.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a property access such as the expression "foo.bar".
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class PropertyExpression extends Expression {
+
+    private Expression objectExpression;
+    private Expression property;
+    private boolean spreadSafe = false;
+    private boolean safe = false;
+    private boolean isStatic = false;
+
+    private boolean implicitThis = false;
+
+    public boolean isStatic() {
+        return isStatic;
+    }
+
+    public PropertyExpression(Expression objectExpression, String property) {
+        this(objectExpression, new ConstantExpression(property), false);
+    }
+    
+    public PropertyExpression(Expression objectExpression, Expression property) {
+        this(objectExpression, property, false);
+    }
+
+    public PropertyExpression(Expression objectExpression, Expression property, boolean safe) {
+        this.objectExpression = objectExpression;
+        this.property = property;
+        this.safe = safe;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitPropertyExpression(this);
+    }
+
+    public boolean isDynamic() {
+        return true;
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        return this;
+    }
+
+    public Expression getObjectExpression() {
+        return objectExpression;
+    }
+
+    public void setObjectExpression(Expression exp) {
+        objectExpression=exp;
+    }    
+    
+    public Expression getProperty() {
+        return property;
+    }
+    
+    public String getPropertyAsString() {
+        if (property==null) return null;
+        if (! (property instanceof ConstantExpression)) return null;
+        ConstantExpression constant = (ConstantExpression) property;
+        return constant.getText();
+    }
+
+    public String getText() {
+        return objectExpression.getText() + "." + property.getText();
+    }
+
+    /**
+     * @return is this a safe navigation, i.e. if true then if the source object is null
+     * then this navigation will return null
+     */
+    public boolean isSafe() {
+        return safe;
+    }
+
+    public boolean isSpreadSafe() {
+        return spreadSafe;
+    }
+
+    public void setSpreadSafe(boolean value) {
+        spreadSafe = value;
+    }
+
+    public String toString() {
+        return super.toString() + "[object: " + objectExpression + " property: " + property + "]";
+    }
+
+    public void setStatic(boolean aStatic) {
+        this.isStatic = aStatic;
+    }
+    
+    public boolean isImplicitThis(){
+        return implicitThis;
+    }
+    
+    public void setImplicitThis(boolean it) {
+        implicitThis  = it;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/RangeExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/RangeExpression.java
new file mode 100644
index 0000000..39e0f78
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/RangeExpression.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a range expression such as for iterating
+ * <pre>for i in 0..10 {...}
+ * </pre>
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class RangeExpression extends Expression {
+
+    private Expression from;
+    private Expression to;
+    private boolean inclusive;
+
+    public RangeExpression(Expression from, Expression to, boolean inclusive) {
+        this.from = from;
+        this.to = to;
+        this.inclusive = inclusive;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitRangeExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new RangeExpression(transformer.transform(from), transformer.transform(to), inclusive); 
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public Expression getFrom() {
+        return from;
+    }
+
+    public Expression getTo() {
+        return to;
+    }
+
+    public boolean isInclusive() {
+        return inclusive;
+    }
+
+    public String getText() {
+        return "(" + from.getText() +
+               (!isInclusive()? "..<" : ".." ) +
+               to.getText() + ")";
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/RegexExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/RegexExpression.java
new file mode 100644
index 0000000..bae34af
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/RegexExpression.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a regular expression of the form ~<double quoted string> which creates
+ * a regular expression. 
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class RegexExpression extends Expression {
+    
+    private final Expression string;
+    
+    public RegexExpression(Expression string) {
+        this.string = string;
+        super.setType(ClassHelper.PATTERN_TYPE);
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitRegexExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new RegexExpression(transformer.transform(string)); 
+        ret.setSourcePosition(this);
+        return ret;       
+    }
+
+    public Expression getRegex() {
+        return string;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/SpreadExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/SpreadExpression.java
new file mode 100644
index 0000000..b4a47b8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/SpreadExpression.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a spread expression *x in the list expression [1, *x, 2].
+ * 
+ * @version $Revision$
+ */
+public class SpreadExpression extends Expression {
+
+    private final Expression expression;
+	
+    public SpreadExpression(Expression expression) {
+        this.expression = expression;
+    }
+
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitSpreadExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new SpreadExpression(transformer.transform(expression)); 
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public String getText() {
+		return "*" + expression.getText();
+	}
+
+    public ClassNode getType() {
+        return expression.getType();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/SpreadMapExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/SpreadMapExpression.java
new file mode 100644
index 0000000..67c94ab
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/SpreadMapExpression.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a spread map expression *:m
+ *         in the map expression [1, *:m, 2, "c":100]
+ *      or in the method invoke expression func(1, *:m, 2, "c":100).
+ * 
+ * @version $Revision$
+ */
+public class SpreadMapExpression extends Expression {
+
+    private Expression expression;
+	
+    public SpreadMapExpression(Expression expression) {
+        this.expression = expression;
+    }
+
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitSpreadMapExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new SpreadMapExpression(transformer.transform(expression)); 
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public String getText() {
+	return "*:" + expression.getText();
+    }
+
+    public ClassNode getType() {
+        return expression.getType();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/StaticMethodCallExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/StaticMethodCallExpression.java
new file mode 100644
index 0000000..98539e6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/StaticMethodCallExpression.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import groovy.lang.MetaMethod;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * A static method call on a class
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class StaticMethodCallExpression extends Expression {
+
+    private ClassNode ownerType;
+    private String method;
+    private Expression arguments;
+    private MetaMethod metaMethod = null;
+
+    public StaticMethodCallExpression(ClassNode type, String method, Expression arguments) {
+        ownerType = type;
+        this.method = method;
+        this.arguments = arguments;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitStaticMethodCallExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new StaticMethodCallExpression(getOwnerType(), method, transformer.transform(arguments));
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public Expression getArguments() {
+        return arguments;
+    }
+
+    public String getMethod() {
+        return method;
+    }
+
+    public String getText() {
+        return getOwnerType().getName() + "." + method + arguments.getText();
+    }
+
+    public String toString() {
+        return super.toString() + "[" + getOwnerType().getName() + "#" + method + " arguments: " + arguments + "]";
+    }
+
+    public ClassNode getOwnerType() {
+        return ownerType;
+    }
+
+    public void setOwnerType(ClassNode ownerType) {
+        this.ownerType = ownerType;
+    }
+
+    public void setMetaMethod(MetaMethod metaMethod) {
+        this.metaMethod = metaMethod;
+    }
+
+    public MetaMethod getMetaMethod() {
+        return metaMethod;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/TernaryExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/TernaryExpression.java
new file mode 100644
index 0000000..2b2c2af
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/TernaryExpression.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a ternary expression (booleanExpression) ? expression : expression
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TernaryExpression extends Expression {
+
+    private BooleanExpression booleanExpression;
+    private Expression trueExpression;
+    private Expression falseExpression;
+
+    public TernaryExpression(
+        BooleanExpression booleanExpression,
+        Expression trueExpression,
+        Expression falseExpression) {
+        this.booleanExpression = booleanExpression;
+        this.trueExpression = trueExpression;
+        this.falseExpression = falseExpression;
+    }
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitTernaryExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new TernaryExpression(
+                (BooleanExpression) transformer.transform(booleanExpression),
+                transformer.transform(trueExpression),
+                transformer.transform(falseExpression)); 
+        ret.setSourcePosition(this);
+        return ret; 
+    }
+
+    public String toString() {
+        return super.toString() +"[" + booleanExpression + " ? " + trueExpression + " : " + falseExpression + "]";
+    }
+    
+    public BooleanExpression getBooleanExpression() {
+        return booleanExpression;
+    }
+
+    public Expression getFalseExpression() {
+        return falseExpression;
+    }
+
+    public Expression getTrueExpression() {
+        return trueExpression;
+    }
+
+    public String getText() {
+        return "("
+            + booleanExpression.getText()
+            + ") ? "
+            + trueExpression.getText()
+            + " : "
+            + falseExpression.getText();
+    }
+
+    public ClassNode getType() {
+        return ClassHelper.OBJECT_TYPE;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/TupleExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/TupleExpression.java
new file mode 100644
index 0000000..890bb0d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/TupleExpression.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a tuple expression {1, 2, 3} which creates an immutable List
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TupleExpression extends Expression {
+    private List expressions;
+
+    public TupleExpression() {
+        this(0);
+    }
+
+    public TupleExpression(Expression expr) {
+        this(1);
+        addExpression(expr);
+    }
+
+    public TupleExpression(Expression expr1, Expression expr2) {
+        this(2);
+        addExpression(expr1);
+        addExpression(expr2);
+    }
+
+    public TupleExpression(Expression expr1, Expression expr2, Expression expr3) {
+        this(3);
+        addExpression(expr1);
+        addExpression(expr2);
+        addExpression(expr3);
+    }
+    
+    public TupleExpression(int length) {
+        this.expressions = new ArrayList(length);
+    }
+    
+    public TupleExpression(List expressions) {
+        this.expressions = expressions;
+    }
+    
+    public TupleExpression(Expression[] expressionArray) {
+        this();
+        expressions.addAll(Arrays.asList(expressionArray));
+    }
+
+    public TupleExpression addExpression(Expression expression) {
+        expressions.add(expression);
+        return this;
+    }
+    
+    public List getExpressions() {
+        return expressions;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitTupleExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new TupleExpression(transformExpressions(getExpressions(), transformer)); 
+        ret.setSourcePosition(this);
+        return ret;
+    }
+
+    public Expression getExpression(int i) {
+        return (Expression) expressions.get(i);
+    }
+
+    public String getText() {
+        StringBuffer buffer = new StringBuffer("(");
+        boolean first = true;
+        for (Iterator iter = expressions.iterator(); iter.hasNext(); ) {
+            if (first) {
+                first = false;
+            }
+            else {
+                buffer.append(", ");
+            }
+            
+            buffer.append(((Expression)iter.next()).getText());
+        }
+        buffer.append(")");
+        return buffer.toString();
+    }
+
+    public String toString() {
+        return super.toString() + expressions;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/UnaryMinusExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/UnaryMinusExpression.java
new file mode 100644
index 0000000..81371e7
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/UnaryMinusExpression.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * @author sam
+ */
+public class UnaryMinusExpression extends Expression {
+
+	private final Expression expression;
+	
+	public UnaryMinusExpression(Expression expression) {
+		this.expression = expression;
+	}
+
+	public Expression getExpression() {
+		return expression;
+	}
+
+	public void visit(GroovyCodeVisitor visitor) {
+		visitor.visitUnaryMinusExpression(this);
+	}
+
+	public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new UnaryMinusExpression(transformer.transform(expression));
+        ret.setSourcePosition(this);
+        return ret;
+	}
+
+    public String getText() {
+		return expression.getText();
+	}
+
+    public ClassNode getType() {
+        return expression.getType();
+    }
+
+    public boolean isDynamic() {
+        return false;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/UnaryPlusExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/UnaryPlusExpression.java
new file mode 100644
index 0000000..0682072
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/UnaryPlusExpression.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * @author Paul King
+ */
+public class UnaryPlusExpression extends Expression {
+
+	private final Expression expression;
+
+	public UnaryPlusExpression(Expression expression) {
+		this.expression = expression;
+	}
+
+	public Expression getExpression() {
+		return expression;
+	}
+
+	public void visit(GroovyCodeVisitor visitor) {
+		visitor.visitUnaryPlusExpression(this);
+	}
+
+	public Expression transformExpression(ExpressionTransformer transformer) {
+        Expression ret = new UnaryPlusExpression(transformer.transform(expression));
+        ret.setSourcePosition(this);
+        return ret;
+	}
+
+    public String getText() {
+		return expression.getText();
+	}
+
+    public ClassNode getType() {
+        return expression.getType();
+    }
+
+    public boolean isDynamic() {
+        return false;
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/VariableExpression.java b/groovy/src/main/org/codehaus/groovy/ast/expr/VariableExpression.java
new file mode 100644
index 0000000..3074728
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/VariableExpression.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.expr;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.Variable;
+
+/**
+ * Represents a local variable name, the simplest form of expression. e.g. "foo".
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class VariableExpression extends Expression implements Variable {
+
+    public static final VariableExpression THIS_EXPRESSION = new VariableExpression("this", ClassHelper.DYNAMIC_TYPE);
+    public static final VariableExpression SUPER_EXPRESSION = new VariableExpression("super", ClassHelper.DYNAMIC_TYPE);
+
+    private String variable;
+    private boolean inStaticContext;
+    private boolean isDynamicTyped=false;
+    private Variable accessedVariable;
+    boolean closureShare=false;
+
+    public Variable getAccessedVariable() {
+        return accessedVariable;
+    }
+
+    public void setAccessedVariable(Variable origin) {
+        this.accessedVariable = origin;
+    }
+
+    public VariableExpression(String variable, ClassNode type) {
+        this.variable = variable;
+        setType(ClassHelper.getWrapper(type));
+    }
+    
+    public VariableExpression(String variable) {
+        this(variable, ClassHelper.DYNAMIC_TYPE);
+    }
+    
+    public VariableExpression(Variable variable) {
+        this(variable.getName(), variable.getType());
+        setAccessedVariable(variable);
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitVariableExpression(this);
+    }
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        return this;
+    }
+
+    public String getText() {
+        return variable;
+    }
+    
+    public String getName() {
+        return variable;
+    }
+
+    /**
+     * @return true if this variable is dynamically typed
+     */
+    public String toString() {
+        return super.toString() + "[variable: " + variable + (this.isDynamicTyped() ? "" : " type: " + getType()) + "]";
+    }
+
+    public Expression getInitialExpression() {
+        return null;
+    }
+
+    public boolean hasInitialExpression() {
+        return false;
+    }
+    
+    public boolean isInStaticContext() {
+        if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.isInStaticContext();
+        return inStaticContext;
+    }
+    
+    public void setInStaticContext(boolean inStaticContext) {
+        this.inStaticContext = inStaticContext;
+    }
+
+    public void setType(ClassNode cn){
+        super.setType(cn);
+        isDynamicTyped |= ClassHelper.DYNAMIC_TYPE==cn;
+    }
+    
+    public boolean isDynamicTyped() {
+        return isDynamicTyped;
+    }
+
+    public boolean isClosureSharedVariable() {
+        if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.isClosureSharedVariable();
+        return closureShare;
+    }
+    
+    public void setClosureSharedVariable(boolean inClosure) {
+        closureShare = inClosure;        
+    }
+    
+    public ClassNode getType() {
+        if (accessedVariable!=null && accessedVariable!=this) return accessedVariable.getType();
+        return super.getType();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/expr/package.html b/groovy/src/main/org/codehaus/groovy/ast/expr/package.html
new file mode 100644
index 0000000..bac5071
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/expr/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.ast.expr.*</title>
+  </head>
+  <body>
+    <p>AST nodes for Groovy expressions</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/ast/package.html b/groovy/src/main/org/codehaus/groovy/ast/package.html
new file mode 100644
index 0000000..71d94b0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.ast.*</title>
+  </head>
+  <body>
+    <p>Groovy AST nodes for the syntax of the language</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/AssertStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/AssertStatement.java
new file mode 100644
index 0000000..9bfcb58
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/AssertStatement.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+
+/**
+ * Represents an assert statement such as 
+ * <code>
+ * assert i != 0 : "should never be zero";
+ * </code>
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class AssertStatement extends Statement {
+
+    private BooleanExpression booleanExpression;
+    private Expression messageExpression;
+    
+    public AssertStatement(BooleanExpression booleanExpression) {
+        this(booleanExpression, ConstantExpression.NULL);
+    }
+    
+    public AssertStatement(BooleanExpression booleanExpression, Expression messageExpression) {
+        this.booleanExpression = booleanExpression;
+        this.messageExpression = messageExpression;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitAssertStatement(this);
+    }
+
+    public Expression getMessageExpression() {
+        return messageExpression;
+    }
+
+    public BooleanExpression getBooleanExpression() {
+        return booleanExpression;
+    }
+    public void setBooleanExpression(BooleanExpression booleanExpression) {
+        this.booleanExpression = booleanExpression;
+    }
+    public void setMessageExpression(Expression messageExpression) {
+        this.messageExpression = messageExpression;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/BlockStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/BlockStatement.java
new file mode 100644
index 0000000..3e6a713
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/BlockStatement.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.VariableScope;
+
+/**
+ * A list of statements
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class BlockStatement extends Statement {
+
+    private List statements = new ArrayList();
+    private VariableScope scope;
+    
+    public BlockStatement() {
+        this(new ArrayList(), new VariableScope());
+    }
+    
+    public BlockStatement(List statements, VariableScope scope) {
+        this.statements = statements;
+        this.scope = scope;
+    }
+    
+    public BlockStatement(Statement[] statements, VariableScope scope) {
+        this.statements.addAll(Arrays.asList(statements));
+        this.scope = scope;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitBlockStatement(this);
+    }
+
+    public List getStatements() {
+        return statements;
+    }
+
+    public void addStatement(Statement statement) {
+        statements.add(statement);
+    }
+
+    public void addStatements(List listOfStatements) {
+        statements.addAll(listOfStatements);
+    }
+
+    public String toString() {
+        return super.toString() + statements;
+    }
+
+    public String getText() {
+        StringBuffer buffer = new StringBuffer("{ ");
+        boolean first = true;
+        for (Iterator iter = statements.iterator(); iter.hasNext(); ) {
+            if (first) {
+                first = false;
+            }
+            else {
+                buffer.append("; ");
+            }
+            Statement statement = (Statement) iter.next();
+            buffer.append(statement.getText());
+        }
+        buffer.append(" }");
+        return buffer.toString();
+    }
+
+    public boolean isEmpty() {
+        return statements.isEmpty();
+    }
+
+    public void setVariableScope(VariableScope scope) {
+        this.scope = scope;
+    }
+    
+    public VariableScope getVariableScope() {
+        return scope;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/BreakStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/BreakStatement.java
new file mode 100644
index 0000000..1f12060
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/BreakStatement.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+
+/**
+ * Represents a break statement in a switch or loop statement
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class BreakStatement extends Statement {
+
+    private String label;
+    
+    public BreakStatement() {
+        this(null);
+    }
+    
+    public BreakStatement(String label) {
+        this.label = label;
+    }
+    
+    public String getLabel() {
+        return label;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitBreakStatement(this);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/CaseStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/CaseStatement.java
new file mode 100644
index 0000000..3ff239f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/CaseStatement.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.Expression;
+
+
+/**
+ * Represents a case statement in a switch statement
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class CaseStatement extends Statement {
+
+    private final Statement code;
+    private Expression expression;
+    
+    public CaseStatement(Expression expression, Statement code) {
+        this.expression = expression;
+        this.code = code;
+    }
+    
+    public Statement getCode() {
+        return code;
+    }
+    
+    public Expression getExpression() {
+        return expression;
+    }
+    
+    public void setExpression(Expression e) {
+        expression=e;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitCaseStatement(this);
+    }
+    
+    public String toString() {
+        return super.toString() + "[expression: " + expression + "; code: " + code + "]";
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/CatchStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/CatchStatement.java
new file mode 100644
index 0000000..e7b014a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/CatchStatement.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.Parameter;
+
+
+/**
+ * Represents a catch (Exception var) { } statement
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class CatchStatement extends Statement {
+
+    private Parameter variable;
+    private Statement code;
+    
+    public CatchStatement(Parameter variable, Statement code) {
+        this.variable = variable;
+        this.code = code;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitCatchStatement(this);
+    }
+    
+    public Statement getCode() {
+        return code;
+    }
+
+    public ClassNode getExceptionType() {
+        return variable.getType();
+    }
+
+    public Parameter getVariable() {
+        return variable;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/ContinueStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/ContinueStatement.java
new file mode 100644
index 0000000..a21020d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/ContinueStatement.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+
+/**
+ * Represents a continue statement in a loop statement
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ContinueStatement extends Statement {
+
+    private String label;
+    
+    public ContinueStatement() {
+        this(null);
+    }
+    
+    public ContinueStatement(String label) {
+        this.label = label;
+    }
+    
+    public String getLabel() {
+        return label;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitContinueStatement(this);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/DoWhileStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/DoWhileStatement.java
new file mode 100644
index 0000000..33585d9
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/DoWhileStatement.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+
+/**
+ * Represents a do { ... } while (condition) loop in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DoWhileStatement extends Statement {
+
+    private BooleanExpression booleanExpression;
+    private Statement loopBlock;
+    
+
+    public DoWhileStatement(BooleanExpression booleanExpression, Statement loopBlock) {
+        this.booleanExpression = booleanExpression;
+        this.loopBlock = loopBlock;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitDoWhileLoop(this);
+    }
+    
+    public BooleanExpression getBooleanExpression() {
+        return booleanExpression;
+    }
+
+    public Statement getLoopBlock() {
+        return loopBlock;
+    }
+    public void setBooleanExpression(BooleanExpression booleanExpression) {
+        this.booleanExpression = booleanExpression;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/EmptyStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/EmptyStatement.java
new file mode 100644
index 0000000..83ca0e8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/EmptyStatement.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents an empty statement
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class EmptyStatement extends Statement {
+
+    public static final EmptyStatement INSTANCE = new EmptyStatement();
+    
+    public void visit(GroovyCodeVisitor visitor) {
+    }
+
+    public boolean isEmpty() {
+        return true;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/ExpressionStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/ExpressionStatement.java
new file mode 100644
index 0000000..a71a6fb
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/ExpressionStatement.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.Expression;
+
+
+/**
+ * A simple statement such as a method call where the return value is ignored
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ExpressionStatement extends Statement {
+
+    private Expression expression;
+    
+    public ExpressionStatement(Expression expression) {
+        if (expression == null) {
+            throw new IllegalArgumentException("expression cannot be null");
+        }
+        this.expression = expression;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitExpressionStatement(this);
+    }
+
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public void setExpression(Expression expression) {
+        this.expression = expression;
+    }
+
+    public String getText() {
+    	return this.toString();
+    }
+    public String toString() {
+        return super.toString() + "[expression:" + expression + "]";
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/ForStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/ForStatement.java
new file mode 100644
index 0000000..f21b6ff
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/ForStatement.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.VariableScope;
+import org.codehaus.groovy.ast.expr.Expression;
+
+/**
+ * Represents a standard for loop in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ForStatement extends Statement {
+    public static final Parameter FOR_LOOP_DUMMY = new Parameter(ClassHelper.OBJECT_TYPE,"forLoopDummyParameter");
+
+    private Parameter variable;
+    private Expression collectionExpression;
+    private Statement loopBlock;
+    private VariableScope scope;
+    
+
+    public ForStatement(Parameter variable, Expression collectionExpression, Statement loopBlock) {
+        this.variable = variable; 
+        this.collectionExpression = collectionExpression;
+        this.loopBlock = loopBlock;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitForLoop(this);
+    }
+    
+    public Expression getCollectionExpression() {
+        return collectionExpression;
+    }
+
+    public Statement getLoopBlock() {
+        return loopBlock;
+    }
+
+    public Parameter getVariable() {
+        return variable;
+    }
+    
+    public ClassNode getVariableType() {
+        return variable.getType();
+    }
+    
+    public void setCollectionExpression(Expression collectionExpression) {
+        this.collectionExpression = collectionExpression;
+    }
+
+    public void setVariableScope(VariableScope variableScope) {
+       scope = variableScope;        
+    }
+
+    public VariableScope getVariableScope() {
+        return scope;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/IfStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/IfStatement.java
new file mode 100644
index 0000000..4e00ff3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/IfStatement.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+
+/**
+ * Represents a do { ... } while (condition) loop in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class IfStatement extends Statement {
+
+    private BooleanExpression booleanExpression;
+    private Statement ifBlock;
+    private Statement elseBlock;
+    
+
+    public IfStatement(BooleanExpression booleanExpression, Statement ifBlock, Statement elseBlock) {
+        this.booleanExpression = booleanExpression;
+        this.ifBlock = ifBlock;
+        this.elseBlock = elseBlock;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitIfElse(this);
+    }
+    
+    public BooleanExpression getBooleanExpression() {
+        return booleanExpression;
+    }
+    
+    public Statement getIfBlock() {
+        return ifBlock;
+    }
+
+    public Statement getElseBlock() {
+        return elseBlock;
+    }
+
+    public void setBooleanExpression(BooleanExpression booleanExpression) {
+        this.booleanExpression = booleanExpression;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/ReturnStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/ReturnStatement.java
new file mode 100644
index 0000000..369874f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/ReturnStatement.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+
+/**
+ * A return statement
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ReturnStatement extends Statement {
+
+    public static final ReturnStatement RETURN_NULL_OR_VOID = new ReturnStatement(ConstantExpression.NULL);
+
+    private Expression expression;
+    
+    public ReturnStatement(ExpressionStatement statement) {
+        this(statement.getExpression());
+        setStatementLabel(statement.getStatementLabel());
+    }
+    
+    public ReturnStatement(Expression expression) {
+        this.expression = expression;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitReturnStatement(this);
+    }
+
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public String getText() {
+        return "return " + expression.getText();
+    }
+
+    public void setExpression(Expression expression) {
+        this.expression = expression;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/Statement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/Statement.java
new file mode 100644
index 0000000..87d7abd
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/Statement.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.ASTNode;
+
+/**
+ * Base class for any statement
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class Statement extends ASTNode {
+
+    private String statementLabel;
+
+    public Statement() {
+        statementLabel = null;
+    }
+
+    public String getStatementLabel() {
+        return statementLabel;
+    }
+
+    public void setStatementLabel( String label ) {
+        statementLabel = label;
+    }
+
+    public boolean isEmpty() {
+        return false;
+    }
+    
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/SwitchStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/SwitchStatement.java
new file mode 100644
index 0000000..fb3e7e2
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/SwitchStatement.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.Expression;
+
+/**
+ * Represents a switch (object) { case value: ... case [1, 2, 3]: ...  default: ... } statement in Groovy.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class SwitchStatement extends Statement {
+
+    private Expression expression;
+    private List caseStatements = new ArrayList();
+    private Statement defaultStatement;
+    
+
+    public SwitchStatement(Expression expression) {
+        this(expression, EmptyStatement.INSTANCE);
+    }
+
+    public SwitchStatement(Expression expression, Statement defaultStatement) {
+        this.expression = expression;
+        this.defaultStatement = defaultStatement;
+    }
+
+    public SwitchStatement(Expression expression, List caseStatements, Statement defaultStatement) {
+        this.expression = expression;
+        this.caseStatements = caseStatements;
+        this.defaultStatement = defaultStatement;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitSwitch(this);
+    }
+    
+    public List getCaseStatements() {
+        return caseStatements;
+    }
+
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public void setExpression(Expression e) {
+        expression=e;
+    }
+    
+    public Statement getDefaultStatement() {
+        return defaultStatement;
+    }
+
+    public void setDefaultStatement(Statement defaultStatement) {
+        this.defaultStatement = defaultStatement;
+    }
+
+    public void addCase(CaseStatement caseStatement) {
+        caseStatements.add(caseStatement);
+    }
+
+    /**
+     * @return the case statement of the given index or null
+     */
+    public CaseStatement getCaseStatement(int idx) {
+        if (idx >= 0 && idx < caseStatements.size()) {
+            return (CaseStatement) caseStatements.get(idx);
+        }
+        return null;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/SynchronizedStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/SynchronizedStatement.java
new file mode 100644
index 0000000..57225d8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/SynchronizedStatement.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.Expression;
+
+
+/**
+ * Represents a synchronized statement
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class SynchronizedStatement extends Statement {
+
+    private Statement code;
+    private Expression expression;
+    
+    public SynchronizedStatement(Expression expression, Statement code) {
+        this.expression = expression;
+        this.code = code;
+    }
+    
+    public Statement getCode() {
+        return code;
+    }
+    
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitSynchronizedStatement(this);
+    }
+    public void setExpression(Expression expression) {
+        this.expression = expression;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/ThrowStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/ThrowStatement.java
new file mode 100644
index 0000000..aa32dd6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/ThrowStatement.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.Expression;
+
+
+/**
+ * Represents a throw statement
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ThrowStatement extends Statement {
+
+    private Expression expression;
+    
+    public ThrowStatement(Expression expression) {
+        this.expression = expression;
+    }
+    
+    public Expression getExpression() {
+        return expression;
+    }
+
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitThrowStatement(this);
+    }
+    public void setExpression(Expression expression) {
+        this.expression = expression;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/TryCatchStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/TryCatchStatement.java
new file mode 100644
index 0000000..4cdf0b0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/TryCatchStatement.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+
+/**
+ * Represents a try { ... } catch () finally {} statement in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TryCatchStatement extends Statement {
+
+    private Statement tryStatement;
+    private List catchStatements = new ArrayList();
+    private Statement finallyStatement;
+    
+
+    public TryCatchStatement(Statement tryStatement, Statement finallyStatement) {
+        this.tryStatement = tryStatement;
+        this.finallyStatement = finallyStatement;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitTryCatchFinally(this);
+    }
+    
+    public List getCatchStatements() {
+        return catchStatements;
+    }
+
+    public Statement getFinallyStatement() {
+        return finallyStatement;
+    }
+
+    public Statement getTryStatement() {
+        return tryStatement;
+    }
+
+    public void addCatch(CatchStatement catchStatement) {
+        catchStatements.add(catchStatement);
+    }
+
+    /**
+     * @return the catch statement of the given index or null
+     */
+    public CatchStatement getCatchStatement(int idx) {
+        if (idx >= 0 && idx < catchStatements.size()) {
+            return (CatchStatement) catchStatements.get(idx);
+        }
+        return null;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/WhileStatement.java b/groovy/src/main/org/codehaus/groovy/ast/stmt/WhileStatement.java
new file mode 100644
index 0000000..163a5d0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/WhileStatement.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.ast.stmt;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+
+/**
+ * Represents a while (condition) { ... } loop in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class WhileStatement extends Statement {
+
+    private BooleanExpression booleanExpression;
+    private Statement loopBlock;
+    
+
+    public WhileStatement(BooleanExpression booleanExpression, Statement loopBlock) {
+        this.booleanExpression = booleanExpression;
+        this.loopBlock = loopBlock;
+    }
+    
+    public void visit(GroovyCodeVisitor visitor) {
+        visitor.visitWhileLoop(this);
+    }
+    
+    public BooleanExpression getBooleanExpression() {
+        return booleanExpression;
+    }
+
+    public Statement getLoopBlock() {
+        return loopBlock;
+    }
+
+	public void setBooleanExpression(BooleanExpression booleanExpression) {
+		this.booleanExpression = booleanExpression;
+	}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/ast/stmt/package.html b/groovy/src/main/org/codehaus/groovy/ast/stmt/package.html
new file mode 100644
index 0000000..95a6e74
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/ast/stmt/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.ast.stmt.*</title>
+  </head>
+  <body>
+    <p>AST nodes for Groovy statements</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/binding/AbstractFullBinding.java b/groovy/src/main/org/codehaus/groovy/binding/AbstractFullBinding.java
new file mode 100644
index 0000000..5a7c53c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/AbstractFullBinding.java
@@ -0,0 +1,112 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+import groovy.lang.Closure;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public abstract class AbstractFullBinding  implements FullBinding {

+

+    protected SourceBinding sourceBinding;

+    protected TargetBinding targetBinding;

+    protected Closure validator;

+    protected Closure converter;

+    protected Closure reverseConverter;

+

+    private void fireBinding() {

+        if ((sourceBinding == null) || (targetBinding == null)) {

+            // should we throw invalid binding exception?  or fail quietly?

+            return;

+        }

+        Object result = sourceBinding.getSourceValue();

+        if (getValidator() != null) {

+            Object validation = getValidator().call();

+            if ((validation == null)

+                || ((validation instanceof Boolean) && !((Boolean)validation).booleanValue()))

+            {

+                // should we throw a validation failed exception?  or fail quietly?

+                return;

+            }

+        }

+        if (getConverter() != null) {

+            result = getConverter().call(result);

+        }

+        targetBinding.updateTargetValue(result);

+    }

+

+    public void update() {

+        fireBinding();

+    }

+

+    private void fireReverseBinding() {

+        if (!(sourceBinding instanceof TargetBinding) || !(targetBinding instanceof SourceBinding)) {

+            throw new RuntimeException("Binding Instance is not reversable");

+        }

+        Object result = ((SourceBinding)targetBinding).getSourceValue();

+        if (getReverseConverter() != null) {

+            result = getReverseConverter().call(result);

+        }

+        ((TargetBinding)sourceBinding).updateTargetValue(result);

+    }

+

+    public void reverseUpdate() {

+        fireReverseBinding();

+    }

+

+    public SourceBinding getSourceBinding() {

+        return sourceBinding;

+    }

+

+    public void setSourceBinding(SourceBinding sourceBinding) {

+        this.sourceBinding = sourceBinding;

+    }

+

+    public TargetBinding getTargetBinding() {

+        return targetBinding;

+    }

+

+    public void setTargetBinding(TargetBinding targetBinding) {

+        this.targetBinding = targetBinding;

+    }

+

+    public Closure getValidator() {

+        return validator;

+    }

+

+    public void setValidator(Closure validator) {

+        this.validator = validator;

+    }

+

+    public Closure getConverter() {

+        return converter;

+    }

+

+    public void setConverter(Closure converter) {

+        this.converter = converter;

+    }

+

+    public Closure getReverseConverter() {

+        return reverseConverter;

+    }

+

+    public void setReverseConverter(Closure reverseConverter) {

+        this.reverseConverter = reverseConverter;

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/binding/BindingUpdatable.java b/groovy/src/main/org/codehaus/groovy/binding/BindingUpdatable.java
new file mode 100644
index 0000000..a6ddc4a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/BindingUpdatable.java
@@ -0,0 +1,56 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public interface BindingUpdatable {

+

+    /**

+     * Causes automatic updating of bound values to be turned on.

+     * This is idempotent between calls to unbind and rebind; i.e. multiple calls

+     * to bind will have only the effect of the first call.

+     */

+    void bind();

+

+    /**

+     * Causes automatic updating of bound values to be turned off.

+     * This is idempotent between calls to bind and rebind; i.e. multiple calls

+     * to unbind will have only the effect of the first call. 

+     */

+    void unbind();

+

+    /**

+     * Causes the current bindings to be reset.

+     * If the binding is not bound, it is a no-op.

+     * If the binding is bound, it will be turned off, then turned on against current values.

+     */

+    void rebind();

+

+    /**

+     * Causes the values to be propigated from the source to the target

+     */

+    void update();

+

+    /**

+     * If supported, Causes the values to be propigated from the target to the source,

+     * If not supported, an exception may be thrown 

+     */

+    void reverseUpdate();

+}

diff --git a/groovy/src/main/org/codehaus/groovy/binding/ClosureSourceBinding.java b/groovy/src/main/org/codehaus/groovy/binding/ClosureSourceBinding.java
new file mode 100644
index 0000000..3d9412b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/ClosureSourceBinding.java
@@ -0,0 +1,58 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+import groovy.lang.Closure;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public class ClosureSourceBinding implements SourceBinding {

+

+    Closure closure;

+    Object[] arguments;

+

+    public ClosureSourceBinding(Closure closure) {

+        this(closure, new Object[0]);

+    }

+

+    public ClosureSourceBinding(Closure closure, Object[] arguments) { //TODO in Groovy 2.0 use varargs?

+        this.closure = closure;

+        this.arguments = arguments;

+    }

+

+    public Closure getClosure() {

+        return closure;

+    }

+

+    public void setClosure(Closure closure) {

+        this.closure = closure;

+    }

+

+    public Object getSourceValue() {

+        return closure.call(arguments);

+    }

+

+    public void setClosureArguments(Object[] arguments) {

+        this.arguments = arguments;

+    }

+

+    public void setClosureArgument(Object argument) {

+        this.arguments = new Object[] {argument};

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/binding/EventTriggerBinding.java b/groovy/src/main/org/codehaus/groovy/binding/EventTriggerBinding.java
new file mode 100644
index 0000000..e5b4d2b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/EventTriggerBinding.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+import groovy.lang.Closure;

+import org.codehaus.groovy.runtime.InvokerHelper;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public class EventTriggerBinding implements TriggerBinding {

+

+    Object triggerBean;

+    String eventName;

+

+    public EventTriggerBinding(Object triggerBean, String eventName) {

+        this.triggerBean = triggerBean;

+        this.eventName = eventName;

+    }

+

+    public FullBinding createBinding(final SourceBinding sourceBinding, final TargetBinding targetBinding) {

+        return new EventTriggerFullBinding(sourceBinding, targetBinding);

+    }

+

+    public Object getTriggerBean() {

+        return triggerBean;

+    }

+

+    public void setTriggerBean(Object triggerBean) {

+        this.triggerBean = triggerBean;

+    }

+

+    public String getEventName() {

+        return eventName;

+    }

+

+    public void setEventName(String eventName) {

+        this.eventName = eventName;

+    }

+

+    private class EventTriggerFullBinding extends AbstractFullBinding {

+

+        Closure handler;

+

+        public EventTriggerFullBinding(SourceBinding sourceBinding, TargetBinding targetBinding) {

+            setSourceBinding(sourceBinding);

+            setTargetBinding(targetBinding);

+            handler = new Closure(triggerBean) {

+                public Object call(Object[] params) {

+                    update();

+                    return null;

+                }

+            };

+        }

+

+        public void bind() {

+            InvokerHelper.setProperty(triggerBean, eventName, handler);

+        }

+

+        public void unbind() {

+            throw new UnsupportedOperationException();

+        }

+

+        public void rebind() {

+            throw new UnsupportedOperationException();

+        }

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/binding/FullBinding.java b/groovy/src/main/org/codehaus/groovy/binding/FullBinding.java
new file mode 100644
index 0000000..0d2145f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/FullBinding.java
@@ -0,0 +1,46 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+import groovy.lang.Closure;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public interface FullBinding extends BindingUpdatable {

+

+    SourceBinding getSourceBinding();

+

+    TargetBinding getTargetBinding();

+

+    void setSourceBinding(SourceBinding source);

+

+    void setTargetBinding(TargetBinding target);

+

+    void setValidator(Closure validator);

+

+    Closure getValidator();

+

+    void setConverter(Closure converter);

+

+    Closure getConverter();

+

+    void setReverseConverter(Closure reverseConverter);

+

+    Closure getReverseConverter();

+}

diff --git a/groovy/src/main/org/codehaus/groovy/binding/ModelBinding.java b/groovy/src/main/org/codehaus/groovy/binding/ModelBinding.java
new file mode 100644
index 0000000..334a1e6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/ModelBinding.java
@@ -0,0 +1,155 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+import groovy.lang.GroovyObjectSupport;

+import groovy.lang.ReadOnlyPropertyException;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public class ModelBinding extends GroovyObjectSupport implements BindingUpdatable {

+

+    Object model;

+    boolean bound;

+

+    final Map/*<String, PropertyBinding>*/ propertyBindings = new HashMap/*<String, PropertyBinding>*/();

+    final List/*<FullBinding>*/ generatedBindings = new ArrayList/*<FullBinding>*/();

+

+    public ModelBinding(Object model) {

+        this.model = model;

+    }

+

+    public Object getModel() {

+        return model;

+    }

+

+    public synchronized void setModel(Object model) {

+        // should we use a finer grained lock than this?

+

+        this.model = model;

+        //TODO see if bound, mark if so

+        unbind();

+        Iterator iter = propertyBindings.values().iterator();

+        while (iter.hasNext()) {

+            ((PropertyBinding)iter.next()).setBean(model);

+        }

+

+        rebind();

+        update();

+    }

+

+    public Object getProperty(String property) {

+        PropertyBinding pb;

+        synchronized (propertyBindings) {

+            // should we verify the property is valid?

+            Object o = propertyBindings.get(property);

+            if (o == null) {

+                o = new ModelBindingPropertyBinding(model, property);

+                propertyBindings.put(property, o);

+            }

+            pb = (PropertyBinding)o;

+        }

+        FullBinding fb = pb.createBinding(pb, null);

+        if (bound) {

+            fb.bind();

+        }

+        return fb;

+    }

+

+    public void setProperty(String property, Object value) {

+        throw new ReadOnlyPropertyException(property, model.getClass());

+    }

+

+    public void bind() {

+        synchronized (generatedBindings) {

+            if (!bound) {

+                bound = true;

+                Iterator iter = generatedBindings.iterator();

+                while (iter.hasNext()) {

+                    ((FullBinding)iter.next()).bind();

+                    // should we trap exceptions and do an each?

+                }

+            }

+        }

+    }

+

+    public void unbind() {

+        synchronized (generatedBindings) {

+            if (bound) {

+                bound = false;

+                Iterator iter = generatedBindings.iterator();

+                while (iter.hasNext()) {

+                    ((FullBinding)iter.next()).unbind();

+                    // should we trap exceptions and do an each?

+                }

+            }

+        }

+    }

+

+    public void rebind() {

+        synchronized (generatedBindings) {

+            if (bound) {

+                Iterator iter = generatedBindings.iterator();

+                while (iter.hasNext()) {

+                    ((FullBinding)iter.next()).rebind();

+                    // should we trap exceptions and do an each?

+                }

+            }

+        }

+    }

+

+    public void update() {

+        synchronized (generatedBindings) {

+            Iterator iter = generatedBindings.iterator();

+            while (iter.hasNext()) {

+                ((FullBinding)iter.next()).update();

+                // should we trap exceptions and do an each?

+            }

+        }

+    }

+

+    public void reverseUpdate() {

+        synchronized (generatedBindings) {

+            Iterator iter = generatedBindings.iterator();

+            while (iter.hasNext()) {

+                ((FullBinding)iter.next()).reverseUpdate();

+                // should we trap exceptions and do an each?

+            }

+        }

+    }

+

+    class ModelBindingPropertyBinding extends PropertyBinding {

+        public ModelBindingPropertyBinding(Object bean, String propertyName) {

+            super(bean, propertyName);

+        }

+

+        public FullBinding createBinding(SourceBinding source, TargetBinding target) {

+            FullBinding fb = super.createBinding(source, target);

+            generatedBindings.add(fb);

+            return fb;

+        }

+    }

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/binding/PropertyBinding.java b/groovy/src/main/org/codehaus/groovy/binding/PropertyBinding.java
new file mode 100644
index 0000000..b2020f4
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/PropertyBinding.java
@@ -0,0 +1,137 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+import groovy.lang.MissingMethodException;

+import org.codehaus.groovy.runtime.InvokerHelper;

+

+import java.beans.PropertyChangeEvent;

+import java.beans.PropertyChangeListener;

+

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public class PropertyBinding implements SourceBinding, TargetBinding, TriggerBinding {

+

+    Object bean;

+    String propertyName;

+

+    public PropertyBinding(Object bean, String propertyName) {

+        this.bean = bean;

+        this.propertyName = propertyName;

+    }

+

+    public void updateTargetValue(Object newValue) {

+        InvokerHelper.setProperty(bean, propertyName, newValue);

+    }

+

+    public Object getSourceValue() {

+        return InvokerHelper.getPropertySafe(bean, propertyName);

+    }

+

+    public FullBinding createBinding(SourceBinding source, TargetBinding target) {

+        return new PropertyFullBinding

+                (source, target);

+    }

+

+    class PropertyFullBinding extends AbstractFullBinding implements PropertyChangeListener {

+

+        Object boundBean;

+        Object boundProperty;

+        boolean bound;

+        boolean boundToProperty;

+

+        PropertyFullBinding(SourceBinding source, TargetBinding target) {

+            setSourceBinding(source);

+            setTargetBinding(target);

+        }

+

+        public void propertyChange(PropertyChangeEvent event) {

+            if (boundToProperty || event.getPropertyName().equals(boundProperty)) {

+                update();

+            }

+        }

+

+        public void bind() {

+            if (!bound) {

+                bound = true;

+                boundBean = bean;

+                boundProperty = propertyName;

+                try {

+                    InvokerHelper.invokeMethodSafe(boundBean, "addPropertyChangeListener", new Object[] {boundProperty, this});

+                    boundToProperty = true;

+                } catch (MissingMethodException mme) {

+                    try {

+                        boundToProperty = false;

+                        InvokerHelper.invokeMethodSafe(boundBean, "addPropertyChangeListener", new Object[] {this});

+                    } catch (MissingMethodException mme2) {

+                        throw new RuntimeException("Properties in beans of type " + bean.getClass().getName() + " are not observable in any capacity (no PropertyChangeListener support).");

+                    }

+                }

+            }

+        }

+

+        public void unbind() {

+            if (bound) {

+                if (boundToProperty) {

+                    try {

+                        InvokerHelper.invokeMethodSafe(boundBean, "removePropertyChangeListener", new Object[] {boundProperty, this});

+                    } catch (MissingMethodException mme) {

+                        // ignore, too bad so sad they don't follow conventions, we'll just leave the listener attached

+                    }

+                } else {

+                    try {

+                        InvokerHelper.invokeMethodSafe(boundBean, "removePropertyChangeListener", new Object[] {this});

+                    } catch (MissingMethodException mme2) {

+                        // ignore, too bad so sad they don't follow conventions, we'll just leave the listener attached

+                    }

+                }

+                boundBean = null;

+                boundProperty = null;

+                bound = false;

+            }

+        }

+

+        public void rebind() {

+            if (bound) {

+                unbind();

+                bind();

+            }

+        }

+

+    }

+

+    public Object getBean() {

+        return bean;

+    }

+

+    public void setBean(Object bean) {

+        this.bean = bean;

+    }

+

+    public String getPropertyName() {

+        return propertyName;

+    }

+

+    public void setPropertyName(String propertyName) {

+        this.propertyName = propertyName;

+    }

+

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/binding/SourceBinding.java b/groovy/src/main/org/codehaus/groovy/binding/SourceBinding.java
new file mode 100644
index 0000000..d024c43
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/SourceBinding.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public interface SourceBinding {

+

+    Object getSourceValue();

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/binding/SwingTimerTriggerBinding.java b/groovy/src/main/org/codehaus/groovy/binding/SwingTimerTriggerBinding.java
new file mode 100644
index 0000000..9e75d17
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/SwingTimerTriggerBinding.java
@@ -0,0 +1,183 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+import javax.swing.Timer;

+import java.awt.event.ActionListener;

+import java.awt.event.ActionEvent;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision: 7797 $

+ * @since Groovy 1.1

+ */

+public class SwingTimerTriggerBinding implements TriggerBinding {

+    public FullBinding createBinding(SourceBinding source, TargetBinding target) {

+        return new SwingTimerFullBinding((ClosureSourceBinding) source, target);

+    }

+}

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision: 7797 $

+ * @since Groovy 1.1

+ */

+class SwingTimerFullBinding extends AbstractFullBinding implements ActionListener {

+    Timer timer;

+    long startTime;

+    long duration;

+    int stepSize;

+

+    boolean reportSteps;

+    boolean reportFraction;

+    boolean reportElapsed;

+    boolean repeat;

+    boolean bound;

+

+    SwingTimerFullBinding(ClosureSourceBinding source, TargetBinding target) {

+        this(source, target, 50, 1000);

+    }

+

+    SwingTimerFullBinding(SourceBinding source, TargetBinding target, int interval, int duration) {

+        setSourceBinding(source);

+        setTargetBinding(target);

+        timer = new Timer(interval, this);

+        timer.setInitialDelay(0);

+        timer.setRepeats(true);

+        this.duration = duration;

+    }

+

+    void resetTimer() {

+        timer.stop();

+        startTime = System.currentTimeMillis();

+        timer.start();

+    }

+

+    public void bind() {

+        if (!bound) {

+            resetTimer();

+            bound = true;

+        }

+    }

+

+    public void unbind() {

+        if (bound) {

+            timer.stop();

+            bound = false;

+        }

+    }

+

+    public void rebind() {

+        if (bound) {

+            resetTimer();

+        }

+    }

+

+    public void actionPerformed(ActionEvent e) {

+        long currentTime = System.currentTimeMillis();

+        long elapsed = currentTime - startTime;

+        if (elapsed >= duration) {

+            if (repeat) {

+                startTime = currentTime;

+            } else {

+                timer.stop();

+            }

+            // no over-runs...

+            elapsed = duration;

+        }

+

+        // calculate

+        if (reportSteps) {

+            ((ClosureSourceBinding)sourceBinding).setClosureArgument(

+                    new Integer((int) (elapsed / stepSize)));

+              //in Groovy2.0 use valueOf

+        } else if (reportFraction) {

+            ((ClosureSourceBinding)sourceBinding).setClosureArgument(

+                    new Float((float) elapsed / (float) duration));

+            //in Groovy2.0 use valueOf

+        } else if (reportElapsed) {

+            ((ClosureSourceBinding)sourceBinding).setClosureArgument(

+                    new Long(elapsed));

+            //in Groovy2.0 use valueOf

+        } 

+

+        update();

+    }

+

+    public long getDuration() {

+        return duration;

+    }

+

+    public void setDuration(long duration) {

+        this.duration = duration;

+    }

+

+    public int getInterval() {

+        return timer.getDelay();

+    }

+

+    public void setInterval(int interval) {

+        timer.setDelay(interval);

+    }

+

+    public int getStepSize() {

+        return stepSize;

+    }

+

+    public void setStepSize(int stepSize) {

+        this.stepSize = stepSize;

+    }

+

+    public boolean isCoalesce() {

+        return timer.isCoalesce();

+    }

+

+    public void setCoalesce(boolean coalesce) {

+        timer.setCoalesce(coalesce);

+    }

+

+    public boolean isReportSteps() {

+        return reportSteps;

+    }

+

+    public void setReportSteps(boolean reportSteps) {

+        this.reportSteps = reportSteps;

+    }

+

+    public boolean isReportFraction() {

+        return reportFraction;

+    }

+

+    public void setReportFraction(boolean reportFraction) {

+        this.reportFraction = reportFraction;

+    }

+

+    public boolean isReportElapsed() {

+        return reportElapsed;

+    }

+

+    public void setReportElapsed(boolean reportElapsed) {

+        this.reportElapsed = reportElapsed;

+    }

+

+    public boolean isRepeat() {

+        return repeat;

+    }

+

+    public void setRepeat(boolean repeat) {

+        this.repeat = repeat;

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/binding/TargetBinding.java b/groovy/src/main/org/codehaus/groovy/binding/TargetBinding.java
new file mode 100644
index 0000000..9abea5c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/TargetBinding.java
@@ -0,0 +1,26 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public interface TargetBinding {

+    void updateTargetValue(Object value);

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/binding/TriggerBinding.java b/groovy/src/main/org/codehaus/groovy/binding/TriggerBinding.java
new file mode 100644
index 0000000..a3ad5fa
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/TriggerBinding.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.binding;

+

+/**

+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>

+ * @version $Revision$

+ * @since Groovy 1.1

+ */

+public interface TriggerBinding {

+

+    FullBinding createBinding(SourceBinding source, TargetBinding target);

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/binding/package.html b/groovy/src/main/org/codehaus/groovy/binding/package.html
new file mode 100644
index 0000000..e7145de
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/binding/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.binding.*</title>
+  </head>
+  <body>
+    <p>Classes related to property binding.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/bsf/CachingGroovyEngine.java b/groovy/src/main/org/codehaus/groovy/bsf/CachingGroovyEngine.java
new file mode 100644
index 0000000..2496e71
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/bsf/CachingGroovyEngine.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.bsf;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyShell;
+import groovy.lang.Script;
+import org.apache.bsf.BSFDeclaredBean;
+import org.apache.bsf.BSFException;
+import org.apache.bsf.BSFManager;
+import org.apache.bsf.util.BSFFunctions;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.io.ByteArrayInputStream;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+import java.util.logging.Logger;
+import java.util.logging.Level;
+
+/**
+ * A Caching implementation of the GroovyEngine
+ *
+ * @author James Birchfield
+ */
+public class CachingGroovyEngine extends GroovyEngine {
+    private static final Logger LOG = Logger.getLogger(CachingGroovyEngine.class.getName());
+    private static final Object[] EMPTY_ARGS = new Object[]{new String[]{}};
+
+    private Map evalScripts;
+    private Map execScripts;
+    private Binding context;
+    private GroovyClassLoader loader;
+
+    /**
+     * Evaluate an expression.
+     */
+    public Object eval(String source, int lineNo, int columnNo, Object script) throws BSFException {
+        try {
+            Class scriptClass = (Class) evalScripts.get(script);
+            if (scriptClass == null) {
+                scriptClass = loader.parseClass(new ByteArrayInputStream(script.toString().getBytes()), source);
+                evalScripts.put(script, scriptClass);
+            } else {
+                LOG.fine("eval() - Using cached script...");
+            }
+            //can't cache the script because the context may be different.
+            //but don't bother loading parsing the class again
+            Script s = InvokerHelper.createScript(scriptClass, context);
+            return s.run();
+        } catch (Exception e) {
+            throw new BSFException(BSFException.REASON_EXECUTION_ERROR, "exception from Groovy: " + e, e);
+        }
+    }
+
+    /**
+     * Execute a script.
+     */
+    public void exec(String source, int lineNo, int columnNo, Object script) throws BSFException {
+        try {
+            //          shell.run(script.toString(), source, EMPTY_ARGS);
+
+            Class scriptClass = (Class) execScripts.get(script);
+            if (scriptClass == null) {
+                scriptClass = loader.parseClass(new ByteArrayInputStream(script.toString().getBytes()), source);
+                execScripts.put(script, scriptClass);
+            } else {
+                LOG.fine("exec() - Using cached version of class...");
+            }
+            InvokerHelper.invokeMethod(scriptClass, "main", EMPTY_ARGS);
+        } catch (Exception e) {
+            LOG.log(Level.WARNING, "BSF trace", e);
+            throw new BSFException(BSFException.REASON_EXECUTION_ERROR, "exception from Groovy: " + e, e);
+        }
+    }
+
+    /**
+     * Initialize the engine.
+     */
+    public void initialize(final BSFManager mgr, String lang, Vector declaredBeans) throws BSFException {
+        super.initialize(mgr, lang, declaredBeans);
+        ClassLoader parent = mgr.getClassLoader();
+        if (parent == null)
+            parent = GroovyShell.class.getClassLoader();
+        final ClassLoader finalParent = parent;
+        this.loader =
+                (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+                    public Object run() {
+                        CompilerConfiguration configuration = new CompilerConfiguration();
+                        configuration.setClasspath(mgr.getClassPath());
+                        return new GroovyClassLoader(finalParent, configuration);
+                    }
+                });
+        execScripts = new HashMap();
+        evalScripts = new HashMap();
+        context = shell.getContext();
+        // create a shell
+        // register the mgr with object name "bsf"
+        context.setVariable("bsf", new BSFFunctions(mgr, this));
+        int size = declaredBeans.size();
+        for (int i = 0; i < size; i++) {
+            declareBean((BSFDeclaredBean) declaredBeans.elementAt(i));
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/bsf/GroovyEngine.java b/groovy/src/main/org/codehaus/groovy/bsf/GroovyEngine.java
new file mode 100644
index 0000000..6fa76e0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/bsf/GroovyEngine.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.bsf;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyShell;
+import org.apache.bsf.BSFDeclaredBean;
+import org.apache.bsf.BSFException;
+import org.apache.bsf.BSFManager;
+import org.apache.bsf.util.BSFEngineImpl;
+import org.apache.bsf.util.BSFFunctions;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.util.Vector;
+
+/**
+ * A BSF Engine for the <a href="http://groovy.codehaus.org/">Groovy</a>
+ * scripting language.
+ * <p/>
+ * It's derived from the Jython / JPython engine
+ *
+ * @author James Strachan
+ */
+public class GroovyEngine extends BSFEngineImpl {
+    protected GroovyShell shell;
+
+    /*
+     * Convert a non java class name to a java classname
+     * This is used to convert a script name to a name
+     * that can be used as a classname with the script is
+     * loaded in GroovyClassloader#load()
+     * The method simply replaces any invalid characters
+     * with "_".
+     */
+    private String convertToValidJavaClassname(String inName) {
+        if (inName == null || inName.equals("")) {
+            return "_";
+        }
+        StringBuffer output = new StringBuffer(inName.length());
+        boolean firstChar = true;
+        for (int i = 0; i < inName.length(); ++i) {
+            char ch = inName.charAt(i);
+            if (firstChar && !Character.isJavaIdentifierStart(ch)) {
+                ch = '_';
+            } else if (!firstChar
+                    && !(Character.isJavaIdentifierPart(ch) || ch == '.')) {
+                ch = '_';
+            }
+            firstChar = (ch == '.');
+            output.append(ch);
+        }
+        return output.toString();
+    }
+
+    /**
+     * Allow an anonymous function to be declared and invoked
+     */
+    public Object apply(String source, int lineNo, int columnNo, Object funcBody, Vector paramNames,
+                        Vector arguments) throws BSFException {
+        Object object = eval(source, lineNo, columnNo, funcBody);
+        if (object instanceof Closure) {
+            // lets call the function
+            Closure closure = (Closure) object;
+            return closure.call(arguments.toArray());
+        }
+        return object;
+    }
+
+    /**
+     * Call the named method of the given object.
+     */
+    public Object call(Object object, String method, Object[] args) throws BSFException {
+        return InvokerHelper.invokeMethod(object, method, args);
+    }
+
+    /**
+     * Evaluate an expression.
+     */
+    public Object eval(String source, int lineNo, int columnNo, Object script) throws BSFException {
+        try {
+            source = convertToValidJavaClassname(source);
+            return getEvalShell().evaluate(script.toString(), source);
+        } catch (Exception e) {
+            throw new BSFException(BSFException.REASON_EXECUTION_ERROR, "exception from Groovy: " + e, e);
+        }
+    }
+
+    /**
+     * Execute a script.
+     */
+    public void exec(String source, int lineNo, int columnNo, Object script) throws BSFException {
+        try {
+            // use evaluate to pass in the BSF variables
+            source = convertToValidJavaClassname(source);
+            getEvalShell().evaluate(script.toString(), source);
+        } catch (Exception e) {
+            throw new BSFException(BSFException.REASON_EXECUTION_ERROR, "exception from Groovy: " + e, e);
+        }
+    }
+
+    /**
+     * Initialize the engine.
+     */
+    public void initialize(BSFManager mgr, String lang, Vector declaredBeans) throws BSFException {
+        super.initialize(mgr, lang, declaredBeans);
+
+        // create a shell
+        shell = new GroovyShell(mgr.getClassLoader());
+
+        // register the mgr with object name "bsf"
+        shell.setVariable("bsf", new BSFFunctions(mgr, this));
+
+        int size = declaredBeans.size();
+        for (int i = 0; i < size; i++) {
+            declareBean((BSFDeclaredBean) declaredBeans.elementAt(i));
+        }
+    }
+
+    /**
+     * Declare a bean
+     */
+    public void declareBean(BSFDeclaredBean bean) throws BSFException {
+        shell.setVariable(bean.name, bean.bean);
+    }
+
+    /**
+     * Undeclare a previously declared bean.
+     */
+    public void undeclareBean(BSFDeclaredBean bean) throws BSFException {
+        shell.setVariable(bean.name, null);
+    }
+
+    /**
+     * @return a newly created GroovyShell using the same variable scope but a new class loader
+     */
+    protected GroovyShell getEvalShell() {
+        return new GroovyShell(shell);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/bsf/package.html b/groovy/src/main/org/codehaus/groovy/bsf/package.html
new file mode 100644
index 0000000..feb0a25
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/bsf/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.bsf.*</title>
+  </head>
+  <body>
+    <p>Defines the BSF Engine for using Groovy inside any BSF application.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/AnnotationVisitor.java b/groovy/src/main/org/codehaus/groovy/classgen/AnnotationVisitor.java
new file mode 100644
index 0000000..f97782f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/AnnotationVisitor.java
@@ -0,0 +1,336 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.classgen;

+

+import java.lang.reflect.Method;

+import java.util.HashMap;

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+

+import org.codehaus.groovy.ast.ASTNode;

+import org.codehaus.groovy.ast.AnnotationNode;

+import org.codehaus.groovy.ast.ClassHelper;

+import org.codehaus.groovy.ast.ClassNode;

+import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;

+import org.codehaus.groovy.ast.expr.ConstantExpression;

+import org.codehaus.groovy.ast.expr.Expression;

+import org.codehaus.groovy.ast.expr.ListExpression;

+import org.codehaus.groovy.ast.expr.PropertyExpression;

+import org.codehaus.groovy.control.ErrorCollector;

+import org.codehaus.groovy.control.SourceUnit;

+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;

+import org.codehaus.groovy.syntax.SyntaxException;

+

+

+/**

+ * An Annotation visitor responsible with:

+ * - reading annotation metadata (@Retention, @Target, attribute types)

+ * - verify that an <code>AnnotationNode</code> conforms to annotation meta

+ * - enhancing an <code>AnnotationNode</code> AST to reflect real annotation meta

+ * 

+ * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>

+ */

+public class AnnotationVisitor {

+    private static final Class[] EMPTY_ARG_TYPES = new Class[0];

+    private static final Object[] EMPTY_ARGS = new Object[0];

+    

+    private final Class annotationRootClass;

+    private SourceUnit source;

+    private ErrorCollector errorCollector;

+    

+    private AnnotationNode annotation;

+    private Class annotationClass;

+    private Map requiredAttrTypes = new HashMap(); // Map<String, Class>

+    private Map defaultAttrTypes = new HashMap();  // Map<String, Class>

+    

+    public AnnotationVisitor(SourceUnit source, ErrorCollector errorCollector) {

+        this.source = source;

+        this.errorCollector = errorCollector;

+        this.annotationRootClass = loadAnnotationRootClass();

+    }

+    

+    public AnnotationNode visit(AnnotationNode node) {

+        if(!isValidAnnotationClass(node)) {

+            node.setValid(false);

+            return node;

+        }

+        

+        this.annotation = node;

+        if(!node.getClassNode().isResolved()) {

+            addError("Current type was not yet resolved. Cannot introspect it.");

+            node.setValid(false);

+            return node;

+        }

+        this.annotationClass = node.getClassNode().getTypeClass();

+        

+        extractAnnotationMeta(this.annotationClass);

+        

+        if(this.errorCollector.hasErrors()) {

+            this.annotation.setValid(false);

+            return this.annotation;

+        }

+        

+        Map attributes = this.annotation.getMembers();

+        for(Iterator it = attributes.entrySet().iterator(); it.hasNext(); ) {

+            Map.Entry entry = (Map.Entry) it.next();

+            String attrName = (String) entry.getKey();

+            Expression attrExpr = (Expression) entry.getValue();

+            Class attrType = getAttributeType(attrName);

+            if(attrType == null) {

+                addError("Unknown attribute '" + attrName + "'", attrExpr);

+                break;

+            }

+            visitExpression(attrName, attrExpr, attrType);

+        }

+        

+        if(!this.requiredAttrTypes.isEmpty()) {

+            addError("Required attributes " + this.requiredAttrTypes.keySet() + " not found",

+                    this.annotation);

+        }

+        

+        this.annotation.setValid(!this.errorCollector.hasErrors());

+        return this.annotation;

+    }

+    

+    /**

+     * @param node

+     * @return

+     */

+    private boolean isValidAnnotationClass(AnnotationNode node) {

+        return node.getClassNode().implementsInterface("java.lang.annotation.Annotation");

+    }

+

+    protected void visitExpression(String attrName, Expression attrAst, Class attrType) {

+        if(attrType.isArray()) {

+            // check needed as @Test(attr = {"elem"}) passes through the parser

+            if(attrAst instanceof ListExpression) {

+                visitListExpression(attrName, (ListExpression) attrAst, attrType.getComponentType());

+            }

+            else {

+                addError("Annotation list attributes must use Groovy notation [el1, el2]", attrAst);

+            }

+        }

+        if(attrType.isPrimitive()) {

+            visitConstantExpression(attrName, (ConstantExpression) attrAst, ClassHelper.getWrapper(ClassHelper.make(attrType)));

+        }

+        else if(String.class.equals(attrType)) {

+            visitConstantExpression(attrName, (ConstantExpression) attrAst, ClassHelper.make(String.class));

+        }

+        else if(Class.class.equals(attrType)) {

+            // there is nothing to check about ClassExpressions

+        }

+        else if(isEnum(attrType)) {

+            if(attrAst instanceof PropertyExpression) {

+                visitEnumExpression(attrName, (PropertyExpression) attrAst, ClassHelper.make(attrType));

+            }

+            else {

+                addError("Value not defined for annotation attribute " + attrName, attrAst);

+            }

+        }

+        else if(isAnnotation(attrType)) {

+            visitAnnotationExpression(attrName, (AnnotationConstantExpression) attrAst, attrType);

+        }

+    }

+    

+    /**

+     * @param attrName

+     * @param expression

+     * @param attrType

+     */

+    protected void visitAnnotationExpression(String attrName, AnnotationConstantExpression expression, Class attrType) {

+        AnnotationNode annotationNode = (AnnotationNode) expression.getValue();

+        AnnotationVisitor visitor = new AnnotationVisitor(this.source, this.errorCollector);

+        visitor.visit(annotationNode);

+    }

+

+    protected void visitListExpression(String attrName, ListExpression listExpr, Class elementType) {

+        List expressions = listExpr.getExpressions();

+        for (int i = 0; i < expressions.size(); i++) {

+            visitExpression(attrName, (Expression) expressions.get(i), elementType);

+        }

+    }

+    

+    protected void visitConstantExpression(String attrName, ConstantExpression constExpr, ClassNode attrType) {

+        if(!constExpr.getType().isDerivedFrom(attrType)) {

+            addError("Attribute '" + attrName + "' should have type '" + attrType.getName() + "'; "

+                    + "but found type '" + constExpr.getType().getName() + "'",

+                    constExpr);

+        }

+    }

+    

+    protected void visitEnumExpression(String attrName, PropertyExpression propExpr, ClassNode attrType) {

+        if(!propExpr.getObjectExpression().getType().isDerivedFrom(attrType)) {

+            addError("Attribute '" + attrName + "' should have type '" + attrType.getName() +"' (Enum), but found "

+                    + propExpr.getObjectExpression().getType().getName(), 

+                    propExpr);

+        }

+    }

+    

+    private boolean isAnnotation(Class clazz) {

+        Boolean result = (Boolean) invoke(clazz.getClass(), "isAnnotation", EMPTY_ARG_TYPES, clazz, EMPTY_ARGS);

+        return result.booleanValue();

+    }

+    

+    private boolean isEnum(Class clazz) {

+        Boolean result = (Boolean) invoke(clazz.getClass(), "isEnum", EMPTY_ARG_TYPES, clazz, EMPTY_ARGS);

+        return result.booleanValue();

+    }

+    

+    private void extractAnnotationMeta(Class annotationClass) {

+        initializeAnnotationMeta(annotationClass);

+        initializeAttributeTypes(annotationClass);

+    }

+    

+    private void initializeAnnotationMeta(Class annotationClass) {

+        Object[] annotations = (Object[]) invoke(annotationClass.getClass(), 

+                "getAnnotations", EMPTY_ARG_TYPES, annotationClass, EMPTY_ARGS);

+        if (annotations == null) {

+            addError("Cannot retrieve annotation meta information. " 

+                    + ExtendedVerifier.JVM_ERROR_MESSAGE);

+            return;

+        }

+        

+        for(int i = 0; i < annotations.length; i++) {

+            Class annotationType = (Class) invoke(this.annotationRootClass, 

+                    "annotationType", EMPTY_ARG_TYPES, annotations[i], EMPTY_ARGS);

+            if (annotationType == null) continue;

+            

+            if ("java.lang.annotation.Retention".equals(annotationType.getName())) {

+                initializeRetention(annotationClass, annotationType, annotations[i]);

+            }

+            else if("java.lang.annotation.Target".equals(annotationType.getName())) {

+                initializeTarget(annotationClass, annotationType, annotations[i]);

+            }

+        }

+    }

+    

+    private void initializeAttributeTypes(Class annotationClass) {

+        Method[] methods = annotationClass.getDeclaredMethods();

+        for(int i = 0; i < methods.length; i++) {

+            Object defaultValue = invoke(Method.class, "getDefaultValue", EMPTY_ARG_TYPES, methods[i], EMPTY_ARGS);

+            if (defaultValue != null) { 

+                // by now we know JDK1.5 API is available so a null means no default value

+                defaultAttrTypes.put(methods[i].getName(), methods[i].getReturnType());

+            }

+            else {

+                requiredAttrTypes.put(methods[i].getName(), methods[i].getReturnType());

+            }

+        }

+    }

+    

+    private void initializeRetention(Class annotationClass, Class retentionClass, Object retentionAnnotation) {

+        Object retentionPolicyEnum = 

+            invoke(retentionClass, "value", EMPTY_ARG_TYPES, retentionAnnotation, EMPTY_ARGS);

+        if (retentionPolicyEnum == null) {

+            addError("Cannot read @RetentionPolicy on the @" + annotationClass.getName() 

+                    + ExtendedVerifier.JVM_ERROR_MESSAGE);

+            return;

+        }

+        

+        if("RUNTIME".equals(retentionPolicyEnum.toString())) {

+            this.annotation.setRuntimeRetention(true);

+        }

+        else if("SOURCE".equals(retentionPolicyEnum.toString())) {

+            this.annotation.setSourceRetention(true);

+        }

+    }

+    

+    private void initializeTarget(Class annotationClass, Class targetClass, Object targetAnnotation) {

+        Object[] elementTypeEnum =

+            (Object[]) invoke(targetClass, "value", EMPTY_ARG_TYPES, targetAnnotation, EMPTY_ARGS);

+        if (elementTypeEnum == null) {

+            addError("Cannot read @Target on the @" + annotationClass.getName() 

+                    + ExtendedVerifier.JVM_ERROR_MESSAGE);

+            return;

+        }

+        int bitmap = 0;

+        for (int i = 0; i < elementTypeEnum.length; i++) {

+            String targetName = elementTypeEnum[i].toString();

+            if("TYPE".equals(targetName)) {

+                bitmap |= AnnotationNode.TYPE_TARGET;

+            }

+            else if("CONSTRUCTOR".equals(targetName)) {

+                bitmap |= AnnotationNode.CONSTRUCTOR_TARGET;

+            }

+            else if("METHOD".equals(targetName)) {

+                bitmap |= AnnotationNode.METHOD_TARGET;

+            }

+            else if("FIELD".equals(targetName)) {

+                bitmap |= AnnotationNode.FIELD_TARGET;

+            }

+            else if("PARAMETER".equals(targetName)) {

+                bitmap |= AnnotationNode.PARAMETER_TARGET;

+            }

+            else if("LOCAL_VARIABLE".equals(targetName)) {

+                bitmap |= AnnotationNode.LOCAL_VARIABLE_TARGET;

+            }

+            else if("ANNOTATION".equals(targetName)) {

+                bitmap |= AnnotationNode.ANNOTATION_TARGET;

+            }

+        }

+        this.annotation.setAllowedTargets(bitmap);

+    }

+    

+    protected void addError(String msg) {

+        this.errorCollector.addErrorAndContinue(

+          new SyntaxErrorMessage(new SyntaxException(msg 

+                  + " in @" + this.annotationClass.getName() + '\n', 

+                  this.annotation.getLineNumber(), 

+                  this.annotation.getColumnNumber()), this.source)

+        );

+    }

+    

+    protected void addError(String msg, ASTNode expr) {

+        this.errorCollector.addErrorAndContinue(

+          new SyntaxErrorMessage(new SyntaxException(msg 

+                  + " in @" + this.annotationClass.getName() + '\n', 

+                  expr.getLineNumber(), 

+                  expr.getColumnNumber()), this.source)

+        );

+    }

+    

+    private Class getAttributeType(String attr) {

+        if(this.requiredAttrTypes.containsKey(attr)) {

+            return (Class) this.requiredAttrTypes.remove(attr);

+        }

+        

+        return (Class) this.defaultAttrTypes.remove(attr);

+    }

+    

+    private Object invoke(Class clazz, String methodName, Class[] argTypes, Object target, Object[] args) {

+        try {

+            Method m = clazz.getMethod(methodName, argTypes);

+            return m.invoke(target, args);

+        }

+        catch(Throwable cause) {

+            // we report an error on called side

+        }

+     

+        return null;

+    }

+    

+    private Class loadAnnotationRootClass() {

+        try {

+            return Class.forName("java.lang.annotation.Annotation");

+        }

+        catch(Throwable cause) {

+            // report the error later

+        }

+        

+        return null;

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java b/groovy/src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java
new file mode 100644
index 0000000..d5a0d6e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/AsmClassGenerator.java
@@ -0,0 +1,3727 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyRuntimeException;
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.*;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
+import org.codehaus.groovy.syntax.RuntimeParserException;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.*;
+
+import java.util.*;
+
+
+/**
+ * Generates Java class versions of Groovy classes using ASM.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>
+ * @version $Revision$
+ */
+public class AsmClassGenerator extends ClassGenerator {
+
+//    private Logger log = Logger.getLogger(getClass().getName());
+
+    private final ClassVisitor cv;
+    private MethodVisitor mv;
+    private GeneratorContext context;
+
+    private String sourceFile;
+
+    // current class details
+    private ClassNode classNode;
+    private ClassNode outermostClass;
+    private String internalClassName;
+    private String internalBaseClassName;
+
+    /**
+     * maps the variable names to the JVM indices
+     */
+    private CompileStack compileStack;
+
+    /**
+     * have we output a return statement yet
+     */
+    private boolean outputReturn;
+
+    /**
+     * are we on the left or right of an expression
+     */
+    private boolean leftHandExpression = false;
+    /**
+     * Notes for leftHandExpression:
+     * The default is false, that menas the right side is default.
+     * The right side means that variables are read and not written.
+     * Any change of leftHandExpression to true, should be made carefully.
+     * If such a change is needed, then it should be set to false as soon as
+     * possible, but most important in the same method. Setting
+     * leftHandExpression to false is needed for writing variables.
+     */
+
+    // method invocation
+    static final MethodCallerMultiAdapter invokeMethodOnCurrent = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "invokeMethodOnCurrent", true, false);
+    static final MethodCallerMultiAdapter invokeMethodOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "invokeMethodOnSuper", true, false);
+    static final MethodCallerMultiAdapter invokeMethod = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "invokeMethod", true, false);
+    static final MethodCallerMultiAdapter invokeStaticMethod = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "invokeStaticMethod", true, true);
+    static final MethodCallerMultiAdapter invokeNew = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "invokeNew", true, true);
+
+    // fields & properties
+    static final MethodCallerMultiAdapter setField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "setField", false, false);
+    static final MethodCallerMultiAdapter getField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "getField", false, false);
+    static final MethodCallerMultiAdapter setGroovyObjectField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "setGroovyObjectField", false, false);
+    static final MethodCallerMultiAdapter getGroovyObjectField = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "getGroovyObjectField", false, false);
+    static final MethodCallerMultiAdapter setFieldOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "setFieldOnSuper", false, false);
+    static final MethodCallerMultiAdapter getFieldOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "getFieldOnSuper", false, false);
+
+    static final MethodCallerMultiAdapter setProperty = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "setProperty", false, false);
+    static final MethodCallerMultiAdapter getProperty = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "getProperty", false, false);
+    static final MethodCallerMultiAdapter setGroovyObjectProperty = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "setGroovyObjectProperty", false, false);
+    static final MethodCallerMultiAdapter getGroovyObjectProperty = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "getGroovyObjectProperty", false, false);
+    static final MethodCallerMultiAdapter setPropertyOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "setPropertyOnSuper", false, false);
+    static final MethodCallerMultiAdapter getPropertyOnSuper = MethodCallerMultiAdapter.newStatic(ScriptBytecodeAdapter.class, "getPropertyOnSuper", false, false);
+
+    // iterator
+    static final MethodCaller iteratorNextMethod = MethodCaller.newInterface(Iterator.class, "next");
+    static final MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(Iterator.class, "hasNext");
+    // assert
+    static final MethodCaller assertFailedMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "assertFailed");
+    // isCase
+    static final MethodCaller isCaseMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "isCase");
+    //compare
+    static final MethodCaller compareIdenticalMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareIdentical");
+    static final MethodCaller compareEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareEqual");
+    static final MethodCaller compareNotEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareNotEqual");
+    static final MethodCaller compareToMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareTo");
+    static final MethodCaller compareLessThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThan");
+    static final MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareLessThanEqual");
+    static final MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThan");
+    static final MethodCaller compareGreaterThanEqualMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "compareGreaterThanEqual");
+    //regexpr
+    static final MethodCaller findRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "findRegex");
+    static final MethodCaller matchRegexMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "matchRegex");
+    static final MethodCaller regexPattern = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "regexPattern");
+    // spread expressions
+    static final MethodCaller spreadMap = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "spreadMap");
+    static final MethodCaller despreadList = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "despreadList");
+    // Closure
+    static final MethodCaller getMethodPointer = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "getMethodPointer");
+    static final MethodCaller invokeClosureMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "invokeClosure");
+    // unary plus, unary minus, bitwise negation
+    static final MethodCaller unaryPlus = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "unaryPlus");
+    static final MethodCaller unaryMinus = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "unaryMinus");
+    static final MethodCaller bitwiseNegate = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "bitwiseNegate");
+
+    // type conversions
+    static final MethodCaller asTypeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "asType");
+    static final MethodCaller castToTypeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "castToType");
+    static final MethodCaller createListMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createList");
+    static final MethodCaller createTupleMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createTuple");
+    static final MethodCaller createMapMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createMap");
+    static final MethodCaller createRangeMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createRange");
+
+    // wrapper creation methods
+    static final MethodCaller createPojoWrapperMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createPojoWrapper");
+    static final MethodCaller createGroovyObjectWrapperMethod = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "createGroovyObjectWrapper");
+
+    // constructor calls with this() and super()
+    static final MethodCaller selectConstructorAndTransformArguments = MethodCaller.newStatic(ScriptBytecodeAdapter.class, "selectConstructorAndTransformArguments");
+
+    // exception blocks list
+    private List exceptionBlocks = new ArrayList();
+
+    private Set syntheticStaticFields = new HashSet();
+    private boolean passingClosureParams;
+
+    private ConstructorNode constructorNode;
+    private MethodNode methodNode;
+    private BytecodeHelper helper = new BytecodeHelper(null);
+
+    public static final boolean CREATE_DEBUG_INFO = true;
+    public static final boolean CREATE_LINE_NUMBER_INFO = true;
+    private static final boolean MARK_START = true;
+
+    public static final boolean ASM_DEBUG = false; // add marker in the bytecode to show source-byecode relationship
+    private int lineNumber = -1;
+    private int columnNumber = -1;
+    private ASTNode currentASTNode = null;
+
+    private DummyClassGenerator dummyGen = null;
+    private ClassWriter dummyClassWriter = null;
+
+    private ClassNode interfaceClassLoadingClass;
+
+    private boolean implicitThis = false;
+
+    private Map genericParameterNames = null;
+
+    public AsmClassGenerator(
+            GeneratorContext context, ClassVisitor classVisitor,
+            ClassLoader classLoader, String sourceFile
+    ) {
+        super(classLoader);
+        this.context = context;
+        this.cv = classVisitor;
+        this.sourceFile = sourceFile;
+
+        this.dummyClassWriter = new ClassWriter(true);
+        dummyGen = new DummyClassGenerator(context, dummyClassWriter, classLoader, sourceFile);
+        compileStack = new CompileStack();
+        genericParameterNames = new HashMap();
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return null;
+    }
+
+
+    // GroovyClassVisitor interface
+    //-------------------------------------------------------------------------
+    public void visitClass(ClassNode classNode) {
+
+        try {
+            syntheticStaticFields.clear();
+            this.classNode = classNode;
+            this.outermostClass = null;
+            this.internalClassName = BytecodeHelper.getClassInternalName(classNode);
+
+            this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
+
+            cv.visit(
+                    getBytecodeVersion(),
+                    classNode.getModifiers(),
+                    internalClassName,
+                    BytecodeHelper.getGenericsSignature(classNode),
+                    internalBaseClassName,
+                    BytecodeHelper.getClassInternalNames(classNode.getInterfaces())
+            );
+            cv.visitSource(sourceFile, null);
+            visitAnnotations(classNode, cv);
+
+            if (classNode.isInterface()) {
+                ClassNode owner = classNode;
+                if (owner instanceof InnerClassNode) {
+                    owner = owner.getOuterClass();
+                }
+                String outerClassName = owner.getName();
+                String name = outerClassName + "$" + context.getNextInnerClassIdx();
+                interfaceClassLoadingClass = new InnerClassNode(owner, name, 4128, ClassHelper.OBJECT_TYPE);
+
+                super.visitClass(classNode);
+                createInterfaceSyntheticStaticFields();
+            } else {
+                super.visitClass(classNode);
+                if (!classNode.declaresInterface(ClassHelper.GENERATED_CLOSURE_Type.getName())) {
+                    createMopMethods();
+                }
+                createSyntheticStaticFields();
+            }
+
+            for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
+                ClassNode innerClass = (ClassNode) iter.next();
+                String innerClassName = innerClass.getName();
+                String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassName);
+                {
+                    int index = innerClassName.lastIndexOf('$');
+                    if (index >= 0) innerClassName = innerClassName.substring(index + 1);
+                }
+                String outerClassName = internalClassName; // default for inner classes
+                MethodNode enclosingMethod = innerClass.getEnclosingMethod();
+                if (enclosingMethod != null) {
+                    // local inner classes do not specify the outer class name
+                    outerClassName = null;
+                    innerClassName = null;
+                }
+                cv.visitInnerClass(
+                        innerClassInternalName,
+                        outerClassName,
+                        innerClassName,
+                        innerClass.getModifiers());
+            }
+            //TODO: an inner class should have an entry of itself
+            cv.visitEnd();
+        }
+        catch (GroovyRuntimeException e) {
+            e.setModule(classNode.getModule());
+            throw e;
+        }
+    }
+
+    public void visitGenericType(GenericsType genericsType) {
+        ClassNode type = genericsType.getType();
+        genericParameterNames.put(type.getName(), genericsType);
+    }
+
+    private void createMopMethods() {
+        visitMopMethodList(classNode.getMethods(), true);
+        visitMopMethodList(classNode.getSuperClass().getAllDeclaredMethods(), false);
+    }
+
+    private String[] buildExceptions(ClassNode[] exceptions) {
+        if (exceptions == null) return null;
+        String[] ret = new String[exceptions.length];
+        for (int i = 0; i < exceptions.length; i++) {
+            ret[i] = BytecodeHelper.getClassInternalName(exceptions[i]);
+        }
+        return ret;
+    }
+
+    /**
+     * filters a list of method for MOP methods. For all methods that are no
+     * MOP methods a MOP method is created if the method is not public and the
+     * call would be a call on "this" (isThis == true). If the call is not on
+     * "this", then the call is a call on "super" and all methods are used,
+     * unless they are already a MOP method
+     *
+     * @param methods unfiltered list of methods for MOP
+     * @param isThis  if true, then we are creating a MOP method on "this", "super" else
+     * @see #generateMopCalls(LinkedList,boolean)
+     */
+    private void visitMopMethodList(List methods, boolean isThis) {
+        HashMap mops = new HashMap();
+        class Key {
+            int hash = 0;
+            String name;
+            Parameter[] params;
+
+            Key(String name, Parameter[] params) {
+                this.name = name;
+                this.params = params;
+                hash = name.hashCode() << 2 + params.length;
+            }
+
+            public int hashCode() {
+                return hash;
+            }
+
+            public boolean equals(Object obj) {
+                Key other = (Key) obj;
+                return other.name.equals(name) && equalParameterTypes(other.params,params);
+            }
+        }
+        LinkedList mopCalls = new LinkedList();
+        for (Iterator iter = methods.iterator(); iter.hasNext();) {
+            MethodNode mn = (MethodNode) iter.next();
+            if ((mn.getModifiers() & ACC_ABSTRACT) != 0) continue;
+            // no this$ methods for protected/public isThis=true
+            // super$ method for protected/public isThis=false
+            // --> results in XOR
+            if (isThis ^ (mn.getModifiers() & (ACC_PUBLIC | ACC_PROTECTED)) == 0) continue;
+            String methodName = mn.getName();
+            if (isMopMethod(methodName)) {
+                mops.put(new Key(methodName, mn.getParameters()), mn);
+                continue;
+            }
+            if (methodName.startsWith("<")) continue;
+            String name = getMopMethodName(mn, isThis);
+            Key key = new Key(name, mn.getParameters());
+            if (mops.containsKey(key)) continue;
+            mops.put(key, mn);
+            mopCalls.add(mn);
+        }
+        generateMopCalls(mopCalls, isThis);
+        mopCalls.clear();
+        mops.clear();
+    }
+
+    private boolean equalParameterTypes(Parameter[] p1, Parameter[] p2) {
+        if (p1.length!=p2.length) return false;
+        for (int i=0; i<p1.length; i++) {
+            if (!p1[i].getType().equals(p2[i].getType())) return false;
+        }
+        return true;
+    }
+
+    /**
+     * generates a Meta Object Protocoll method, that is used to call a non public
+     * method, or to make a call to super.
+     *
+     * @param mopCalls list of methods a mop call method should be generated for
+     * @param useThis  true if "this" should be used for the naming
+     */
+    private void generateMopCalls(LinkedList mopCalls, boolean useThis) {
+        for (Iterator iter = mopCalls.iterator(); iter.hasNext();) {
+            MethodNode method = (MethodNode) iter.next();
+            String name = getMopMethodName(method, useThis);
+            Parameter[] parameters = method.getParameters();
+            String methodDescriptor = BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getParameters());
+            mv = cv.visitMethod(Opcodes.ACC_PUBLIC & Opcodes.ACC_SYNTHETIC, name, methodDescriptor, null, null);
+            mv.visitVarInsn(ALOAD, 0);
+            BytecodeHelper helper = new BytecodeHelper(mv);
+            int newRegister = 1;
+            for (int i = 0; i < parameters.length; i++) {
+                ClassNode type = parameters[i].getType();
+                helper.load(parameters[i].getType(), newRegister);
+                // increment to next register, double/long are using two places
+                newRegister++;
+                if (type == ClassHelper.double_TYPE || type == ClassHelper.long_TYPE) newRegister++;
+            }
+            mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(method.getDeclaringClass()), method.getName(), methodDescriptor);
+            helper.doReturn(method.getReturnType());
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+            classNode.addMethod(name, Opcodes.ACC_PUBLIC & Opcodes.ACC_SYNTHETIC, method.getReturnType(), parameters, null, null);
+        }
+    }
+
+    /**
+     * creates a MOP method name from a method
+     *
+     * @param method  the method to be called by the mop method
+     * @param useThis if true, then it is a call on "this", "super" else
+     * @return the mop method name
+     */
+    public static String getMopMethodName(MethodNode method, boolean useThis) {
+        ClassNode declaringNode = method.getDeclaringClass();
+        int distance = 0;
+        for (; declaringNode != null; declaringNode = declaringNode.getSuperClass()) {
+            distance++;
+        }
+        return (useThis ? "this" : "super") + "$" + distance + "$" + method.getName();
+    }
+
+    /**
+     * method to determine if a method is a MOP method. This is done by the
+     * method name. If the name starts with "this$" or "super$", then it is
+     * a MOP method
+     *
+     * @param methodName name of the method to test
+     * @return true if the method is a MOP method
+     */
+    public static boolean isMopMethod(String methodName) {
+        return methodName.startsWith("this$") ||
+                methodName.startsWith("super$");
+    }
+
+    protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+        Parameter[] parameters = node.getParameters();
+        String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), parameters);
+
+        String signature = BytecodeHelper.getGenericsMethodSignature(node);
+        int modifiers = node.getModifiers();
+        if (isVargs(node.getParameters())) modifiers |= Opcodes.ACC_VARARGS;
+        mv = cv.visitMethod(modifiers, node.getName(), methodType, signature, buildExceptions(node.getExceptions()));
+        visitAnnotations(node, mv);
+        for (int i = 0; i < parameters.length; i++) {
+            visitParameterAnnotations(parameters[i], i, mv);
+        }
+        helper = new BytecodeHelper(mv);
+        if (!node.isAbstract()) {
+            Statement code = node.getCode();
+            
+            if (isConstructor && (code == null || !((ConstructorNode) node).firstStatementIsSpecialConstructorCall())) {
+                // invokes the super class constructor
+                mv.visitVarInsn(ALOAD, 0);
+                mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(classNode.getSuperClass()), "<init>", "()V");
+            }
+
+            compileStack.init(node.getVariableScope(), parameters, mv, classNode);
+
+            // ensure we save the current (meta) class in a register
+            (new ClassExpression(classNode)).visit(this);
+            mv.visitInsn(POP);
+            (new ClassExpression(ClassHelper.METACLASS_TYPE)).visit(this);
+            mv.visitInsn(POP);
+
+            // handle body
+            super.visitConstructorOrMethod(node, isConstructor);
+            if (!outputReturn || node.isVoidMethod()) {
+                mv.visitInsn(RETURN);
+            }
+            compileStack.clear();
+
+            // lets do all the exception blocks
+            for (Iterator iter = exceptionBlocks.iterator(); iter.hasNext();) {
+                Runnable runnable = (Runnable) iter.next();
+                runnable.run();
+            }
+            exceptionBlocks.clear();
+
+            mv.visitMaxs(0, 0);
+        }
+        mv.visitEnd();
+    }
+
+    private boolean isVargs(Parameter[] p) {
+        if (p.length==0) return false;
+        ClassNode clazz = p[p.length-1].getType();
+        return (clazz.isArray());
+    }
+
+    public void visitConstructor(ConstructorNode node) {
+        this.constructorNode = node;
+        this.methodNode = null;
+        outputReturn = false;
+        super.visitConstructor(node);
+    }
+
+    public void visitMethod(MethodNode node) {
+        this.constructorNode = null;
+        this.methodNode = node;
+        outputReturn = false;
+
+        super.visitMethod(node);
+    }
+
+    public void visitField(FieldNode fieldNode) {
+        onLineNumber(fieldNode, "visitField: " + fieldNode.getName());
+        ClassNode t = fieldNode.getType();
+        String signature = helper.getGenericsBounds(t);
+        FieldVisitor fv = cv.visitField(
+                fieldNode.getModifiers(),
+                fieldNode.getName(),
+                BytecodeHelper.getTypeDescription(t),
+                signature, //fieldValue,  //br  all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer.
+                null);
+        visitAnnotations(fieldNode, fv);
+        fv.visitEnd();
+    }
+
+    public void visitProperty(PropertyNode statement) {
+        // the verifyer created the field and the setter/getter methods, so here is
+        // not really something to do
+        onLineNumber(statement, "visitProperty:" + statement.getField().getName());
+        this.methodNode = null;
+    }
+
+    // GroovyCodeVisitor interface
+    //-------------------------------------------------------------------------
+
+    // Statements
+    //-------------------------------------------------------------------------
+
+    protected void visitStatement(Statement statement) {
+        String name = statement.getStatementLabel();
+        if (name != null) {
+            Label label = compileStack.createLocalLabel(name);
+            mv.visitLabel(label);
+        }
+    }
+
+    public void visitBlockStatement(BlockStatement block) {
+        onLineNumber(block, "visitBlockStatement");
+        visitStatement(block);
+
+        compileStack.pushVariableScope(block.getVariableScope());
+        super.visitBlockStatement(block);
+        compileStack.pop();
+    }
+
+    private void visitExpressionOrStatement(Object o) {
+        if (o == EmptyExpression.INSTANCE) return;
+        if (o instanceof Expression) {
+            Expression expr = (Expression) o;
+            visitAndAutoboxBoolean(expr);
+            if (isPopRequired(expr)) mv.visitInsn(POP);
+        } else {
+            ((Statement) o).visit(this);
+        }
+    }
+
+    private void visitForLoopWithClosureList(ForStatement loop) {
+        compileStack.pushLoop(loop.getVariableScope(), loop.getStatementLabel());
+
+        ClosureListExpression clExpr = (ClosureListExpression) loop.getCollectionExpression();
+        compileStack.pushVariableScope(clExpr.getVariableScope());
+
+        List expressions = clExpr.getExpressions();
+        int size = expressions.size();
+
+        // middle element is condition, lower half is init, higher half is increment
+        int condIndex = (size - 1) / 2;
+
+        // visit init
+        for (int i = 0; i < condIndex; i++) {
+            visitExpressionOrStatement(expressions.get(i));
+        }
+
+        Label continueLabel = compileStack.getContinueLabel();
+        Label breakLabel = compileStack.getBreakLabel();
+
+        mv.visitLabel(continueLabel);
+
+        // visit condition leave boolean on stack
+        {
+            Expression condExpr = (Expression) expressions.get(condIndex);
+            if (condExpr == EmptyExpression.INSTANCE) {
+                mv.visitIntInsn(BIPUSH, 0);
+            } else if (isComparisonExpression(condExpr)) {
+                condExpr.visit(this);
+            } else {
+                visitAndAutoboxBoolean(condExpr);
+                helper.unbox(ClassHelper.boolean_TYPE);
+            }
+        }
+        // jump if we don't want to continue
+        // note: ifeq tests for ==0, a boolean is 0 if it is false
+        mv.visitJumpInsn(IFEQ, breakLabel);
+
+        // Generate the loop body
+        loop.getLoopBlock().visit(this);
+
+        // visit increment
+        for (int i = condIndex + 1; i < size; i++) {
+            visitExpressionOrStatement(expressions.get(i));
+        }
+
+        // jump to test the condition again
+        mv.visitJumpInsn(GOTO, continueLabel);
+
+        // loop end
+        mv.visitLabel(breakLabel);
+
+        compileStack.pop();
+        compileStack.pop();
+
+    }
+
+    public void visitForLoop(ForStatement loop) {
+
+        onLineNumber(loop, "visitForLoop");
+        visitStatement(loop);
+
+
+        Parameter loopVar = loop.getVariable();
+        if (loopVar == ForStatement.FOR_LOOP_DUMMY) {
+            visitForLoopWithClosureList(loop);
+            return;
+        }
+
+        compileStack.pushLoop(loop.getVariableScope(), loop.getStatementLabel());
+
+        // Declare the loop counter.
+        Variable variable = compileStack.defineVariable(loop.getVariable(), false);
+
+        //
+        // Then get the iterator and generate the loop control
+        MethodCallExpression iterator = new MethodCallExpression(loop.getCollectionExpression(), "iterator", new ArgumentListExpression());
+        iterator.visit(this);
+
+        final int iteratorIdx = compileStack.defineTemporaryVariable("iterator", ClassHelper.make(java.util.Iterator.class), true);
+
+        Label continueLabel = compileStack.getContinueLabel();
+        Label breakLabel = compileStack.getBreakLabel();
+
+        mv.visitLabel(continueLabel);
+        mv.visitVarInsn(ALOAD, iteratorIdx);
+        iteratorHasNextMethod.call(mv);
+        // note: ifeq tests for ==0, a boolean is 0 if it is false
+        mv.visitJumpInsn(IFEQ, breakLabel);
+
+        mv.visitVarInsn(ALOAD, iteratorIdx);
+        iteratorNextMethod.call(mv);
+        helper.storeVar(variable);
+
+        // Generate the loop body
+        loop.getLoopBlock().visit(this);
+
+        mv.visitJumpInsn(GOTO, continueLabel);
+        mv.visitLabel(breakLabel);
+
+        compileStack.pop();
+    }
+
+    public void visitWhileLoop(WhileStatement loop) {
+        onLineNumber(loop, "visitWhileLoop");
+        visitStatement(loop);
+
+        compileStack.pushLoop(loop.getStatementLabel());
+        Label continueLabel = compileStack.getContinueLabel();
+        Label breakLabel = compileStack.getBreakLabel();
+
+        mv.visitLabel(continueLabel);
+        loop.getBooleanExpression().visit(this);
+        mv.visitJumpInsn(IFEQ, breakLabel);
+
+        loop.getLoopBlock().visit(this);
+
+        mv.visitJumpInsn(GOTO, continueLabel);
+        mv.visitLabel(breakLabel);
+
+        compileStack.pop();
+    }
+
+    public void visitDoWhileLoop(DoWhileStatement loop) {
+        onLineNumber(loop, "visitDoWhileLoop");
+        visitStatement(loop);
+
+        compileStack.pushLoop(loop.getStatementLabel());
+        Label breakLabel = compileStack.getBreakLabel();
+        Label continueLabel = compileStack.getContinueLabel();
+        mv.visitLabel(continueLabel);
+
+        loop.getLoopBlock().visit(this);
+
+        loop.getBooleanExpression().visit(this);
+        mv.visitJumpInsn(IFEQ, continueLabel);
+        mv.visitLabel(breakLabel);
+
+        compileStack.pop();
+    }
+
+    public void visitIfElse(IfStatement ifElse) {
+        onLineNumber(ifElse, "visitIfElse");
+        visitStatement(ifElse);
+        ifElse.getBooleanExpression().visit(this);
+
+        Label l0 = new Label();
+        mv.visitJumpInsn(IFEQ, l0);
+
+        // if-else is here handled as a special version
+        // of a booelan expression
+        compileStack.pushBooleanExpression();
+        ifElse.getIfBlock().visit(this);
+        compileStack.pop();
+
+        Label l1 = new Label();
+        mv.visitJumpInsn(GOTO, l1);
+        mv.visitLabel(l0);
+
+        compileStack.pushBooleanExpression();
+        ifElse.getElseBlock().visit(this);
+        compileStack.pop();
+
+        mv.visitLabel(l1);
+    }
+
+    public void visitTernaryExpression(TernaryExpression expression) {
+        onLineNumber(expression, "visitTernaryExpression");
+
+        BooleanExpression boolPart = expression.getBooleanExpression();
+        Expression truePart = expression.getTrueExpression();
+        Expression falsePart = expression.getFalseExpression();
+        
+        if (expression instanceof ElvisOperatorExpression) {
+            visitAndAutoboxBoolean(expression.getTrueExpression());
+            boolPart = new BooleanExpression(
+                    new BytecodeExpression() {
+                        public void visit(GroovyCodeVisitor visitor) {
+                            mv.visitInsn(DUP);
+                        }
+                    }
+            );
+            truePart = BytecodeExpression.NOP;
+            final Expression oldFalse = falsePart;
+            falsePart = new BytecodeExpression() {
+                public void visit(GroovyCodeVisitor visitor) {
+                    mv.visitInsn(POP);
+                    visitAndAutoboxBoolean(oldFalse);
+                }
+            };
+        }
+        
+        
+        boolPart.visit(this);
+
+        Label l0 = new Label();
+        mv.visitJumpInsn(IFEQ, l0);
+        compileStack.pushBooleanExpression();
+        visitAndAutoboxBoolean(truePart);
+        compileStack.pop();
+        
+        Label l1 = new Label();
+        mv.visitJumpInsn(GOTO, l1);
+        mv.visitLabel(l0);
+        compileStack.pushBooleanExpression();
+        visitAndAutoboxBoolean(falsePart);
+        compileStack.pop();
+        
+        mv.visitLabel(l1);
+    }
+
+    public void visitAssertStatement(AssertStatement statement) {
+        onLineNumber(statement, "visitAssertStatement");
+        visitStatement(statement);
+
+        BooleanExpression booleanExpression = statement.getBooleanExpression();
+        booleanExpression.visit(this);
+
+        Label l0 = new Label();
+        mv.visitJumpInsn(IFEQ, l0);
+
+        // do nothing
+
+        Label l1 = new Label();
+        mv.visitJumpInsn(GOTO, l1);
+        mv.visitLabel(l0);
+
+        // push expression string onto stack
+        String expressionText = booleanExpression.getText();
+        List list = new ArrayList();
+        addVariableNames(booleanExpression, list);
+        if (list.isEmpty()) {
+            mv.visitLdcInsn(expressionText);
+        } else {
+            boolean first = true;
+
+            // lets create a new expression
+            mv.visitTypeInsn(NEW, "java/lang/StringBuffer");
+            mv.visitInsn(DUP);
+            mv.visitLdcInsn(expressionText + ". Values: ");
+
+            mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer", "<init>", "(Ljava/lang/String;)V");
+
+            int tempIndex = compileStack.defineTemporaryVariable("assert", true);
+
+            for (Iterator iter = list.iterator(); iter.hasNext();) {
+                String name = (String) iter.next();
+                String text = name + " = ";
+                if (first) {
+                    first = false;
+                } else {
+                    text = ", " + text;
+                }
+
+                mv.visitVarInsn(ALOAD, tempIndex);
+                mv.visitLdcInsn(text);
+                mv.visitMethodInsn(
+                        INVOKEVIRTUAL,
+                        "java/lang/StringBuffer",
+                        "append",
+                        "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
+                mv.visitInsn(POP);
+
+                mv.visitVarInsn(ALOAD, tempIndex);
+                new VariableExpression(name).visit(this);
+                mv.visitMethodInsn(
+                        INVOKEVIRTUAL,
+                        "java/lang/StringBuffer",
+                        "append",
+                        "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
+                mv.visitInsn(POP);
+
+            }
+            mv.visitVarInsn(ALOAD, tempIndex);
+            compileStack.removeVar(tempIndex);
+        }
+        // now the optional exception expression
+        statement.getMessageExpression().visit(this);
+
+        assertFailedMethod.call(mv);
+        mv.visitLabel(l1);
+    }
+
+    private void addVariableNames(Expression expression, List list) {
+        if (expression instanceof BooleanExpression) {
+            BooleanExpression boolExp = (BooleanExpression) expression;
+            addVariableNames(boolExp.getExpression(), list);
+        } else if (expression instanceof BinaryExpression) {
+            BinaryExpression binExp = (BinaryExpression) expression;
+            addVariableNames(binExp.getLeftExpression(), list);
+            addVariableNames(binExp.getRightExpression(), list);
+        } else if (expression instanceof VariableExpression) {
+            VariableExpression varExp = (VariableExpression) expression;
+            list.add(varExp.getName());
+        }
+    }
+
+    public void visitTryCatchFinally(TryCatchStatement statement) {
+        onLineNumber(statement, "visitTryCatchFinally");
+        visitStatement(statement);
+
+        CatchStatement catchStatement = statement.getCatchStatement(0);
+        Statement tryStatement = statement.getTryStatement();
+        final Statement finallyStatement = statement.getFinallyStatement();
+
+        int anyExceptionIndex = compileStack.defineTemporaryVariable("exception", false);
+        if (!finallyStatement.isEmpty()) {
+            compileStack.pushFinallyBlock(
+                    new Runnable() {
+                        public void run() {
+                            compileStack.pushFinallyBlockVisit(this);
+                            finallyStatement.visit(AsmClassGenerator.this);
+                            compileStack.popFinallyBlockVisit(this);
+                        }
+                    }
+            );
+        }
+
+        // start try block, label needed for exception table
+        final Label tryStart = new Label();
+        mv.visitLabel(tryStart);
+        tryStatement.visit(this);
+        // goto finally part
+        final Label finallyStart = new Label();
+        mv.visitJumpInsn(GOTO, finallyStart);
+        // marker needed for Exception table
+        final Label tryEnd = new Label();
+        mv.visitLabel(tryEnd);
+
+        for (Iterator it = statement.getCatchStatements().iterator(); it.hasNext();) {
+            catchStatement = (CatchStatement) it.next();
+            ClassNode exceptionType = catchStatement.getExceptionType();
+            // start catch block, label needed for exception table
+            final Label catchStart = new Label();
+            mv.visitLabel(catchStart);
+            // create exception variable and store the exception 
+            compileStack.defineVariable(catchStatement.getVariable(), true);
+            // handle catch body
+            catchStatement.visit(this);
+            // goto finally start
+            mv.visitJumpInsn(GOTO, finallyStart);
+            // add exception to table
+            final String exceptionTypeInternalName = BytecodeHelper.getClassInternalName(exceptionType);
+            exceptionBlocks.add(new Runnable() {
+                public void run() {
+                    mv.visitTryCatchBlock(tryStart, tryEnd, catchStart, exceptionTypeInternalName);
+                }
+            });
+        }
+
+        // marker needed for the exception table
+        final Label endOfAllCatches = new Label();
+        mv.visitLabel(endOfAllCatches);
+
+        // remove the finally, don't let it visit itself
+        if (!finallyStatement.isEmpty()) compileStack.popFinallyBlock();
+
+        // start finally
+        mv.visitLabel(finallyStart);
+        finallyStatement.visit(this);
+        // goto end of finally
+        Label afterFinally = new Label();
+        mv.visitJumpInsn(GOTO, afterFinally);
+
+        // start a block catching any Exception
+        final Label catchAny = new Label();
+        mv.visitLabel(catchAny);
+        //store exception
+        mv.visitVarInsn(ASTORE, anyExceptionIndex);
+        finallyStatement.visit(this);
+        // load the exception and rethrow it
+        mv.visitVarInsn(ALOAD, anyExceptionIndex);
+        mv.visitInsn(ATHROW);
+
+        // end of all catches and finally parts
+        mv.visitLabel(afterFinally);
+
+        // add catch any block to exception table
+        exceptionBlocks.add(new Runnable() {
+            public void run() {
+                mv.visitTryCatchBlock(tryStart, endOfAllCatches, catchAny, null);
+            }
+        });
+    }
+
+    public void visitSwitch(SwitchStatement statement) {
+        onLineNumber(statement, "visitSwitch");
+        visitStatement(statement);
+
+        statement.getExpression().visit(this);
+
+        // switch does not have a continue label. use its parent's for continue
+        Label breakLabel = compileStack.pushSwitch();
+
+        int switchVariableIndex = compileStack.defineTemporaryVariable("switch", true);
+
+        List caseStatements = statement.getCaseStatements();
+        int caseCount = caseStatements.size();
+        Label[] labels = new Label[caseCount + 1];
+        for (int i = 0; i < caseCount; i++) {
+            labels[i] = new Label();
+        }
+
+        int i = 0;
+        for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) {
+            CaseStatement caseStatement = (CaseStatement) iter.next();
+            visitCaseStatement(caseStatement, switchVariableIndex, labels[i], labels[i + 1]);
+        }
+
+        statement.getDefaultStatement().visit(this);
+
+        mv.visitLabel(breakLabel);
+
+        compileStack.pop();
+    }
+
+    public void visitCaseStatement(CaseStatement statement) {
+    }
+
+    public void visitCaseStatement(
+            CaseStatement statement,
+            int switchVariableIndex,
+            Label thisLabel,
+            Label nextLabel) {
+
+        onLineNumber(statement, "visitCaseStatement");
+
+        mv.visitVarInsn(ALOAD, switchVariableIndex);
+        statement.getExpression().visit(this);
+
+        isCaseMethod.call(mv);
+
+        Label l0 = new Label();
+        mv.visitJumpInsn(IFEQ, l0);
+
+        mv.visitLabel(thisLabel);
+
+        statement.getCode().visit(this);
+
+        // now if we don't finish with a break we need to jump past
+        // the next comparison
+        if (nextLabel != null) {
+            mv.visitJumpInsn(GOTO, nextLabel);
+        }
+
+        mv.visitLabel(l0);
+    }
+
+    public void visitBreakStatement(BreakStatement statement) {
+        onLineNumber(statement, "visitBreakStatement");
+        visitStatement(statement);
+
+        String name = statement.getLabel();
+        Label breakLabel = compileStack.getNamedBreakLabel(name);
+        compileStack.applyFinallyBlocks(breakLabel, true);
+
+        mv.visitJumpInsn(GOTO, breakLabel);
+    }
+
+    public void visitContinueStatement(ContinueStatement statement) {
+        onLineNumber(statement, "visitContinueStatement");
+        visitStatement(statement);
+
+        String name = statement.getLabel();
+        Label continueLabel = compileStack.getContinueLabel();
+        if (name != null) continueLabel = compileStack.getNamedContinueLabel(name);
+        compileStack.applyFinallyBlocks(continueLabel, false);
+        mv.visitJumpInsn(GOTO, continueLabel);
+    }
+
+    public void visitSynchronizedStatement(SynchronizedStatement statement) {
+        onLineNumber(statement, "visitSynchronizedStatement");
+        visitStatement(statement);
+
+        statement.getExpression().visit(this);
+        final int index = compileStack.defineTemporaryVariable("synchronized", ClassHelper.Integer_TYPE, true);
+
+        final Label synchronizedStart = new Label();
+        final Label synchronizedEnd = new Label();
+        final Label catchAll = new Label();
+
+        mv.visitVarInsn(ALOAD, index);
+        mv.visitInsn(MONITORENTER);
+        mv.visitLabel(synchronizedStart);
+
+        Runnable finallyPart = new Runnable() {
+            public void run() {
+                mv.visitVarInsn(ALOAD, index);
+                mv.visitInsn(MONITOREXIT);
+            }
+        };
+        compileStack.pushFinallyBlock(finallyPart);
+        statement.getCode().visit(this);
+
+        finallyPart.run();
+        mv.visitJumpInsn(GOTO, synchronizedEnd);
+        mv.visitLabel(catchAll);
+        finallyPart.run();
+        mv.visitInsn(ATHROW);
+        mv.visitLabel(synchronizedEnd);
+
+        compileStack.popFinallyBlock();
+        exceptionBlocks.add(new Runnable() {
+            public void run() {
+                mv.visitTryCatchBlock(synchronizedStart, catchAll, catchAll, null);
+            }
+        });
+    }
+
+    public void visitThrowStatement(ThrowStatement statement) {
+        onLineNumber(statement, "visitThrowStatement");
+        visitStatement(statement);
+
+        statement.getExpression().visit(this);
+
+        // we should infer the type of the exception from the expression
+        mv.visitTypeInsn(CHECKCAST, "java/lang/Throwable");
+
+        mv.visitInsn(ATHROW);
+    }
+
+    public void visitReturnStatement(ReturnStatement statement) {
+        onLineNumber(statement, "visitReturnStatement");
+        visitStatement(statement);
+
+        ClassNode returnType;
+        if (methodNode != null) {
+            returnType = methodNode.getReturnType();
+        } else if (constructorNode != null) {
+            returnType = constructorNode.getReturnType();
+        } else {
+            throw new GroovyBugError("I spotted a return that is neither in a method nor in a constructor... I can not handle that");
+        }
+
+        if (returnType == ClassHelper.VOID_TYPE) {
+            if (!(statement == ReturnStatement.RETURN_NULL_OR_VOID)) {
+                throwException("Cannot use return statement with an expression on a method that returns void");
+            }
+            compileStack.applyFinallyBlocks();
+            mv.visitInsn(RETURN);
+            outputReturn = true;
+            return;
+        }
+
+        Expression expression = statement.getExpression();
+        evaluateExpression(expression);
+        if (returnType == ClassHelper.OBJECT_TYPE && expression.getType() != null && expression.getType() == ClassHelper.VOID_TYPE) {
+            mv.visitInsn(ACONST_NULL); // cheat the caller
+        } else {
+            // return is based on class type
+            // we may need to cast
+            doConvertAndCast(returnType, expression, false, true, false);
+        }
+        if (compileStack.hasFinallyBlocks()) {
+            // value is always saved in boxed form, so no need to have a special load routine here
+            int returnValueIdx = compileStack.defineTemporaryVariable("returnValue", ClassHelper.OBJECT_TYPE, true);
+            compileStack.applyFinallyBlocks();
+            helper.load(ClassHelper.OBJECT_TYPE, returnValueIdx);
+        }
+        // value is always saved in boxed form, so we need to unbox it here        
+        helper.unbox(returnType);
+        helper.doReturn(returnType);
+        outputReturn = true;
+    }
+
+    /**
+     * Casts to the given type unless it can be determined that the cast is unnecessary
+     */
+    protected void doConvertAndCast(ClassNode type, Expression expression, boolean ignoreAutoboxing, boolean forceCast, boolean coerce) {
+        ClassNode expType = getExpressionType(expression);
+        // temp resolution: convert all primitive casting to corresponsing Object type
+        if (!ignoreAutoboxing && ClassHelper.isPrimitiveType(type)) {
+            type = ClassHelper.getWrapper(type);
+        }
+
+        if (forceCast || (type != null && !type.equals(expType))) {
+            doConvertAndCast(type, coerce);
+        }
+    }
+
+    /**
+     * @param expression
+     */
+    protected void evaluateExpression(Expression expression) {
+        visitAndAutoboxBoolean(expression);
+
+        Expression assignExpr = createReturnLHSExpression(expression);
+        if (assignExpr != null) {
+            leftHandExpression = false;
+            assignExpr.visit(this);
+        }
+    }
+
+    public void visitExpressionStatement(ExpressionStatement statement) {
+        onLineNumber(statement, "visitExpressionStatement: " + statement.getExpression().getClass().getName());
+        visitStatement(statement);
+
+        Expression expression = statement.getExpression();
+
+        visitAndAutoboxBoolean(expression);
+
+        if (isPopRequired(expression)) {
+            mv.visitInsn(POP);
+        }
+    }
+
+    // Expressions
+    //-------------------------------------------------------------------------
+
+    public void visitDeclarationExpression(DeclarationExpression expression) {
+        onLineNumber(expression, "visitDeclarationExpression: \"" + expression.getVariableExpression().getName() + "\"");
+
+        Expression rightExpression = expression.getRightExpression();
+        // no need to visit left side, just get the variable name
+        VariableExpression vex = expression.getVariableExpression();
+        ClassNode type = vex.getType();
+
+        // lets not cast for primitive types as we handle these in field setting etc
+        if (ClassHelper.isPrimitiveType(type)) {
+            rightExpression.visit(this);
+        } else {
+            if (type != ClassHelper.OBJECT_TYPE) {
+                visitCastExpression(new CastExpression(type, rightExpression));
+            } else {
+                visitAndAutoboxBoolean(rightExpression);
+            }
+        }
+        compileStack.defineVariable(vex, true);
+    }
+
+    public void visitBinaryExpression(BinaryExpression expression) {
+        onLineNumber(expression, "visitBinaryExpression: \"" + expression.getOperation().getText() + "\" ");
+        switch (expression.getOperation().getType()) {
+            case Types.EQUAL: // = assignment
+                evaluateEqual(expression);
+                break;
+
+            case Types.COMPARE_IDENTICAL: // ===
+                evaluateBinaryExpression(compareIdenticalMethod, expression);
+                break;
+
+            case Types.COMPARE_EQUAL: // ==
+                evaluateBinaryExpression(compareEqualMethod, expression);
+                break;
+
+            case Types.COMPARE_NOT_EQUAL:
+                evaluateBinaryExpression(compareNotEqualMethod, expression);
+                break;
+
+            case Types.COMPARE_TO:
+                evaluateCompareTo(expression);
+                break;
+
+            case Types.COMPARE_GREATER_THAN:
+                evaluateBinaryExpression(compareGreaterThanMethod, expression);
+                break;
+
+            case Types.COMPARE_GREATER_THAN_EQUAL:
+                evaluateBinaryExpression(compareGreaterThanEqualMethod, expression);
+                break;
+
+            case Types.COMPARE_LESS_THAN:
+                evaluateBinaryExpression(compareLessThanMethod, expression);
+                break;
+
+            case Types.COMPARE_LESS_THAN_EQUAL:
+                evaluateBinaryExpression(compareLessThanEqualMethod, expression);
+                break;
+
+            case Types.LOGICAL_AND:
+                evaluateLogicalAndExpression(expression);
+                break;
+
+            case Types.LOGICAL_OR:
+                evaluateLogicalOrExpression(expression);
+                break;
+
+            case Types.BITWISE_AND:
+                evaluateBinaryExpression("and", expression);
+                break;
+
+            case Types.BITWISE_AND_EQUAL:
+                evaluateBinaryExpressionWithAssignment("and", expression);
+                break;
+
+            case Types.BITWISE_OR:
+                evaluateBinaryExpression("or", expression);
+                break;
+
+            case Types.BITWISE_OR_EQUAL:
+                evaluateBinaryExpressionWithAssignment("or", expression);
+                break;
+
+            case Types.BITWISE_XOR:
+                evaluateBinaryExpression("xor", expression);
+                break;
+
+            case Types.BITWISE_XOR_EQUAL:
+                evaluateBinaryExpressionWithAssignment("xor", expression);
+                break;
+
+            case Types.PLUS:
+                evaluateBinaryExpression("plus", expression);
+                break;
+
+            case Types.PLUS_EQUAL:
+                evaluateBinaryExpressionWithAssignment("plus", expression);
+                break;
+
+            case Types.MINUS:
+                evaluateBinaryExpression("minus", expression);
+                break;
+
+            case Types.MINUS_EQUAL:
+                evaluateBinaryExpressionWithAssignment("minus", expression);
+                break;
+
+            case Types.MULTIPLY:
+                evaluateBinaryExpression("multiply", expression);
+                break;
+
+            case Types.MULTIPLY_EQUAL:
+                evaluateBinaryExpressionWithAssignment("multiply", expression);
+                break;
+
+            case Types.DIVIDE:
+                evaluateBinaryExpression("div", expression);
+                break;
+
+            case Types.DIVIDE_EQUAL:
+                //SPG don't use divide since BigInteger implements directly
+                //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
+                evaluateBinaryExpressionWithAssignment("div", expression);
+                break;
+
+            case Types.INTDIV:
+                evaluateBinaryExpression("intdiv", expression);
+                break;
+
+            case Types.INTDIV_EQUAL:
+                evaluateBinaryExpressionWithAssignment("intdiv", expression);
+                break;
+
+            case Types.MOD:
+                evaluateBinaryExpression("mod", expression);
+                break;
+
+            case Types.MOD_EQUAL:
+                evaluateBinaryExpressionWithAssignment("mod", expression);
+                break;
+
+            case Types.POWER:
+                evaluateBinaryExpression("power", expression);
+                break;
+
+            case Types.POWER_EQUAL:
+                evaluateBinaryExpressionWithAssignment("power", expression);
+                break;
+
+            case Types.LEFT_SHIFT:
+                evaluateBinaryExpression("leftShift", expression);
+                break;
+
+            case Types.LEFT_SHIFT_EQUAL:
+                evaluateBinaryExpressionWithAssignment("leftShift", expression);
+                break;
+
+            case Types.RIGHT_SHIFT:
+                evaluateBinaryExpression("rightShift", expression);
+                break;
+
+            case Types.RIGHT_SHIFT_EQUAL:
+                evaluateBinaryExpressionWithAssignment("rightShift", expression);
+                break;
+
+            case Types.RIGHT_SHIFT_UNSIGNED:
+                evaluateBinaryExpression("rightShiftUnsigned", expression);
+                break;
+
+            case Types.RIGHT_SHIFT_UNSIGNED_EQUAL:
+                evaluateBinaryExpressionWithAssignment("rightShiftUnsigned", expression);
+                break;
+
+            case Types.KEYWORD_INSTANCEOF:
+                evaluateInstanceof(expression);
+                break;
+
+            case Types.FIND_REGEX:
+                evaluateBinaryExpression(findRegexMethod, expression);
+                break;
+
+            case Types.MATCH_REGEX:
+                evaluateBinaryExpression(matchRegexMethod, expression);
+                break;
+
+            case Types.LEFT_SQUARE_BRACKET:
+                if (leftHandExpression) {
+                    throwException("Should not be called here. Possible reason: postfix operation on array.");
+                    // This is handled right now in the evaluateEqual()
+                    // should support this here later
+                    //evaluateBinaryExpression("putAt", expression);
+                } else {
+                    evaluateBinaryExpression("getAt", expression);
+                }
+                break;
+
+            case Types.KEYWORD_IN:
+                evaluateBinaryExpression(isCaseMethod, expression);
+                break;
+
+            default:
+                throwException("Operation: " + expression.getOperation() + " not supported");
+        }
+    }
+
+    private void load(Expression exp) {
+
+        boolean wasLeft = leftHandExpression;
+        leftHandExpression = false;
+//        if (CREATE_DEBUG_INFO)
+//            helper.mark("-- loading expression: " + exp.getClass().getName() +
+//                    " at [" + exp.getLineNumber() + ":" + exp.getColumnNumber() + "]");
+        //exp.visit(this);
+        visitAndAutoboxBoolean(exp);
+//        if (CREATE_DEBUG_INFO)
+//            helper.mark(" -- end of loading --");
+
+        leftHandExpression = wasLeft;
+    }
+
+    public void visitPostfixExpression(PostfixExpression expression) {
+        switch (expression.getOperation().getType()) {
+            case Types.PLUS_PLUS:
+                evaluatePostfixMethod("next", expression.getExpression());
+                break;
+            case Types.MINUS_MINUS:
+                evaluatePostfixMethod("previous", expression.getExpression());
+                break;
+        }
+    }
+
+    private void throwException(String s) {
+        throw new RuntimeParserException(s, currentASTNode);
+    }
+
+    public void visitPrefixExpression(PrefixExpression expression) {
+        switch (expression.getOperation().getType()) {
+            case Types.PLUS_PLUS:
+                evaluatePrefixMethod("next", expression.getExpression());
+                break;
+            case Types.MINUS_MINUS:
+                evaluatePrefixMethod("previous", expression.getExpression());
+                break;
+        }
+    }
+
+    public void visitClosureExpression(ClosureExpression expression) {
+        ClassNode innerClass = createClosureClass(expression);
+        addInnerClass(innerClass);
+        innerClass.addInterface(ClassHelper.GENERATED_CLOSURE_Type);
+        String innerClassinternalName = BytecodeHelper.getClassInternalName(innerClass);
+
+        passingClosureParams = true;
+        List constructors = innerClass.getDeclaredConstructors();
+        ConstructorNode node = (ConstructorNode) constructors.get(0);
+
+        Parameter[] localVariableParams = node.getParameters();
+
+        mv.visitTypeInsn(NEW, innerClassinternalName);
+        mv.visitInsn(DUP);
+        if (isStaticMethod() && !classNode.declaresInterface(ClassHelper.GENERATED_CLOSURE_Type.getName())) {
+            visitClassExpression(new ClassExpression(classNode));
+            visitClassExpression(new ClassExpression(getOutermostClass()));
+        } else {
+            mv.visitVarInsn(ALOAD, 0);
+            loadThis();
+        }
+
+        // now lets load the various parameters we're passing
+        // we start at index 1 because the first variable we pass
+        // is the owner instance and at this point it is already 
+        // on the stack
+        for (int i = 2; i < localVariableParams.length; i++) {
+            Parameter param = localVariableParams[i];
+            String name = param.getName();
+
+            // compileStack.containsVariable(name) means to ask if the variable is already declared
+            // compileStack.getScope().isReferencedClassVariable(name) means to ask if the variable is a field
+            // If it is no field and is not yet declared, then it is either a closure shared variable or 
+            // an already declared variable. 
+            if (!compileStack.containsVariable(name) && compileStack.getScope().isReferencedClassVariable(name)) {
+                visitFieldExpression(new FieldExpression(classNode.getField(name)));
+            } else {
+                Variable v = compileStack.getVariable(name, classNode.getSuperClass() != ClassHelper.CLOSURE_TYPE);
+                if (v == null) {
+                    // variable is not on stack because we are
+                    // inside a nested Closure and this variable
+                    // was not used before
+                    // then load it from the Closure field
+                    FieldNode field = classNode.getField(name);
+                    mv.visitVarInsn(ALOAD, 0);
+                    mv.visitFieldInsn(GETFIELD, internalClassName, name, BytecodeHelper.getTypeDescription(field.getType()));
+                    // and define it
+                    // Note:
+                    // we can simply define it here and don't have to
+                    // be afraid about name problems because a second
+                    // variable with that name is not allowed inside the closure
+                    param.setClosureSharedVariable(false);
+                    v = compileStack.defineVariable(param, true);
+                    param.setClosureSharedVariable(true);
+                    v.setHolder(true);
+                }
+                mv.visitVarInsn(ALOAD, v.getIndex());
+            }
+        }
+        passingClosureParams = false;
+
+        // we may need to pass in some other constructors
+        //cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V");
+        mv.visitMethodInsn(
+                INVOKESPECIAL,
+                innerClassinternalName,
+                "<init>",
+                BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, localVariableParams));
+    }
+
+    /**
+     * Loads either this object or if we're inside a closure then load the top level owner
+     */
+    protected void loadThisOrOwner() {
+        if (isInnerClass()) {
+            visitFieldExpression(new FieldExpression(classNode.getField("owner")));
+        } else {
+            loadThis();
+        }
+    }
+
+    public void visitRegexExpression(RegexExpression expression) {
+        expression.getRegex().visit(this);
+        regexPattern.call(mv);
+    }
+
+    /**
+     * Generate byte code for constants
+     *
+     * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152">Class field types</a>
+     */
+    public void visitConstantExpression(ConstantExpression expression) {
+        Object value = expression.getValue();
+        helper.loadConstant(value);
+    }
+
+    public void visitSpreadExpression(SpreadExpression expression) {
+        throw new GroovyBugError("SpreadExpression should not be visited here");
+    }
+
+    public void visitSpreadMapExpression(SpreadMapExpression expression) {
+        Expression subExpression = expression.getExpression();
+        subExpression.visit(this);
+        spreadMap.call(mv);
+    }
+
+    public void visitMethodPointerExpression(MethodPointerExpression expression) {
+        Expression subExpression = expression.getExpression();
+        subExpression.visit(this);
+        loadDynamicName(expression.getMethodName());
+        getMethodPointer.call(mv);
+    }
+
+    private void loadDynamicName(Expression name) {
+        if (name instanceof ConstantExpression) {
+            ConstantExpression ce = (ConstantExpression) name;
+            Object value = ce.getValue();
+            if (value instanceof String) {
+                helper.loadConstant(value);
+                return;
+            }
+        }
+        new CastExpression(ClassHelper.STRING_TYPE, name).visit(this);
+    }
+
+    public void visitUnaryMinusExpression(UnaryMinusExpression expression) {
+        Expression subExpression = expression.getExpression();
+        subExpression.visit(this);
+        unaryMinus.call(mv);
+    }
+
+    public void visitUnaryPlusExpression(UnaryPlusExpression expression) {
+        Expression subExpression = expression.getExpression();
+        subExpression.visit(this);
+        unaryPlus.call(mv);
+    }
+
+    public void visitBitwiseNegationExpression(BitwiseNegationExpression expression) {
+        Expression subExpression = expression.getExpression();
+        subExpression.visit(this);
+        bitwiseNegate.call(mv);
+    }
+
+    public void visitCastExpression(CastExpression expression) {
+        ClassNode type = expression.getType();
+        visitAndAutoboxBoolean(expression.getExpression());
+        doConvertAndCast(type, expression.getExpression(), expression.isIgnoringAutoboxing(), false, expression.isCoerce());
+    }
+
+    public void visitNotExpression(NotExpression expression) {
+        Expression subExpression = expression.getExpression();
+        subExpression.visit(this);
+        // if we do !object, then the cast to boolean will
+        // do the conversion of Object to boolean. so a simple
+        // call to unbox is enough here.
+        if (
+                !isComparisonExpression(subExpression) &&
+                        !(subExpression instanceof BooleanExpression)) {
+            helper.unbox(boolean.class);
+        }
+        helper.negateBoolean();
+    }
+
+    /**
+     * return a primitive boolean value of the BooleanExpresion.
+     *
+     * @param expression
+     */
+    public void visitBooleanExpression(BooleanExpression expression) {
+        compileStack.pushBooleanExpression();
+        expression.getExpression().visit(this);
+
+        if (!isComparisonExpression(expression.getExpression())) {
+// comment out for optimization when boolean values are not autoboxed for eg. function calls.
+//           Class typeClass = expression.getExpression().getTypeClass();
+//           if (typeClass != null && typeClass != boolean.class) {
+            helper.unbox(boolean.class); // to return a primitive boolean
+//            }
+        }
+        compileStack.pop();
+    }
+
+    private void makeInvokeMethodCall(MethodCallExpression call, boolean useSuper, MethodCallerMultiAdapter adapter) {
+        // receiver
+        // we operate on GroovyObject if possible
+        Expression objectExpression = call.getObjectExpression();
+        if (!isStaticMethod() && !isStaticContext() && isThisExpression(call.getObjectExpression())) {
+            objectExpression = new CastExpression(ClassHelper.make(GroovyObject.class), objectExpression);
+        }
+        // message name
+        Expression messageName = new CastExpression(ClassHelper.STRING_TYPE, call.getMethod());
+        if (useSuper) {
+            makeCall(new ClassExpression(getOutermostClass().getSuperClass()),
+                    objectExpression, messageName,
+                    call.getArguments(), adapter,
+                    call.isSafe(), call.isSpreadSafe(),
+                    false
+            );
+        } else {
+            makeCall(objectExpression, messageName,
+                    call.getArguments(), adapter,
+                    call.isSafe(), call.isSpreadSafe(),
+                    call.isImplicitThis()
+            );
+        }
+    }
+
+    private void makeCall(
+            Expression receiver, Expression message, Expression arguments,
+            MethodCallerMultiAdapter adapter,
+            boolean safe, boolean spreadSafe, boolean implicitThis
+    ) {
+        ClassNode cn = classNode;
+        if (isInClosure() && !implicitThis) {
+            cn = getOutermostClass();
+        }
+        makeCall(new ClassExpression(cn), receiver, message, arguments,
+                adapter, safe, spreadSafe, implicitThis);
+    }
+
+    private void makeCall(
+            ClassExpression sender,
+            Expression receiver, Expression message, Expression arguments,
+            MethodCallerMultiAdapter adapter,
+            boolean safe, boolean spreadSafe, boolean implicitThis
+    ) {
+        // ensure VariableArguments are read, not stored
+        boolean lhs = leftHandExpression;
+        leftHandExpression = false;
+
+        // sender
+        sender.visit(this);
+        // receiver
+        boolean oldVal = this.implicitThis;
+        this.implicitThis = implicitThis;
+        visitAndAutoboxBoolean(receiver);
+        this.implicitThis = oldVal;
+        // message
+        if (message != null) message.visit(this);
+
+        // arguments
+        boolean containsSpreadExpression = containsSpreadExpression(arguments);
+        int numberOfArguments = containsSpreadExpression ? -1 : argumentSize(arguments);
+        if (numberOfArguments > adapter.MAX_ARGS || containsSpreadExpression) {
+            ArgumentListExpression ae;
+            if (arguments instanceof ArgumentListExpression) {
+                ae = (ArgumentListExpression) arguments;
+            } else if (arguments instanceof TupleExpression) {
+                TupleExpression te = (TupleExpression) arguments;
+                ae = new ArgumentListExpression(te.getExpressions());
+            } else {
+                ae = new ArgumentListExpression();
+                ae.addExpression(arguments);
+            }
+            if (containsSpreadExpression) {
+                despreadList(ae.getExpressions(), true);
+            } else {
+                ae.visit(this);
+            }
+        } else if (numberOfArguments > 0) {
+            TupleExpression te = (TupleExpression) arguments;
+            for (int i = 0; i < numberOfArguments; i++) {
+                Expression argument = te.getExpression(i);
+                visitAndAutoboxBoolean(argument);
+                if (argument instanceof CastExpression) loadWrapper(argument);
+            }
+        }
+
+        adapter.call(mv, numberOfArguments, safe, spreadSafe);
+
+        leftHandExpression = lhs;
+    }
+
+    private void despreadList(List expressions, boolean wrap) {
+
+        ArrayList spreadIndexes = new ArrayList();
+        ArrayList spreadExpressions = new ArrayList();
+        ArrayList normalArguments = new ArrayList();
+        for (int i = 0; i < expressions.size(); i++) {
+            Object expr = expressions.get(i);
+            if (!(expr instanceof SpreadExpression)) {
+                normalArguments.add(expr);
+            } else {
+                spreadIndexes.add(new ConstantExpression(new Integer(i - spreadExpressions.size())));
+                spreadExpressions.add(((SpreadExpression) expr).getExpression());
+            }
+        }
+
+        //load normal arguments as array
+        visitTupleExpression(new ArgumentListExpression(normalArguments), wrap);
+        //load spread expressions as array
+        (new TupleExpression(spreadExpressions)).visit(this);
+        //load insertion index
+        (new ArrayExpression(ClassHelper.int_TYPE, spreadIndexes, null)).visit(this);
+        despreadList.call(mv);
+    }
+
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        onLineNumber(call, "visitMethodCallExpression: \"" + call.getMethod() + "\":");
+
+        Expression arguments = call.getArguments();
+        String methodName = call.getMethodAsString();
+        boolean isSuperMethodCall = usesSuper(call);
+        boolean isThisExpression = isThisExpression(call.getObjectExpression());
+
+        // are we a local variable
+        if (methodName != null && isThisExpression && isFieldOrVariable(methodName) && !classNode.hasPossibleMethod(methodName, arguments)) {
+            // lets invoke the closure method
+            visitVariableExpression(new VariableExpression(methodName));
+            if (arguments instanceof TupleExpression) {
+                arguments.visit(this);
+            } else {
+                new TupleExpression(arguments).visit(this);
+            }
+            invokeClosureMethod.call(mv);
+        } else {
+            MethodCallerMultiAdapter adapter = invokeMethod;
+            if (isThisExpression) adapter = invokeMethodOnCurrent;
+            if (isSuperMethodCall) adapter = invokeMethodOnSuper;
+            if (isStaticInvocation(call)) adapter = invokeStaticMethod;
+            makeInvokeMethodCall(call, isSuperMethodCall, adapter);
+        }
+    }
+
+    private boolean isStaticInvocation(MethodCallExpression call) {
+        if (!isThisExpression(call.getObjectExpression())) return false;
+        if (isStaticMethod()) return true;
+        return isStaticContext() && !call.isImplicitThis();
+    }
+
+    protected boolean emptyArguments(Expression arguments) {
+        return argumentSize(arguments) == 0;
+    }
+
+    protected static boolean containsSpreadExpression(Expression arguments) {
+        List args = null;
+        if (arguments instanceof TupleExpression) {
+            TupleExpression tupleExpression = (TupleExpression) arguments;
+            args = tupleExpression.getExpressions();
+        } else if (arguments instanceof ListExpression) {
+            ListExpression le = (ListExpression) arguments;
+            args = le.getExpressions();
+        } else {
+            return arguments instanceof SpreadExpression;
+        }
+        for (Iterator iter = args.iterator(); iter.hasNext();) {
+            if (iter.next() instanceof SpreadExpression) return true;
+        }
+        return false;
+    }
+
+    protected static int argumentSize(Expression arguments) {
+        if (arguments instanceof TupleExpression) {
+            TupleExpression tupleExpression = (TupleExpression) arguments;
+            int size = tupleExpression.getExpressions().size();
+            return size;
+        }
+        return 1;
+    }
+
+    public void visitStaticMethodCallExpression(StaticMethodCallExpression call) {
+        onLineNumber(call, "visitStaticMethodCallExpression: \"" + call.getMethod() + "\":");
+
+        makeCall(
+                new ClassExpression(call.getOwnerType()),
+                new ConstantExpression(call.getMethod()),
+                call.getArguments(),
+                invokeStaticMethod,
+                false, false, false);
+    }
+    
+    private void addGeneratedClosureConstructorCall(ConstructorCallExpression call) {
+        mv.visitVarInsn(ALOAD, 0);
+        ClassNode callNode = classNode.getSuperClass();
+        TupleExpression arguments = (TupleExpression) call.getArguments();
+        if (arguments.getExpressions().size()!=2) throw new GroovyBugError("expected 2 arguments for closure constructor super call, but got"+arguments.getExpressions().size());
+        arguments.getExpression(0).visit(this);
+        arguments.getExpression(1).visit(this);
+        Parameter p = new Parameter(ClassHelper.OBJECT_TYPE,"_p");
+        String descriptor = helper.getMethodDescriptor(ClassHelper.VOID_TYPE, new Parameter[]{p,p});
+        mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(callNode), "<init>", descriptor);
+    }
+
+    private void visitSpecialConstructorCall(ConstructorCallExpression call) {
+        if (classNode.declaresInterface(ClassHelper.GENERATED_CLOSURE_Type.getName())) {
+            addGeneratedClosureConstructorCall(call);
+            return;
+        }
+        
+        ClassNode callNode = classNode;
+        if (call.isSuperCall()) callNode = callNode.getSuperClass();
+        List constructors = sortConstructors(call, callNode);
+        call.getArguments().visit(this);
+        // keep Object[] on stack
+        mv.visitInsn(DUP);
+        // to select the constructor we need also the number of
+        // available constructors and the class we want to make
+        // the call on
+        helper.pushConstant(constructors.size());
+        visitClassExpression(new ClassExpression(callNode));
+        // removes one Object[] leaves the int containing the 
+        // call flags and the construtcor number
+        selectConstructorAndTransformArguments.call(mv);
+        // Object[],int -> int,Object[],int
+        // we need to examine the flags and maybe change the 
+        // Object[] later, so this reordering will do the job
+        mv.visitInsn(DUP_X1);
+        // test if rewrap flag is set
+        mv.visitInsn(ICONST_1);
+        mv.visitInsn(IAND);
+        Label afterIf = new Label();
+        mv.visitJumpInsn(IFEQ, afterIf);
+        // true part, so rewrap using the first argument
+        mv.visitInsn(ICONST_0);
+        mv.visitInsn(AALOAD);
+        mv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;");
+        mv.visitLabel(afterIf);
+        // here the stack is int,Object[], but we need the
+        // the int for our table, so swap it
+        mv.visitInsn(SWAP);
+        //load "this"
+        if (constructorNode!=null) {
+            mv.visitVarInsn(ALOAD, 0);
+        } else {
+            mv.visitTypeInsn(NEW, BytecodeHelper.getClassInternalName(callNode));
+        }
+        mv.visitInsn(SWAP);
+        //prepare switch with >>8        
+        mv.visitIntInsn(BIPUSH, 8);
+        mv.visitInsn(ISHR);
+        Label[] targets = new Label[constructors.size()];
+        int[] indices = new int[constructors.size()];
+        for (int i = 0; i < targets.length; i++) {
+            targets[i] = new Label();
+            indices[i] = i;
+        }
+        // create switch targets
+        Label defaultLabel = new Label();
+        Label afterSwitch = new Label();
+        mv.visitLookupSwitchInsn(defaultLabel, indices, targets);
+        for (int i = 0; i < targets.length; i++) {
+            mv.visitLabel(targets[i]);
+            // to keep the stack height, we need to leave
+            // one Object[] on the stack as last element. At the 
+            // same time, we need the Object[] on top of the stack
+            // to extract the parameters. 
+            if (constructorNode!=null) {
+                // in this case we need one "this", so a SWAP will exchange 
+                // "this" and Object[], a DUP_X1 will then copy the Object[]
+                /// to the last place in the stack: 
+                //     Object[],this -SWAP-> this,Object[]
+                //     this,Object[] -DUP_X1-> Object[],this,Object[] 
+                mv.visitInsn(SWAP);
+                mv.visitInsn(DUP_X1);
+            } else {
+                // in this case we need two "this" in between and the Object[]
+                // at the bottom of the stack as well as on top for our invokeSpecial            
+                // So we do DUP_X1, DUP2_X1, POP 
+                //     Object[],this -DUP_X1-> this,Object[],this
+                //     this,Object[],this -DUP2_X1-> Object[],this,this,Object[],this
+                //     Object[],this,this,Object[],this -POP->  Object[],this,this,Object[]
+                mv.visitInsn(DUP_X1);
+                mv.visitInsn(DUP2_X1);
+                mv.visitInsn(POP);
+            }            
+
+            ConstructorNode cn = (ConstructorNode) constructors.get(i);
+            String descriptor = helper.getMethodDescriptor(ClassHelper.VOID_TYPE, cn.getParameters());
+            // unwrap the Object[] and make transformations if needed
+            // that means, to duplicate the Object[], make a cast with possible
+            // unboxing and then swap it with the Object[] for each parameter
+            Parameter[] parameters = cn.getParameters();
+            for (int p = 0; p < parameters.length; p++) {
+                mv.visitInsn(DUP);
+                helper.pushConstant(p);
+                mv.visitInsn(AALOAD);
+                ClassNode type = parameters[p].getType();
+                if (ClassHelper.isPrimitiveType(type)) {
+                    helper.unbox(type);
+                } else {
+                    helper.doCast(type);
+                }
+                helper.swapWithObject(type);
+            }
+            // at the end we remove the Object[]
+            mv.visitInsn(POP);
+            // make the constructor call
+            mv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper.getClassInternalName(callNode), "<init>", descriptor);
+            mv.visitJumpInsn(GOTO, afterSwitch);
+        }
+        mv.visitLabel(defaultLabel);
+        // this part should never be reached!
+        mv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
+        mv.visitInsn(DUP);
+        mv.visitLdcInsn("illegal constructor number");
+        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/IllegalArgumentException", "<init>", "(Ljava/lang/String;)V");
+        mv.visitInsn(ATHROW);
+        mv.visitLabel(afterSwitch);
+        
+        // to keep the stack hight we kept one object on the stack
+        // for the switch, now we remove that object
+        if (constructorNode==null) {
+            // but in case we are not in a constructor we have an additional
+            // object on the stack, the result of our constructor call
+            // which we want to keep, so we swap the arguments to remove 
+            // the right one
+            mv.visitInsn(SWAP);
+        }
+        mv.visitInsn(POP);
+    }
+
+    private List sortConstructors(ConstructorCallExpression call, ClassNode callNode) {
+        // sort in a new list to prevent side effects
+        List constructors = new ArrayList(callNode.getDeclaredConstructors());
+        Comparator comp = new Comparator() {
+            public int compare(Object arg0, Object arg1) {
+                ConstructorNode c0 = (ConstructorNode) arg0;
+                ConstructorNode c1 = (ConstructorNode) arg1;
+                String descriptor0 = helper.getMethodDescriptor(ClassHelper.VOID_TYPE, c0.getParameters());
+                String descriptor1 = helper.getMethodDescriptor(ClassHelper.VOID_TYPE, c1.getParameters());
+                return descriptor0.compareTo(descriptor1);
+            }
+        };
+        Collections.sort(constructors, comp);
+        return constructors;
+    }
+
+    public void visitConstructorCallExpression(ConstructorCallExpression call) {
+        onLineNumber(call, "visitConstructorCallExpression: \"" + call.getType().getName() + "\":");
+
+        if (call.isSpecialCall()) {
+            visitSpecialConstructorCall(call);
+            return;
+        }
+
+        Expression arguments = call.getArguments();
+        if (arguments instanceof TupleExpression) {
+            TupleExpression tupleExpression = (TupleExpression) arguments;
+            int size = tupleExpression.getExpressions().size();
+            if (size == 0) {
+                arguments = MethodCallExpression.NO_ARGUMENTS;
+            }
+        }
+
+        Expression receiverClass = new ClassExpression(call.getType());
+        makeCall(
+                receiverClass, null,
+                arguments,
+                invokeNew, false, false, false
+        );
+    }
+
+    private static String makeFieldClassName(ClassNode type) {
+        String internalName = BytecodeHelper.getClassInternalName(type);
+        StringBuffer ret = new StringBuffer(internalName.length());
+        for (int i = 0; i < internalName.length(); i++) {
+            char c = internalName.charAt(i);
+            if (c == '/') {
+                ret.append('$');
+            } else if (c == ';') {
+                //append nothing -> delete ';'
+            } else {
+                ret.append(c);
+            }
+        }
+        return ret.toString();
+    }
+
+    private static String getStaticFieldName(ClassNode type) {
+        ClassNode componentType = type;
+        String prefix = "";
+        for (; componentType.isArray(); componentType = componentType.getComponentType()) {
+            prefix += "$";
+        }
+        if (prefix.length() != 0) prefix = "array" + prefix;
+        String name = prefix + "class$" + makeFieldClassName(componentType);
+        return name;
+    }
+
+    private void visitAttributeOrProperty(PropertyExpression expression, MethodCallerMultiAdapter adapter) {
+        Expression objectExpression = expression.getObjectExpression();
+        if (isThisOrSuper(objectExpression)) {
+            // let's use the field expression if it's available
+            String name = expression.getPropertyAsString();
+            if (name != null) {
+                FieldNode field = null;
+                if (isSuperExpression(objectExpression)) {
+                    field = classNode.getSuperClass().getField(name);
+                } else {
+                    field = classNode.getField(name);
+                }
+                if (field != null) {
+                    visitFieldExpression(new FieldExpression(field));
+                    return;
+                }
+            }
+            if (isSuperExpression(objectExpression)) {
+                String prefix;
+                if (leftHandExpression) {
+                    prefix = "set";
+                } else {
+                    prefix = "get";
+                }
+                String propName = prefix + MetaClassHelper.capitalize(name);
+                visitMethodCallExpression(new MethodCallExpression(objectExpression, propName, MethodCallExpression.NO_ARGUMENTS));
+                return;
+            }
+        }
+
+        // arguments already on stack if any
+        makeCall(
+                objectExpression, // receiver
+                new CastExpression(ClassHelper.STRING_TYPE, expression.getProperty()), // messageName
+                MethodCallExpression.NO_ARGUMENTS,
+                adapter,
+                expression.isSafe(), expression.isSpreadSafe(), expression.isImplicitThis()
+        );
+    }
+
+    private boolean isStaticContext() {
+        if (!isInClosure()) return false;
+        if (constructorNode != null) return false;
+        return classNode.isStaticClass() || methodNode.isStatic();
+    }
+
+    public void visitPropertyExpression(PropertyExpression expression) {
+        Expression objectExpression = expression.getObjectExpression();
+        MethodCallerMultiAdapter adapter;
+        if (leftHandExpression) {
+            adapter = setProperty;
+            if (isGroovyObject(objectExpression)) adapter = setGroovyObjectProperty;
+            if (isStaticContext() && isThisOrSuper(objectExpression)) adapter = setProperty;
+        } else {
+            adapter = getProperty;
+            if (isGroovyObject(objectExpression)) adapter = getGroovyObjectProperty;
+            if (isStaticContext() && isThisOrSuper(objectExpression)) adapter = getProperty;
+        }
+        visitAttributeOrProperty(expression, adapter);
+    }
+
+    public void visitAttributeExpression(AttributeExpression expression) {
+        Expression objectExpression = expression.getObjectExpression();
+        MethodCallerMultiAdapter adapter;
+        if (leftHandExpression) {
+            adapter = setField;
+            if (isGroovyObject(objectExpression)) adapter = setGroovyObjectField;
+            if (usesSuper(expression)) adapter = setFieldOnSuper;
+        } else {
+            adapter = getField;
+            if (isGroovyObject(objectExpression)) adapter = getGroovyObjectField;
+            if (usesSuper(expression)) adapter = getFieldOnSuper;
+        }
+        visitAttributeOrProperty(expression, adapter);
+    }
+
+    protected boolean isGroovyObject(Expression objectExpression) {
+        return isThisExpression(objectExpression);
+    }
+
+    public void visitFieldExpression(FieldExpression expression) {
+        FieldNode field = expression.getField();
+
+        if (field.isStatic()) {
+            if (leftHandExpression) {
+                storeStaticField(expression);
+            } else {
+                loadStaticField(expression);
+            }
+        } else {
+            if (leftHandExpression) {
+                storeThisInstanceField(expression);
+            } else {
+                loadInstanceField(expression);
+            }
+        }
+    }
+
+    /**
+     * @param fldExp
+     */
+    public void loadStaticField(FieldExpression fldExp) {
+        FieldNode field = fldExp.getField();
+        boolean holder = field.isHolder() && !isInClosureConstructor();
+        ClassNode type = field.getType();
+
+        String ownerName = (field.getOwner().equals(classNode))
+                ? internalClassName
+                : BytecodeHelper.getClassInternalName(field.getOwner());
+        if (holder) {
+            mv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
+            mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
+        } else {
+            mv.visitFieldInsn(GETSTATIC, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
+            if (ClassHelper.isPrimitiveType(type)) {
+                helper.box(type);
+            } else {
+            }
+        }
+    }
+
+    /**
+     * RHS instance field. should move most of the code in the BytecodeHelper
+     *
+     * @param fldExp
+     */
+    public void loadInstanceField(FieldExpression fldExp) {
+        FieldNode field = fldExp.getField();
+        boolean holder = field.isHolder() && !isInClosureConstructor();
+        ClassNode type = field.getType();
+        String ownerName = (field.getOwner().equals(classNode))
+                ? internalClassName
+                : helper.getClassInternalName(field.getOwner());
+
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitFieldInsn(GETFIELD, ownerName, fldExp.getFieldName(), BytecodeHelper.getTypeDescription(type));
+
+        if (holder) {
+            mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
+        } else {
+            if (ClassHelper.isPrimitiveType(type)) {
+                helper.box(type);
+            } else {
+            }
+        }
+    }
+
+    public void storeThisInstanceField(FieldExpression expression) {
+        FieldNode field = expression.getField();
+
+        boolean holder = field.isHolder() && !isInClosureConstructor();
+        ClassNode type = field.getType();
+
+        String ownerName = (field.getOwner().equals(classNode)) ?
+                internalClassName : BytecodeHelper.getClassInternalName(field.getOwner());
+        if (holder) {
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitFieldInsn(GETFIELD, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
+            mv.visitInsn(SWAP);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
+        } else {
+            if (isInClosureConstructor()) {
+                helper.doCast(type);
+            } else if (!ClassHelper.isPrimitiveType(type)) {
+                doConvertAndCast(type);
+            }
+            mv.visitVarInsn(ALOAD, 0);
+            //helper.swapObjectWith(type);
+            mv.visitInsn(SWAP);
+            helper.unbox(type);
+            helper.putField(field, ownerName);
+        }
+    }
+
+
+    public void storeStaticField(FieldExpression expression) {
+        FieldNode field = expression.getField();
+
+        boolean holder = field.isHolder() && !isInClosureConstructor();
+
+        ClassNode type = field.getType();
+
+        String ownerName = (field.getOwner().equals(classNode))
+                ? internalClassName
+                : helper.getClassInternalName(field.getOwner());
+        if (holder) {
+            mv.visitFieldInsn(GETSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
+            mv.visitInsn(SWAP);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
+        } else {
+            helper.doCast(type);
+            mv.visitFieldInsn(PUTSTATIC, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(type));
+        }
+    }
+
+    protected void visitOuterFieldExpression(FieldExpression expression, ClassNode outerClassNode, int steps, boolean first) {
+        FieldNode field = expression.getField();
+        boolean isStatic = field.isStatic();
+
+        int tempIdx = compileStack.defineTemporaryVariable(field, leftHandExpression && first);
+
+        if (steps > 1 || !isStatic) {
+            mv.visitVarInsn(ALOAD, 0);
+            mv.visitFieldInsn(
+                    GETFIELD,
+                    internalClassName,
+                    "owner",
+                    BytecodeHelper.getTypeDescription(outerClassNode));
+        }
+
+        if (steps == 1) {
+            int opcode = (leftHandExpression) ? ((isStatic) ? PUTSTATIC : PUTFIELD) : ((isStatic) ? GETSTATIC : GETFIELD);
+            String ownerName = BytecodeHelper.getClassInternalName(outerClassNode);
+
+            if (leftHandExpression) {
+                mv.visitVarInsn(ALOAD, tempIdx);
+                boolean holder = field.isHolder() && !isInClosureConstructor();
+                if (!holder) {
+                    doConvertAndCast(field.getType());
+                }
+            }
+            mv.visitFieldInsn(opcode, ownerName, expression.getFieldName(), BytecodeHelper.getTypeDescription(field.getType()));
+            if (!leftHandExpression) {
+                if (ClassHelper.isPrimitiveType(field.getType())) {
+                    helper.box(field.getType());
+                }
+            }
+        } else {
+            visitOuterFieldExpression(expression, outerClassNode.getOuterClass(), steps - 1, false);
+        }
+    }
+
+
+    /**
+     * Visits a bare (unqualified) variable expression.
+     */
+
+    public void visitVariableExpression(VariableExpression expression) {
+
+        String variableName = expression.getName();
+
+        //-----------------------------------------------------------------------
+        // SPECIAL CASES
+
+        //
+        // "this" for static methods is the Class instance
+
+        ClassNode classNode = this.classNode;
+        if (isInClosure()) classNode = getOutermostClass();
+
+        if (variableName.equals("this")) {
+            if (isStaticMethod() || (!implicitThis && isStaticContext())) {
+                visitClassExpression(new ClassExpression(classNode));
+            } else {
+                loadThis();
+            }
+            return;
+        }
+
+        //
+        // "super" also requires special handling
+
+        if (variableName.equals("super")) {
+            if (isStaticMethod()) {
+                visitClassExpression(new ClassExpression(classNode.getSuperClass()));
+            } else {
+                loadThis();
+            }
+            return;                                               // <<< FLOW CONTROL <<<<<<<<<
+        }
+
+        Variable variable = compileStack.getVariable(variableName, false);
+
+        VariableScope scope = compileStack.getScope();
+        if (variable == null) {
+            processClassVariable(variableName);
+        } else {
+            processStackVariable(variable);
+        }
+    }
+
+    private void loadThis() {
+        mv.visitVarInsn(ALOAD, 0);
+        if (!implicitThis && isInClosure()) {
+            mv.visitMethodInsn(
+                    INVOKEVIRTUAL,
+                    "groovy/lang/Closure",
+                    "getThisObject",
+                    "()Ljava/lang/Object;"
+            );
+        }
+    }
+
+    protected void processStackVariable(Variable variable) {
+        if (leftHandExpression) {
+            helper.storeVar(variable);
+        } else {
+            helper.loadVar(variable);
+        }
+        if (ASM_DEBUG) {
+            helper.mark("var: " + variable.getName());
+        }
+    }
+
+    protected void processClassVariable(String name) {
+        if (passingClosureParams && isInScriptBody()) {
+            // let's create a ScriptReference to pass into the closure
+            mv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/ScriptReference");
+            mv.visitInsn(DUP);
+
+            loadThisOrOwner();
+            mv.visitLdcInsn(name);
+
+            mv.visitMethodInsn(
+                    INVOKESPECIAL,
+                    "org/codehaus/groovy/runtime/ScriptReference",
+                    "<init>",
+                    "(Lgroovy/lang/Script;Ljava/lang/String;)V");
+        } else {
+            PropertyExpression pexp = new PropertyExpression(VariableExpression.THIS_EXPRESSION, name);
+            pexp.setImplicitThis(true);
+            visitPropertyExpression(pexp);
+        }
+    }
+
+
+    protected void processFieldAccess(String name, FieldNode field, int steps) {
+        FieldExpression expression = new FieldExpression(field);
+
+        if (steps == 0) {
+            visitFieldExpression(expression);
+        } else {
+            visitOuterFieldExpression(expression, classNode.getOuterClass(), steps, true);
+        }
+    }
+
+
+    /**
+     * @return true if we are in a script body, where all variables declared are no longer
+     *         local variables but are properties
+     */
+    protected boolean isInScriptBody() {
+        if (classNode.isScriptBody()) {
+            return true;
+        } else {
+            return classNode.isScript() && methodNode != null && methodNode.getName().equals("run");
+        }
+    }
+
+    /**
+     * @return true if this expression will have left a value on the stack
+     *         that must be popped
+     */
+    protected boolean isPopRequired(Expression expression) {
+        if (expression instanceof MethodCallExpression) {
+            if (expression.getType() == ClassHelper.VOID_TYPE) { // nothing on the stack
+                return false;
+            } else {
+                return true;
+            }
+        }
+        if (expression instanceof DeclarationExpression) {
+            return false;
+        }
+        if (expression instanceof BinaryExpression) {
+            BinaryExpression binExp = (BinaryExpression) expression;
+            switch (binExp.getOperation().getType()) {   // br todo should leave a copy of the value on the stack for all the assignemnt.
+//                case Types.EQUAL :   // br a copy of the right value is left on the stack (see evaluateEqual()) so a pop is required for a standalone assignment
+//                case Types.PLUS_EQUAL : // this and the following are related to evaluateBinaryExpressionWithAssignment()
+//                case Types.MINUS_EQUAL :
+//                case Types.MULTIPLY_EQUAL :
+//                case Types.DIVIDE_EQUAL :
+//                case Types.INTDIV_EQUAL :
+//                case Types.MOD_EQUAL :
+//                    return false;
+            }
+        }
+        if (expression instanceof ConstructorCallExpression) {
+            ConstructorCallExpression cce = (ConstructorCallExpression) expression;
+            return !cce.isSpecialCall();
+        }
+        return true;
+    }
+
+    protected void createInterfaceSyntheticStaticFields() {
+        if (syntheticStaticFields.isEmpty()) return;
+
+        addInnerClass(interfaceClassLoadingClass);
+
+        for (Iterator iter = syntheticStaticFields.iterator(); iter.hasNext();) {
+            String staticFieldName = (String) iter.next();
+            // generate a field node
+            interfaceClassLoadingClass.addField(staticFieldName, ACC_STATIC + ACC_SYNTHETIC, ClassHelper.CLASS_Type, null);
+        }
+    }
+
+    protected void createSyntheticStaticFields() {
+        for (Iterator iter = syntheticStaticFields.iterator(); iter.hasNext();) {
+            String staticFieldName = (String) iter.next();
+            // generate a field node
+            FieldNode fn = classNode.getField(staticFieldName);
+            if (fn != null) {
+                boolean type = fn.getType() == ClassHelper.CLASS_Type;
+                boolean modifiers = fn.getModifiers() == ACC_STATIC + ACC_SYNTHETIC;
+                if (type && modifiers) continue;
+                String text = "";
+                if (!type) text = " with wrong type: " + fn.getType() + " (java.lang.Class needed)";
+                if (!modifiers)
+                    text = " with wrong modifiers: " + fn.getModifiers() + " (" + (ACC_STATIC + ACC_SYNTHETIC) + " needed)";
+                throwException(
+                        "tried to set a static syntethic field " + staticFieldName + " in " + classNode.getName() +
+                                " for class resolving, but found alreeady a node of that" +
+                                " name " + text);
+            } else {
+                cv.visitField(ACC_STATIC + ACC_SYNTHETIC, staticFieldName, "Ljava/lang/Class;", null, null);
+            }
+        }
+
+        mv =
+                cv.visitMethod(
+                        ACC_STATIC + ACC_SYNTHETIC,
+                        "class$",
+                        "(Ljava/lang/String;)Ljava/lang/Class;",
+                        null,
+                        null);
+        Label l0 = new Label();
+        mv.visitLabel(l0);
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName", "(Ljava/lang/String;)Ljava/lang/Class;");
+        Label l1 = new Label();
+        mv.visitLabel(l1);
+        mv.visitInsn(ARETURN);
+        Label l2 = new Label();
+        mv.visitLabel(l2);
+        mv.visitVarInsn(ASTORE, 1);
+        mv.visitTypeInsn(NEW, "java/lang/NoClassDefFoundError");
+        mv.visitInsn(DUP);
+        mv.visitVarInsn(ALOAD, 1);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/ClassNotFoundException", "getMessage", "()Ljava/lang/String;");
+        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/NoClassDefFoundError", "<init>", "(Ljava/lang/String;)V");
+        mv.visitInsn(ATHROW);
+        mv.visitTryCatchBlock(l0, l2, l2, "java/lang/ClassNotFoundException"); // br using l2 as the 2nd param seems create the right table entry
+        mv.visitMaxs(3, 2);
+    }
+
+    /**
+     * load class object on stack
+     */
+    public void visitClassExpression(ClassExpression expression) {
+        ClassNode type = expression.getType();
+
+        if (ClassHelper.isPrimitiveType(type)) {
+            ClassNode objectType = ClassHelper.getWrapper(type);
+            mv.visitFieldInsn(GETSTATIC, BytecodeHelper.getClassInternalName(objectType), "TYPE", "Ljava/lang/Class;");
+        } else {
+            String staticFieldName;
+            if (type.equals(classNode)) {
+                staticFieldName = "class$0";
+                if (compileStack.getCurrentClassIndex() != -1) {
+                    mv.visitVarInsn(ALOAD, compileStack.getCurrentClassIndex());
+                    return;
+                }
+            } else if (type.equals(ClassHelper.METACLASS_TYPE)) {
+                staticFieldName = getStaticFieldName(type);
+                if (compileStack.getCurrentMetaClassIndex() != -1) {
+                    mv.visitVarInsn(ALOAD, compileStack.getCurrentMetaClassIndex());
+                    return;
+                }
+            } else {
+                staticFieldName = getStaticFieldName(type);
+            }
+
+            syntheticStaticFields.add(staticFieldName);
+
+            String internalClassName = this.internalClassName;
+            if (classNode.isInterface()) {
+                internalClassName = BytecodeHelper.getClassInternalName(interfaceClassLoadingClass);
+            }
+
+            mv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
+
+            Label l0 = new Label();
+            mv.visitJumpInsn(IFNONNULL, l0);
+            mv.visitLdcInsn(BytecodeHelper.getClassLoadingTypeDescription(type));
+            mv.visitMethodInsn(INVOKESTATIC, internalClassName, "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
+            mv.visitInsn(DUP);
+            mv.visitFieldInsn(PUTSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
+            Label l1 = new Label();
+            mv.visitJumpInsn(GOTO, l1);
+            mv.visitLabel(l0);
+            mv.visitFieldInsn(GETSTATIC, internalClassName, staticFieldName, "Ljava/lang/Class;");
+            mv.visitLabel(l1);
+
+            if (type.equals(classNode)) {
+                mv.visitInsn(DUP);
+                int index = compileStack.defineTemporaryVariable("class$0", ClassHelper.CLASS_Type, true);
+                compileStack.setCurrentClassIndex(index);
+            } else if (type.equals(ClassHelper.METACLASS_TYPE)) {
+                mv.visitInsn(DUP);
+                int index = compileStack.defineTemporaryVariable("meta$class$0", ClassHelper.CLASS_Type, true);
+                compileStack.setCurrentMetaClassIndex(index);
+            }
+        }
+    }
+
+    public void visitRangeExpression(RangeExpression expression) {
+        expression.getFrom().visit(this);
+        expression.getTo().visit(this);
+
+        helper.pushConstant(expression.isInclusive());
+
+        createRangeMethod.call(mv);
+    }
+
+    public void visitMapEntryExpression(MapEntryExpression expression) {
+        throw new GroovyBugError("MapEntryExpression should not be visited here");
+    }
+
+    public void visitMapExpression(MapExpression expression) {
+        List entries = expression.getMapEntryExpressions();
+        int size = entries.size();
+        helper.pushConstant(size * 2);
+
+        mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
+
+        int i = 0;
+        for (Iterator iter = entries.iterator(); iter.hasNext();) {
+            Object object = iter.next();
+            MapEntryExpression entry = (MapEntryExpression) object;
+
+            mv.visitInsn(DUP);
+            helper.pushConstant(i++);
+            visitAndAutoboxBoolean(entry.getKeyExpression());
+            mv.visitInsn(AASTORE);
+
+            mv.visitInsn(DUP);
+            helper.pushConstant(i++);
+            visitAndAutoboxBoolean(entry.getValueExpression());
+            mv.visitInsn(AASTORE);
+        }
+        createMapMethod.call(mv);
+    }
+
+    public void visitArgumentlistExpression(ArgumentListExpression ale) {
+        if (containsSpreadExpression(ale)) {
+            despreadList(ale.getExpressions(), true);
+        } else {
+            visitTupleExpression(ale, true);
+        }
+    }
+
+    public void visitTupleExpression(TupleExpression expression) {
+        visitTupleExpression(expression, false);
+    }
+
+    private void visitTupleExpression(TupleExpression expression, boolean useWrapper) {
+        int size = expression.getExpressions().size();
+
+        helper.pushConstant(size);
+
+        mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
+
+        for (int i = 0; i < size; i++) {
+            mv.visitInsn(DUP);
+            helper.pushConstant(i);
+            Expression argument = expression.getExpression(i);
+            visitAndAutoboxBoolean(argument);
+            if (useWrapper && argument instanceof CastExpression) loadWrapper(argument);
+
+            mv.visitInsn(AASTORE);
+        }
+    }
+
+    private void loadWrapper(Expression argument) {
+        ClassNode goalClass = argument.getType();
+        visitClassExpression(new ClassExpression(goalClass));
+        if (goalClass.isDerivedFromGroovyObject()) {
+            createGroovyObjectWrapperMethod.call(mv);
+        } else {
+            createPojoWrapperMethod.call(mv);
+        }
+    }
+
+    public void visitArrayExpression(ArrayExpression expression) {
+        ClassNode elementType = expression.getElementType();
+        String arrayTypeName = BytecodeHelper.getClassInternalName(elementType);
+        List sizeExpression = expression.getSizeExpression();
+
+        int size = 0;
+        int dimensions = 0;
+        if (sizeExpression != null) {
+            for (Iterator iter = sizeExpression.iterator(); iter.hasNext();) {
+                Expression element = (Expression) iter.next();
+                if (element == ConstantExpression.EMTPY_EXPRESSION) break;
+                dimensions++;
+                // lets convert to an int
+                visitAndAutoboxBoolean(element);
+                helper.unbox(int.class);
+            }
+        } else {
+            size = expression.getExpressions().size();
+            helper.pushConstant(size);
+        }
+
+        int storeIns = AASTORE;
+        if (sizeExpression != null) {
+            arrayTypeName = BytecodeHelper.getTypeDescription(expression.getType());
+            mv.visitMultiANewArrayInsn(arrayTypeName, dimensions);
+        } else if (ClassHelper.isPrimitiveType(elementType)) {
+            int primType = 0;
+            if (elementType == ClassHelper.boolean_TYPE) {
+                primType = T_BOOLEAN;
+                storeIns = BASTORE;
+            } else if (elementType == ClassHelper.char_TYPE) {
+                primType = T_CHAR;
+                storeIns = CASTORE;
+            } else if (elementType == ClassHelper.float_TYPE) {
+                primType = T_FLOAT;
+                storeIns = FASTORE;
+            } else if (elementType == ClassHelper.double_TYPE) {
+                primType = T_DOUBLE;
+                storeIns = DASTORE;
+            } else if (elementType == ClassHelper.byte_TYPE) {
+                primType = T_BYTE;
+                storeIns = BASTORE;
+            } else if (elementType == ClassHelper.short_TYPE) {
+                primType = T_SHORT;
+                storeIns = SASTORE;
+            } else if (elementType == ClassHelper.int_TYPE) {
+                primType = T_INT;
+                storeIns = IASTORE;
+            } else if (elementType == ClassHelper.long_TYPE) {
+                primType = T_LONG;
+                storeIns = LASTORE;
+            }
+            mv.visitIntInsn(NEWARRAY, primType);
+        } else {
+            mv.visitTypeInsn(ANEWARRAY, arrayTypeName);
+        }
+
+        for (int i = 0; i < size; i++) {
+            mv.visitInsn(DUP);
+            helper.pushConstant(i);
+            Expression elementExpression = expression.getExpression(i);
+            if (elementExpression == null) {
+                ConstantExpression.NULL.visit(this);
+            } else {
+                if (!elementType.equals(elementExpression.getType())) {
+                    visitCastExpression(new CastExpression(elementType, elementExpression, true));
+                } else {
+                    visitAndAutoboxBoolean(elementExpression);
+                }
+            }
+            mv.visitInsn(storeIns);
+        }
+
+        if (sizeExpression == null && ClassHelper.isPrimitiveType(elementType)) {
+            int par = compileStack.defineTemporaryVariable("par", true);
+            mv.visitVarInsn(ALOAD, par);
+        }
+    }
+
+    public void visitClosureListExpression(ClosureListExpression expression) {
+        compileStack.pushVariableScope(expression.getVariableScope());
+
+        List expressions = expression.getExpressions();
+        final int size = expressions.size();
+        // init declarations
+        LinkedList declarations = new LinkedList();
+        for (int i = 0; i < size; i++) {
+            Object expr = expressions.get(i);
+            if (expr instanceof DeclarationExpression) {
+                declarations.add(expr);
+                DeclarationExpression de = (DeclarationExpression) expr;
+                BinaryExpression be = new BinaryExpression(
+                        de.getLeftExpression(),
+                        de.getOperation(),
+                        de.getRightExpression());
+                expressions.set(i, be);
+                de.setRightExpression(ConstantExpression.NULL);
+                visitDeclarationExpression(de);
+            }
+        }
+
+        LinkedList instructions = new LinkedList();
+        BytecodeSequence seq = new BytecodeSequence(instructions);
+        BlockStatement bs = new BlockStatement();
+        bs.addStatement(seq);
+        Parameter closureIndex = new Parameter(ClassHelper.int_TYPE, "__closureIndex");
+        ClosureExpression ce = new ClosureExpression(new Parameter[]{closureIndex}, bs);
+        ce.setVariableScope(expression.getVariableScope());
+
+        // to keep stack hight put a null on stack
+        instructions.add(ConstantExpression.NULL);
+
+        // init table
+        final Label dflt = new Label();
+        final Label tableEnd = new Label();
+        final Label[] labels = new Label[size];
+        instructions.add(new BytecodeInstruction() {
+            public void visit(MethodVisitor mv) {
+                mv.visitVarInsn(ILOAD, 1);
+                mv.visitTableSwitchInsn(0, size - 1, dflt, labels);
+            }
+        });
+
+        // visit cases
+        for (int i = 0; i < size; i++) {
+            final Label label = new Label();
+            Object expr = expressions.get(i);
+            final boolean isStatement = expr instanceof Statement;
+            labels[i] = label;
+            instructions.add(new BytecodeInstruction() {
+                public void visit(MethodVisitor mv) {
+                    mv.visitLabel(label);
+                    // expressions will leave a value on stack, statements not
+                    // so expressions need to pop the alibi null
+                    if (!isStatement) mv.visitInsn(POP);
+                }
+            });
+            instructions.add(expr);
+            instructions.add(new BytecodeInstruction() {
+                public void visit(MethodVisitor mv) {
+                    mv.visitJumpInsn(GOTO, tableEnd);
+                }
+            });
+        }
+
+        // default case
+        {
+            instructions.add(new BytecodeInstruction() {
+                public void visit(MethodVisitor mv) {
+                    mv.visitLabel(dflt);
+                }
+            });
+            ConstantExpression text = new ConstantExpression("invalid index for closure");
+            ConstructorCallExpression cce = new ConstructorCallExpression(ClassHelper.make(IllegalArgumentException.class), text);
+            ThrowStatement ts = new ThrowStatement(cce);
+            instructions.add(ts);
+        }
+
+        // return
+        instructions.add(new BytecodeInstruction() {
+            public void visit(MethodVisitor mv) {
+                mv.visitLabel(tableEnd);
+                mv.visitInsn(ARETURN);
+            }
+        });
+
+        // load main Closure
+        visitClosureExpression(ce);
+
+        // we need later an array to store the curried
+        // closures, so we create it here and ave it
+        // in a temporary variable
+        helper.pushConstant(size);
+        mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
+        int listArrayVar = compileStack.defineTemporaryVariable("_listOfClosures", true);
+
+        // add curried versions
+        for (int i = 0; i < size; i++) {
+            // stack: closure
+
+            // we need to create a curried closure version
+            // so we store the type on stack
+            mv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/CurriedClosure");
+            // stack: closure, type
+            // for a constructor call we need the type two times 
+
+            // and the closure after them
+            mv.visitInsn(DUP2);
+            mv.visitInsn(SWAP);
+            // stack: closure,type,type,closure
+
+            // so we can create the curried closure
+            helper.pushConstant(i);
+            mv.visitMethodInsn(INVOKESPECIAL, "org/codehaus/groovy/runtime/CurriedClosure", "<init>", "(Lgroovy/lang/Closure;I)V");
+            // stack: closure,curriedClosure
+
+            // we need to save the result
+            mv.visitVarInsn(ALOAD, listArrayVar);
+            mv.visitInsn(SWAP);
+            helper.pushConstant(i);
+            mv.visitInsn(SWAP);
+            mv.visitInsn(AASTORE);
+            // stack: closure
+        }
+
+        // we don't need the closure any longer, so remove it
+        mv.visitInsn(POP);
+        // we load the array and create a list from it
+        mv.visitVarInsn(ALOAD, listArrayVar);
+        createListMethod.call(mv);
+
+        // remove the temporary variable to keep the 
+        // stack clean
+        compileStack.removeVar(listArrayVar);
+        compileStack.pop();
+    }
+
+    public void visitBytecodeSequence(BytecodeSequence bytecodeSequence) {
+        List instructions = bytecodeSequence.getInstructions();
+        for (Iterator iterator = instructions.iterator(); iterator.hasNext();) {
+            Object part = iterator.next();
+            if (part == EmptyExpression.INSTANCE) {
+                mv.visitInsn(ACONST_NULL);
+            } else if (part instanceof Expression) {
+                visitAndAutoboxBoolean((Expression) part);
+            } else if (part instanceof Statement) {
+                Statement stm = (Statement) part;
+                stm.visit(this);
+                mv.visitInsn(ACONST_NULL);
+            } else {
+                BytecodeInstruction runner = (BytecodeInstruction) part;
+                runner.visit(mv);
+            }
+        }
+    }
+
+    public void visitListExpression(ListExpression expression) {
+        int size = expression.getExpressions().size();
+        boolean containsSpreadExpression = containsSpreadExpression(expression);
+        if (!containsSpreadExpression) {
+            helper.pushConstant(size);
+
+            mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
+
+            for (int i = 0; i < size; i++) {
+                mv.visitInsn(DUP);
+                helper.pushConstant(i);
+                visitAndAutoboxBoolean(expression.getExpression(i));
+                mv.visitInsn(AASTORE);
+            }
+        } else {
+            despreadList(expression.getExpressions(), false);
+        }
+        createListMethod.call(mv);
+    }
+
+    public void visitGStringExpression(GStringExpression expression) {
+
+        mv.visitTypeInsn(NEW, "org/codehaus/groovy/runtime/GStringImpl");
+        mv.visitInsn(DUP);
+
+        int size = expression.getValues().size();
+        helper.pushConstant(size);
+        mv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
+
+        for (int i = 0; i < size; i++) {
+            mv.visitInsn(DUP);
+            helper.pushConstant(i);
+            visitAndAutoboxBoolean(expression.getValue(i));
+            mv.visitInsn(AASTORE);
+        }
+
+        List strings = expression.getStrings();
+        size = strings.size();
+        helper.pushConstant(size);
+        mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
+
+        for (int i = 0; i < size; i++) {
+            mv.visitInsn(DUP);
+            helper.pushConstant(i);
+            mv.visitLdcInsn(((ConstantExpression) strings.get(i)).getValue());
+            mv.visitInsn(AASTORE);
+        }
+
+        mv.visitMethodInsn(INVOKESPECIAL, "org/codehaus/groovy/runtime/GStringImpl", "<init>", "([Ljava/lang/Object;[Ljava/lang/String;)V");
+    }
+
+    /**
+     * Note: ignore it. Annotation generation needs the current visitor.
+     */
+    public void visitAnnotations(AnnotatedNode node) {
+    }
+
+    private void visitAnnotations(AnnotatedNode targetNode, Object visitor) {
+        Map annotionMap = targetNode.getAnnotations();
+        if (annotionMap.isEmpty()) return;
+
+        Iterator it = annotionMap.values().iterator();
+        while (it.hasNext()) {
+            AnnotationNode an = (AnnotationNode) it.next();
+            //skip builtin properties
+            if (an.isBuiltIn()) continue;
+            if (an.hasSourceRetention()) continue;
+
+            AnnotationVisitor av = getAnnotationVisitor(targetNode, an, visitor);
+            visitAnnotationAttributes(an, av);
+            av.visitEnd();
+        }
+    }
+
+    // TODO remove dup between this and visitAnnotations
+    private void visitParameterAnnotations(Parameter parameter, int paramNumber, MethodVisitor mv) {
+        Map annotionMap = parameter.getAnnotations();
+        if (annotionMap.isEmpty()) return;
+
+        Iterator it = annotionMap.values().iterator();
+        while (it.hasNext()) {
+            AnnotationNode an = (AnnotationNode) it.next();
+            //skip builtin properties
+            if (an.isBuiltIn()) continue;
+            if (an.hasSourceRetention()) continue;
+
+            final String annotationDescriptor = BytecodeHelper.getTypeDescription(an.getClassNode());
+            AnnotationVisitor av = mv.visitParameterAnnotation(paramNumber, annotationDescriptor, an.hasRuntimeRetention());
+            visitAnnotationAttributes(an, av);
+            av.visitEnd();
+        }
+    }
+
+    private AnnotationVisitor getAnnotationVisitor(AnnotatedNode targetNode, AnnotationNode an, Object visitor) {
+        final String annotationDescriptor = BytecodeHelper.getTypeDescription(an.getClassNode());
+        if (targetNode instanceof MethodNode) {
+            return ((MethodVisitor) visitor).visitAnnotation(annotationDescriptor, an.hasRuntimeRetention());
+        } else if (targetNode instanceof FieldNode) {
+            return ((FieldVisitor) visitor).visitAnnotation(annotationDescriptor, an.hasRuntimeRetention());
+        } else if (targetNode instanceof ClassNode) {
+            return ((ClassVisitor) visitor).visitAnnotation(annotationDescriptor, an.hasRuntimeRetention());
+        }
+
+        throwException("Cannot create an AnnotationVisitor. Please report Groovy bug");
+
+        return null;
+    }
+
+    /**
+     * Generate the annotation attributes.
+     */
+    private void visitAnnotationAttributes(AnnotationNode an, AnnotationVisitor av) {
+        Map constantAttrs = new HashMap();
+        Map enumAttrs = new HashMap();
+        Map atAttrs = new HashMap();
+        Map arrayAttrs = new HashMap();
+
+        Iterator mIt = an.getMembers().keySet().iterator();
+        while (mIt.hasNext()) {
+            String name = (String) mIt.next();
+            Expression expr = an.getMember(name);
+            if (expr instanceof AnnotationConstantExpression) {
+                atAttrs.put(name, ((AnnotationConstantExpression) expr).getValue());
+            } else if (expr instanceof ConstantExpression) {
+                constantAttrs.put(name, ((ConstantExpression) expr).getValue());
+            } else if (expr instanceof ClassExpression) {
+                constantAttrs.put(name,
+                        Type.getType(BytecodeHelper.getTypeDescription(expr.getType())));
+            } else if (expr instanceof PropertyExpression) {
+                enumAttrs.put(name, expr);
+            } else if (expr instanceof ListExpression) {
+                arrayAttrs.put(name, expr);
+            }
+        }
+
+        for (Iterator it = constantAttrs.entrySet().iterator(); it.hasNext();) {
+            Map.Entry entry = (Map.Entry) it.next();
+            av.visit((String) entry.getKey(), entry.getValue());
+        }
+        for (Iterator it = enumAttrs.entrySet().iterator(); it.hasNext();) {
+            Map.Entry entry = (Map.Entry) it.next();
+            PropertyExpression propExp = (PropertyExpression) entry.getValue();
+            av.visitEnum((String) entry.getKey(),
+                    BytecodeHelper.getTypeDescription(propExp.getObjectExpression().getType()),
+                    String.valueOf(((ConstantExpression) propExp.getProperty()).getValue()));
+        }
+        for (Iterator it = atAttrs.entrySet().iterator(); it.hasNext();) {
+            Map.Entry entry = (Map.Entry) it.next();
+            AnnotationNode atNode = (AnnotationNode) entry.getValue();
+            AnnotationVisitor av2 = av.visitAnnotation((String) entry.getKey(),
+                    BytecodeHelper.getTypeDescription(atNode.getClassNode()));
+            visitAnnotationAttributes(atNode, av2);
+            av2.visitEnd();
+        }
+
+        visitArrayAttributes(an, arrayAttrs, av);
+    }
+
+    private void visitArrayAttributes(AnnotationNode an, Map arrayAttr, AnnotationVisitor av) {
+        if (arrayAttr.isEmpty()) return;
+
+        for (Iterator it = arrayAttr.entrySet().iterator(); it.hasNext();) {
+            Map.Entry entry = (Map.Entry) it.next();
+            String attrName = (String) entry.getKey();
+            ListExpression listExpr = (ListExpression) entry.getValue();
+            AnnotationVisitor av2 = av.visitArray(attrName);
+            List values = listExpr.getExpressions();
+            if (!values.isEmpty()) {
+                Expression expr = (Expression) values.get(0);
+                int arrayElementType = -1;
+                if (expr instanceof AnnotationConstantExpression) {
+                    arrayElementType = 1;
+                } else if (expr instanceof ConstantExpression) {
+                    arrayElementType = 2;
+                } else if (expr instanceof ClassExpression) {
+                    arrayElementType = 3;
+                } else if (expr instanceof PropertyExpression) {
+                    arrayElementType = 4;
+                }
+                for (Iterator exprIt = listExpr.getExpressions().iterator(); exprIt.hasNext();) {
+                    switch (arrayElementType) {
+                        case 1:
+                            AnnotationNode atAttr =
+                                    (AnnotationNode) ((AnnotationConstantExpression) exprIt.next()).getValue();
+                            AnnotationVisitor av3 = av2.visitAnnotation(null,
+                                    BytecodeHelper.getTypeDescription(atAttr.getClassNode()));
+                            visitAnnotationAttributes(atAttr, av3);
+                            av3.visitEnd();
+                            break;
+                        case 2:
+                            av2.visit(null, ((ConstantExpression) exprIt.next()).getValue());
+                            break;
+                        case 3:
+                            av2.visit(null, Type.getType(
+                                    BytecodeHelper.getTypeDescription(((Expression) exprIt.next()).getType())));
+                            break;
+                        case 4:
+                            PropertyExpression propExpr = (PropertyExpression) exprIt.next();
+                            av2.visitEnum(null,
+                                    BytecodeHelper.getTypeDescription(propExpr.getObjectExpression().getType()),
+                                    String.valueOf(((ConstantExpression) propExpr.getProperty()).getValue()));
+                            break;
+                    }
+                }
+
+            }
+            av2.visitEnd();
+        }
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+    protected boolean addInnerClass(ClassNode innerClass) {
+        innerClass.setModule(classNode.getModule());
+        return innerClasses.add(innerClass);
+    }
+
+    protected ClassNode createClosureClass(ClosureExpression expression) {
+        ClassNode outerClass = getOutermostClass();
+        String name = outerClass.getName() + "$"
+                + context.getNextClosureInnerName(outerClass, classNode, methodNode); // br added a more infomative name
+        boolean staticMethodOrInStaticClass = isStaticMethod() || classNode.isStaticClass();
+
+        Parameter[] parameters = expression.getParameters();
+        if (parameters == null) {
+            parameters = Parameter.EMPTY_ARRAY;
+        } else if (parameters.length == 0) {
+            // lets create a default 'it' parameter
+            parameters = new Parameter[]{new Parameter(ClassHelper.OBJECT_TYPE, "it", ConstantExpression.NULL)};
+        }
+
+        Parameter[] localVariableParams = getClosureSharedVariables(expression);
+        removeInitialValues(localVariableParams);
+
+        InnerClassNode answer = new InnerClassNode(outerClass, name, 0, ClassHelper.CLOSURE_TYPE); // closures are local inners and not public
+        answer.setEnclosingMethod(this.methodNode);
+        answer.setSynthetic(true);
+
+        if (staticMethodOrInStaticClass) {
+            answer.setStaticClass(true);
+        }
+        if (isInScriptBody()) {
+            answer.setScriptBody(true);
+        }
+        MethodNode method =
+                answer.addMethod("doCall", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, parameters, ClassNode.EMPTY_ARRAY, expression.getCode());
+        method.setSourcePosition(expression);
+
+        VariableScope varScope = expression.getVariableScope();
+        if (varScope == null) {
+            throw new RuntimeException(
+                    "Must have a VariableScope by now! for expression: " + expression + " class: " + name);
+        } else {
+            method.setVariableScope(varScope.copy());
+        }
+        if (parameters.length > 1
+                || (parameters.length == 1
+                && parameters[0].getType() != null
+                && parameters[0].getType() != ClassHelper.OBJECT_TYPE)) {
+
+            // lets add a typesafe call method
+            MethodNode call = answer.addMethod(
+                    "call",
+                    ACC_PUBLIC,
+                    ClassHelper.OBJECT_TYPE,
+                    parameters,
+                    ClassNode.EMPTY_ARRAY,
+                    new ReturnStatement(
+                            new MethodCallExpression(
+                                    VariableExpression.THIS_EXPRESSION,
+                                    "doCall",
+                                    new ArgumentListExpression(parameters))));
+            call.setSourcePosition(expression);
+        }
+
+        // lets make the constructor
+        BlockStatement block = new BlockStatement();
+        block.setSourcePosition(expression);
+        VariableExpression outer = new VariableExpression("_outerInstance");
+        outer.setSourcePosition(expression);
+        block.getVariableScope().putReferencedLocalVariable(outer);
+        VariableExpression thisObject = new VariableExpression("_thisObject");
+        thisObject.setSourcePosition(expression);
+        block.getVariableScope().putReferencedLocalVariable(thisObject);
+        TupleExpression conArgs = new TupleExpression(outer, thisObject);
+        block.addStatement(
+                new ExpressionStatement(
+                        new ConstructorCallExpression(
+                                ClassNode.SUPER,
+                                conArgs)));
+
+        // lets assign all the parameter fields from the outer context
+        for (int i = 0; i < localVariableParams.length; i++) {
+            Parameter param = localVariableParams[i];
+            String paramName = param.getName();
+            Expression initialValue = null;
+            ClassNode type = param.getType();
+            FieldNode paramField = null;
+            if (true) {
+                initialValue = new VariableExpression(paramName);
+                ClassNode realType = type;
+                type = ClassHelper.makeReference();
+                param.setType(ClassHelper.makeReference());
+                paramField = answer.addField(paramName, ACC_PRIVATE, type, initialValue);
+                paramField.setHolder(true);
+                String methodName = Verifier.capitalize(paramName);
+
+                // lets add a getter & setter
+                Expression fieldExp = new FieldExpression(paramField);
+                answer.addMethod(
+                        "get" + methodName,
+                        ACC_PUBLIC,
+                        realType,
+                        Parameter.EMPTY_ARRAY,
+                        ClassNode.EMPTY_ARRAY,
+                        new ReturnStatement(fieldExp));
+
+                /*
+                answer.addMethod(
+                    "set" + methodName,
+                    ACC_PUBLIC,
+                    "void",
+                    new Parameter[] { new Parameter(realType, "__value") },
+                    new ExpressionStatement(
+                        new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("__value"))));
+                        */
+            }
+        }
+
+        Parameter[] params = new Parameter[2 + localVariableParams.length];
+        params[0] = new Parameter(ClassHelper.OBJECT_TYPE, "_outerInstance");
+        params[1] = new Parameter(ClassHelper.OBJECT_TYPE, "_thisObject");
+        System.arraycopy(localVariableParams, 0, params, 2, localVariableParams.length);
+
+        ASTNode sn = answer.addConstructor(ACC_PUBLIC, params, ClassNode.EMPTY_ARRAY, block);
+        sn.setSourcePosition(expression);
+        return answer;
+    }
+
+    /**
+     * this method is called for local variables shared between scopes.
+     * These variables must not have init values because these would
+     * then in later steps be used to create multiple versions of the
+     * same method, in this case the constructor. A closure should not
+     * have more than one constructor!
+     */
+    private void removeInitialValues(Parameter[] params) {
+        for (int i = 0; i < params.length; i++) {
+            if (params[i].hasInitialExpression()) {
+                params[i] = new Parameter(params[i].getType(), params[i].getName());
+            }
+        }
+    }
+
+    protected Parameter[] getClosureSharedVariables(ClosureExpression ce) {
+        VariableScope scope = ce.getVariableScope();
+        Parameter[] ret = new Parameter[scope.getReferencedLocalVariablesCount()];
+        int index = 0;
+        for (Iterator iter = scope.getReferencedLocalVariablesIterator(); iter.hasNext();) {
+            org.codehaus.groovy.ast.Variable element = (org.codehaus.groovy.ast.Variable) iter.next();
+            Parameter p = new Parameter(element.getType(), element.getName());
+            ret[index] = p;
+            index++;
+        }
+        return ret;
+    }
+
+    protected ClassNode getOutermostClass() {
+        if (outermostClass == null) {
+            outermostClass = classNode;
+            while (outermostClass instanceof InnerClassNode) {
+                outermostClass = outermostClass.getOuterClass();
+            }
+        }
+        return outermostClass;
+    }
+
+    protected void doConvertAndCast(ClassNode type) {
+        doConvertAndCast(type, false);
+    }
+
+    protected void doConvertAndCast(ClassNode type, boolean coerce) {
+        if (type == ClassHelper.OBJECT_TYPE) return;
+        if (isValidTypeForCast(type)) {
+            visitClassExpression(new ClassExpression(type));
+            if (coerce) {
+                asTypeMethod.call(mv);
+            } else {
+                castToTypeMethod.call(mv);
+            }
+        }
+        helper.doCast(type);
+    }
+
+    protected void evaluateLogicalOrExpression(BinaryExpression expression) {
+        visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
+        Label l0 = new Label();
+        Label l2 = new Label();
+        mv.visitJumpInsn(IFEQ, l0);
+
+        mv.visitLabel(l2);
+
+        visitConstantExpression(ConstantExpression.TRUE);
+
+        Label l1 = new Label();
+        mv.visitJumpInsn(GOTO, l1);
+        mv.visitLabel(l0);
+
+        visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
+
+        mv.visitJumpInsn(IFNE, l2);
+
+        visitConstantExpression(ConstantExpression.FALSE);
+        mv.visitLabel(l1);
+    }
+
+    // todo: optimization: change to return primitive boolean. need to adjust the BinaryExpression and isComparisonExpression for
+    // consistancy.
+    protected void evaluateLogicalAndExpression(BinaryExpression expression) {
+        visitBooleanExpression(new BooleanExpression(expression.getLeftExpression()));
+        Label l0 = new Label();
+        mv.visitJumpInsn(IFEQ, l0);
+
+        visitBooleanExpression(new BooleanExpression(expression.getRightExpression()));
+
+        mv.visitJumpInsn(IFEQ, l0);
+
+        visitConstantExpression(ConstantExpression.TRUE);
+
+        Label l1 = new Label();
+        mv.visitJumpInsn(GOTO, l1);
+        mv.visitLabel(l0);
+
+        visitConstantExpression(ConstantExpression.FALSE);
+
+        mv.visitLabel(l1);
+    }
+
+    protected void evaluateBinaryExpression(String method, BinaryExpression expression) {
+        makeCall(
+                expression.getLeftExpression(),
+                new ConstantExpression(method),
+                new ArgumentListExpression(expression.getRightExpression()),
+                invokeMethod, false, false, false
+        );
+    }
+
+    protected void evaluateCompareTo(BinaryExpression expression) {
+        Expression leftExpression = expression.getLeftExpression();
+        leftExpression.visit(this);
+        if (isComparisonExpression(leftExpression)) {
+            helper.boxBoolean();
+        }
+
+        // if the right hand side is a boolean expression, we need to autobox
+        Expression rightExpression = expression.getRightExpression();
+        rightExpression.visit(this);
+        if (isComparisonExpression(rightExpression)) {
+            helper.boxBoolean();
+        }
+        compareToMethod.call(mv);
+    }
+
+    protected void evaluateBinaryExpressionWithAssignment(String method, BinaryExpression expression) {
+        Expression leftExpression = expression.getLeftExpression();
+        if (leftExpression instanceof BinaryExpression) {
+            BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
+            if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
+                // lets replace this assignment to a subscript operator with a
+                // method call
+                // e.g. x[5] += 10
+                // -> (x, [], 5), =, x[5] + 10
+                // -> methodCall(x, "putAt", [5, methodCall(x[5], "plus", 10)])
+
+                MethodCallExpression methodCall =
+                        new MethodCallExpression(
+                                expression.getLeftExpression(),
+                                method,
+                                new ArgumentListExpression(expression.getRightExpression()));
+
+                Expression safeIndexExpr = createReusableExpression(leftBinExpr.getRightExpression());
+
+                visitMethodCallExpression(
+                        new MethodCallExpression(
+                                leftBinExpr.getLeftExpression(),
+                                "putAt",
+                                new ArgumentListExpression(safeIndexExpr, methodCall)));
+                //cv.visitInsn(POP);
+                return;
+            }
+        }
+
+        evaluateBinaryExpression(method, expression);
+
+        // br to leave a copy of rvalue on the stack. see also isPopRequired()
+        mv.visitInsn(DUP);
+
+        leftHandExpression = true;
+        evaluateExpression(leftExpression);
+        leftHandExpression = false;
+    }
+
+    private void evaluateBinaryExpression(MethodCaller compareMethod, BinaryExpression expression) {
+        Expression leftExp = expression.getLeftExpression();
+        Expression rightExp = expression.getRightExpression();
+        load(leftExp);
+        load(rightExp);
+        compareMethod.call(mv);
+    }
+
+    protected void evaluateEqual(BinaryExpression expression) {
+        Expression leftExpression = expression.getLeftExpression();
+        if (leftExpression instanceof BinaryExpression) {
+            BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
+            if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
+                // lets replace this assignment to a subscript operator with a
+                // method call
+                // e.g. x[5] = 10
+                // -> (x, [], 5), =, 10
+                // -> methodCall(x, "putAt", [5, 10])
+
+                visitMethodCallExpression(
+                        new MethodCallExpression(
+                                leftBinExpr.getLeftExpression(),
+                                "putAt",
+                                new ArgumentListExpression(
+                                        new Expression[]{leftBinExpr.getRightExpression(), expression.getRightExpression()})));
+                // cv.visitInsn(POP); //this is realted to isPopRequired()
+                return;
+            }
+        }
+
+        // lets evaluate the RHS then hopefully the LHS will be a field
+        Expression rightExpression = expression.getRightExpression();
+        ClassNode type = getLHSType(leftExpression);
+        // lets not cast for primitive types as we handle these in field setting etc
+        if (ClassHelper.isPrimitiveType(type)) {
+            visitAndAutoboxBoolean(rightExpression);
+        } else if (type != ClassHelper.OBJECT_TYPE) {
+            visitCastExpression(new CastExpression(type, rightExpression));
+        } else {
+            visitAndAutoboxBoolean(rightExpression);
+        }
+
+        mv.visitInsn(DUP);  // to leave a copy of the rightexpression value on the stack after the assignment.
+        leftHandExpression = true;
+        leftExpression.visit(this);
+        leftHandExpression = false;
+    }
+
+    /**
+     * Deduces the type name required for some casting
+     *
+     * @return the type of the given (LHS) expression or null if it is java.lang.Object or it cannot be deduced
+     */
+    protected ClassNode getLHSType(Expression leftExpression) {
+        if (leftExpression instanceof VariableExpression) {
+            VariableExpression varExp = (VariableExpression) leftExpression;
+            ClassNode type = varExp.getType();
+            if (isValidTypeForCast(type)) {
+                return type;
+            }
+            String variableName = varExp.getName();
+            Variable variable = compileStack.getVariable(variableName, false);
+            if (variable != null) {
+                if (variable.isHolder()) {
+                    return type;
+                }
+                if (variable.isProperty()) return variable.getType();
+                type = variable.getType();
+                if (isValidTypeForCast(type)) {
+                    return type;
+                }
+            } else {
+                FieldNode field = classNode.getField(variableName);
+                if (field == null) {
+                    field = classNode.getOuterField(variableName);
+                }
+                if (field != null) {
+                    type = field.getType();
+                    if (!field.isHolder() && isValidTypeForCast(type)) {
+                        return type;
+                    }
+                }
+            }
+        } else if (leftExpression instanceof FieldExpression) {
+            FieldExpression fieldExp = (FieldExpression) leftExpression;
+            ClassNode type = fieldExp.getType();
+            if (isValidTypeForCast(type)) {
+                return type;
+            }
+        }
+        return ClassHelper.DYNAMIC_TYPE;
+    }
+
+    protected boolean isValidTypeForCast(ClassNode type) {
+        return type != ClassHelper.DYNAMIC_TYPE &&
+                type != ClassHelper.REFERENCE_TYPE;
+    }
+
+    protected void visitAndAutoboxBoolean(Expression expression) {
+        expression.visit(this);
+
+        if (isComparisonExpression(expression)) {
+            helper.boxBoolean(); // convert boolean to Boolean
+        }
+    }
+
+    private void execMethodAndStoreForSubscriptOperator(String method, Expression expression) {
+        // execute method
+        makeCall(
+                expression,
+                new ConstantExpression(method),
+                MethodCallExpression.NO_ARGUMENTS,
+                invokeMethod,false,false,false);
+        
+        // we need special code for arrays to store the result
+        boolean setResult = true;
+        if (expression instanceof BinaryExpression) {
+            BinaryExpression be = (BinaryExpression) expression;
+            if (be.getOperation().getType()==Types.LEFT_SQUARE_BRACKET) {
+                mv.visitInsn(DUP);
+                final int resultIdx = compileStack.defineTemporaryVariable("postfix_" + method, true);
+                BytecodeExpression result = new BytecodeExpression() {
+                    public void visit(GroovyCodeVisitor visitor) {
+                        mv.visitVarInsn(ALOAD, resultIdx);
+                    }
+                };
+                TupleExpression args = new ArgumentListExpression();
+                args.addExpression(be.getRightExpression());
+                args.addExpression(result);
+                makeCall(
+                        be.getLeftExpression(), new ConstantExpression("putAt"),
+                        args,
+                        invokeMethod,false,false,false);
+                mv.visitInsn(POP);
+                setResult = false;
+            } 
+        } else if (expression instanceof ConstantExpression) {
+            setResult = false;
+        }
+        
+        if (setResult) {
+            // we want to keep a value on stack, so we have to DUP here
+            if (expression instanceof VariableExpression ||
+                expression instanceof PropertyExpression) 
+            {
+                mv.visitInsn(DUP);
+            }
+            leftHandExpression = true;
+            expression.visit(this);
+            leftHandExpression = false;
+        }
+    }
+    
+    protected void evaluatePrefixMethod(String method, Expression expression) {
+        // execute Method
+        execMethodAndStoreForSubscriptOperator(method,expression);
+
+        // new value is already on stack, so nothing to do here
+    }
+
+    protected void evaluatePostfixMethod(String method, Expression expression) {
+        // load 
+        expression.visit(this);
+
+        // save value for later
+        int tempIdx = compileStack.defineTemporaryVariable("postfix_" + method, true);
+
+        // execute Method
+        execMethodAndStoreForSubscriptOperator(method,expression);
+        // remove the result of the method call
+        mv.visitInsn(POP);
+        
+        //reload saved value
+        mv.visitVarInsn(ALOAD, tempIdx);
+        compileStack.removeVar(tempIdx);
+    }
+
+    protected void evaluateInstanceof(BinaryExpression expression) {
+        visitAndAutoboxBoolean(expression.getLeftExpression());
+        Expression rightExp = expression.getRightExpression();
+        ClassNode classType = ClassHelper.DYNAMIC_TYPE;
+        if (rightExp instanceof ClassExpression) {
+            ClassExpression classExp = (ClassExpression) rightExp;
+            classType = classExp.getType();
+        } else {
+            throw new RuntimeException(
+                    "Right hand side of the instanceof keyword must be a class name, not: " + rightExp);
+        }
+        String classInternalName = BytecodeHelper.getClassInternalName(classType);
+        mv.visitTypeInsn(INSTANCEOF, classInternalName);
+    }
+
+    /**
+     * @return true if the given argument expression requires the stack, in
+     *         which case the arguments are evaluated first, stored in the
+     *         variable stack and then reloaded to make a method call
+     */
+    protected boolean argumentsUseStack(Expression arguments) {
+        return arguments instanceof TupleExpression || arguments instanceof ClosureExpression;
+    }
+
+    /**
+     * @return true if the given expression represents a non-static field
+     */
+    protected boolean isNonStaticField(Expression expression) {
+        FieldNode field = null;
+        if (expression instanceof VariableExpression) {
+            VariableExpression varExp = (VariableExpression) expression;
+            field = classNode.getField(varExp.getName());
+        } else if (expression instanceof FieldExpression) {
+            FieldExpression fieldExp = (FieldExpression) expression;
+            field = classNode.getField(fieldExp.getFieldName());
+        } else if (expression.getClass() == PropertyExpression.class) {
+            PropertyExpression fieldExp = (PropertyExpression) expression;
+            String possibleField = fieldExp.getPropertyAsString();
+            if (possibleField != null) field = classNode.getField(possibleField);
+        }
+      return field != null && !field.isStatic();
+    }
+
+    private static boolean isThisExpression(Expression expression) {
+        if (expression instanceof VariableExpression) {
+            VariableExpression varExp = (VariableExpression) expression;
+            return varExp.getName().equals("this");
+        }
+        return false;
+    }
+
+    private static boolean isSuperExpression(Expression expression) {
+        if (expression instanceof VariableExpression) {
+            VariableExpression varExp = (VariableExpression) expression;
+            return varExp.getName().equals("super");
+        }
+        return false;
+    }
+
+    private static boolean isThisOrSuper(Expression expression) {
+        return isThisExpression(expression) || isSuperExpression(expression);
+    }
+
+
+    /**
+     * For assignment expressions, return a safe expression for the LHS we can use
+     * to return the value
+     */
+    protected Expression createReturnLHSExpression(Expression expression) {
+        if (expression instanceof BinaryExpression) {
+            BinaryExpression binExpr = (BinaryExpression) expression;
+            if (binExpr.getOperation().isA(Types.ASSIGNMENT_OPERATOR)) {
+                return createReusableExpression(binExpr.getLeftExpression());
+            }
+        }
+        return null;
+    }
+
+    protected Expression createReusableExpression(Expression expression) {
+        ExpressionTransformer transformer = new ExpressionTransformer() {
+            public Expression transform(Expression expression) {
+                if (expression instanceof PostfixExpression) {
+                    PostfixExpression postfixExp = (PostfixExpression) expression;
+                    return postfixExp.getExpression();
+                } else if (expression instanceof PrefixExpression) {
+                    PrefixExpression prefixExp = (PrefixExpression) expression;
+                    return prefixExp.getExpression();
+                }
+                return expression;
+            }
+        };
+
+        // could just be a postfix / prefix expression or nested inside some other expression
+        return transformer.transform(expression.transformExpression(transformer));
+    }
+
+    protected boolean isComparisonExpression(Expression expression) {
+        if (expression instanceof BinaryExpression) {
+            BinaryExpression binExpr = (BinaryExpression) expression;
+            switch (binExpr.getOperation().getType()) {
+                case Types.COMPARE_EQUAL:
+                case Types.MATCH_REGEX:
+                case Types.COMPARE_GREATER_THAN:
+                case Types.COMPARE_GREATER_THAN_EQUAL:
+                case Types.COMPARE_LESS_THAN:
+                case Types.COMPARE_LESS_THAN_EQUAL:
+                case Types.COMPARE_IDENTICAL:
+                case Types.COMPARE_NOT_EQUAL:
+                case Types.KEYWORD_INSTANCEOF:
+                case Types.KEYWORD_IN:
+                    return true;
+            }
+        } else if (expression instanceof BooleanExpression) {
+            return true;
+        }
+        return false;
+    }
+
+    protected void onLineNumber(ASTNode statement, String message) {
+        int line = statement.getLineNumber();
+        int col = statement.getColumnNumber();
+        this.currentASTNode = statement;
+
+        if (line >= 0) {
+            lineNumber = line;
+            columnNumber = col;
+        }
+        if (line >= 0 && mv != null) {
+            Label l = new Label();
+            mv.visitLabel(l);
+            mv.visitLineNumber(line, l);
+            if (ASM_DEBUG) {
+                helper.mark(message + "[" + statement.getLineNumber() + ":" + statement.getColumnNumber() + "]");
+            }
+        }
+    }
+
+    private boolean isInnerClass() {
+        return classNode instanceof InnerClassNode;
+    }
+
+    /**
+     * @return true if the given name is a local variable or a field
+     */
+    protected boolean isFieldOrVariable(String name) {
+        return compileStack.containsVariable(name) || classNode.getField(name) != null;
+    }
+
+    /**
+     * @return if the type of the expression can be determined at compile time
+     *         then this method returns the type - otherwise null
+     */
+    protected ClassNode getExpressionType(Expression expression) {
+        if (isComparisonExpression(expression)) {
+            return ClassHelper.boolean_TYPE;
+        }
+        if (expression instanceof VariableExpression) {
+            if (expression == VariableExpression.THIS_EXPRESSION) {
+                return classNode;
+            } else if (expression == VariableExpression.SUPER_EXPRESSION) {
+                return classNode.getSuperClass();
+            }
+
+            VariableExpression varExpr = (VariableExpression) expression;
+            Variable variable = compileStack.getVariable(varExpr.getName(), false);
+            if (variable != null && !variable.isHolder()) {
+                ClassNode type = variable.getType();
+                if (!variable.isDynamicTyped()) return type;
+            }
+            if (variable == null) {
+                org.codehaus.groovy.ast.Variable var = (org.codehaus.groovy.ast.Variable) compileStack.getScope().getReferencedClassVariable(varExpr.getName());
+                if (var != null && !var.isDynamicTyped()) return var.getType();
+            }
+        }
+        return expression.getType();
+    }
+
+    protected boolean isInClosureConstructor() {
+        return constructorNode != null
+                && classNode.getOuterClass() != null
+                && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
+    }
+
+    protected boolean isInClosure() {
+        return classNode.getOuterClass() != null
+                && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
+    }
+
+    protected boolean isStaticMethod() {
+      return methodNode != null && methodNode.isStatic();
+    }
+
+    protected CompileUnit getCompileUnit() {
+        CompileUnit answer = classNode.getCompileUnit();
+        if (answer == null) {
+            answer = context.getCompileUnit();
+        }
+        return answer;
+    }
+
+    protected boolean isHolderVariable(Expression expression) {
+        if (expression instanceof FieldExpression) {
+            FieldExpression fieldExp = (FieldExpression) expression;
+            return fieldExp.getField().isHolder();
+        }
+        if (expression instanceof VariableExpression) {
+            VariableExpression varExp = (VariableExpression) expression;
+            Variable variable = compileStack.getVariable(varExp.getName(), false);
+            if (variable != null) {
+                return variable.isHolder();
+            }
+            FieldNode field = classNode.getField(varExp.getName());
+            if (field != null) {
+                return field.isHolder();
+            }
+        }
+        return false;
+    }
+
+    public static boolean usesSuper(MethodCallExpression call) {
+        Expression expression = call.getObjectExpression();
+        if (expression instanceof VariableExpression) {
+            VariableExpression varExp = (VariableExpression) expression;
+            String variable = varExp.getName();
+            return variable.equals("super");
+        }
+        return false;
+    }
+
+    public static boolean usesSuper(PropertyExpression pe) {
+        Expression expression = pe.getObjectExpression();
+        if (expression instanceof VariableExpression) {
+            VariableExpression varExp = (VariableExpression) expression;
+            String variable = varExp.getName();
+            return variable.equals("super");
+        }
+        return false;
+    }
+
+    protected int getBytecodeVersion() {
+        if (!classNode.isUsingGenerics() &&
+                !classNode.isAnnotated()) {
+            return Opcodes.V1_3;
+        }
+
+        final String target = getCompileUnit().getConfig().getTargetBytecode();
+        return CompilerConfiguration.POST_JDK5.equals(target) ? Opcodes.V1_5 : Opcodes.V1_3;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/BytecodeExpression.java b/groovy/src/main/org/codehaus/groovy/classgen/BytecodeExpression.java
new file mode 100644
index 0000000..b88cbdd
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/BytecodeExpression.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.GroovyCodeVisitor;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ExpressionTransformer;
+
+/**
+ * Represents some custom bytecode generation by the compiler
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class BytecodeExpression extends Expression {
+    public static BytecodeExpression NOP = new BytecodeExpression() {
+        public void visit(GroovyCodeVisitor visitor) {
+            //do nothing             
+        }
+    };
+    
+    
+    public BytecodeExpression() {
+    }
+    
+    public abstract void visit(GroovyCodeVisitor visitor);
+
+    public Expression transformExpression(ExpressionTransformer transformer) {
+        return this;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/BytecodeHelper.java b/groovy/src/main/org/codehaus/groovy/classgen/BytecodeHelper.java
new file mode 100644
index 0000000..ead1037
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/BytecodeHelper.java
@@ -0,0 +1,793 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * A helper class for bytecode generation with AsmClassGenerator.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @version $Revision$
+ */
+public class BytecodeHelper implements Opcodes {
+
+    private MethodVisitor mv;
+
+    public MethodVisitor getMethodVisitor() {
+        return mv;
+    }
+
+    public BytecodeHelper(MethodVisitor mv) {
+        this.mv = mv;
+    }
+
+    /**
+     * box the primitive value on the stack
+     *
+     * @param type
+     */
+    public void quickBoxIfNecessary(ClassNode type) {
+        String descr = getTypeDescription(type);
+        if (type == ClassHelper.boolean_TYPE) {
+            boxBoolean();
+        } else if (ClassHelper.isPrimitiveType(type) && type != ClassHelper.VOID_TYPE) {
+            ClassNode wrapper = ClassHelper.getWrapper(type);
+            String internName = getClassInternalName(wrapper);
+            mv.visitTypeInsn(NEW, internName);
+            mv.visitInsn(DUP);
+            if (type == ClassHelper.double_TYPE || type == ClassHelper.long_TYPE) {
+                mv.visitInsn(DUP2_X2);
+                mv.visitInsn(POP2);
+            } else {
+                mv.visitInsn(DUP2_X1);
+                mv.visitInsn(POP2);
+            }
+            mv.visitMethodInsn(INVOKESPECIAL, internName, "<init>", "(" + descr + ")V");
+        }
+    }
+
+    public void quickUnboxIfNecessary(ClassNode type) {
+        if (ClassHelper.isPrimitiveType(type) && type != ClassHelper.VOID_TYPE) { // todo care when BigDecimal or BigIneteger on stack
+            ClassNode wrapper = ClassHelper.getWrapper(type);
+            String internName = getClassInternalName(wrapper);
+            if (type == ClassHelper.boolean_TYPE) {
+                mv.visitTypeInsn(CHECKCAST, internName);
+                mv.visitMethodInsn(INVOKEVIRTUAL, internName, type.getName() + "Value", "()" + getTypeDescription(type));
+            } else { // numbers
+                mv.visitTypeInsn(CHECKCAST, "java/lang/Number");
+                mv.visitMethodInsn(INVOKEVIRTUAL, /*internName*/"java/lang/Number", type.getName() + "Value", "()" + getTypeDescription(type));
+            }
+        }
+    }
+
+    /**
+     * Generates the bytecode to autobox the current value on the stack
+     */
+    public void box(Class type) {
+        if (ReflectionCache.getCachedClass(type).isPrimitive && type != void.class) {
+            String returnString = "(" + getTypeDescription(type) + ")Ljava/lang/Object;";
+            mv.visitMethodInsn(INVOKESTATIC, getClassInternalName(DefaultTypeTransformation.class.getName()), "box", returnString);
+        }
+    }
+
+    public void box(ClassNode type) {
+        if (type.isPrimaryClassNode()) return;
+        box(type.getTypeClass());
+    }
+
+    /**
+     * Generates the bytecode to unbox the current value on the stack
+     */
+    public void unbox(Class type) {
+        if (type.isPrimitive() && type != Void.TYPE) {
+            String returnString = "(Ljava/lang/Object;)" + getTypeDescription(type);
+            mv.visitMethodInsn(
+                    INVOKESTATIC,
+                    getClassInternalName(DefaultTypeTransformation.class.getName()),
+                    type.getName() + "Unbox",
+                    returnString);
+        }
+    }
+
+    public void unbox(ClassNode type) {
+        if (type.isPrimaryClassNode()) return;
+        unbox(type.getTypeClass());
+    }
+
+    public static String getClassInternalName(ClassNode t) {
+        if (t.isPrimaryClassNode()) {
+            return getClassInternalName(t.getName());
+        }
+        return getClassInternalName(t.getTypeClass());
+    }
+
+    public static String getClassInternalName(Class t) {
+        return org.objectweb.asm.Type.getInternalName(t);
+    }
+
+    /**
+     * @return the ASM internal name of the type
+     */
+    public static String getClassInternalName(String name) {
+        return name.replace('.', '/');
+    }
+
+    /**
+     * @return the ASM method type descriptor
+     */
+    public static String getMethodDescriptor(ClassNode returnType, Parameter[] parameters) {
+        StringBuffer buffer = new StringBuffer("(");
+        for (int i = 0; i < parameters.length; i++) {
+            buffer.append(getTypeDescription(parameters[i].getType()));
+        }
+        buffer.append(")");
+        buffer.append(getTypeDescription(returnType));
+        return buffer.toString();
+    }
+
+    /**
+     * @return the ASM method type descriptor
+     */
+    public static String getMethodDescriptor(Class returnType, Class[] paramTypes) {
+        // lets avoid class loading
+        StringBuffer buffer = new StringBuffer("(");
+        for (int i = 0; i < paramTypes.length; i++) {
+            buffer.append(getTypeDescription(paramTypes[i]));
+        }
+        buffer.append(")");
+        buffer.append(getTypeDescription(returnType));
+        return buffer.toString();
+    }
+
+    public static String getTypeDescription(Class c) {
+        return org.objectweb.asm.Type.getDescriptor(c);
+    }
+
+    /**
+     * array types are special:
+     * eg.: String[]: classname: [Ljava.lang.String;
+     * Object:   classname: java.lang.Object
+     * int[] :   classname: [I
+     * unlike getTypeDescription '.' is not replaced by '/'.
+     * it seems that makes problems for
+     * the class loading if '.' is replaced by '/'
+     *
+     * @return the ASM type description for class loading
+     */
+    public static String getClassLoadingTypeDescription(ClassNode c) {
+        StringBuffer buf = new StringBuffer();
+        boolean array = false;
+        while (true) {
+            if (c.isArray()) {
+                buf.append('[');
+                c = c.getComponentType();
+                array = true;
+            } else {
+                if (ClassHelper.isPrimitiveType(c)) {
+                    buf.append(getTypeDescription(c));
+                } else {
+                    if (array) buf.append('L');
+                    buf.append(c.getName());
+                    if (array) buf.append(';');
+                }
+                return buf.toString();
+            }
+        }
+    }
+
+    /**
+     * array types are special:
+     * eg.: String[]: classname: [Ljava/lang/String;
+     * int[]: [I
+     *
+     * @return the ASM type description
+     */
+    public static String getTypeDescription(ClassNode c) {
+        return getTypeDescription(c, true);
+    }
+
+    /**
+     * array types are special:
+     * eg.: String[]: classname: [Ljava/lang/String;
+     * int[]: [I
+     *
+     * @return the ASM type description
+     */
+    private static String getTypeDescription(ClassNode c, boolean end) {
+        StringBuffer buf = new StringBuffer();
+        ClassNode d = c;
+        while (true) {
+            if (ClassHelper.isPrimitiveType(d)) {
+                char car;
+                if (d == ClassHelper.int_TYPE) {
+                    car = 'I';
+                } else if (d == ClassHelper.VOID_TYPE) {
+                    car = 'V';
+                } else if (d == ClassHelper.boolean_TYPE) {
+                    car = 'Z';
+                } else if (d == ClassHelper.byte_TYPE) {
+                    car = 'B';
+                } else if (d == ClassHelper.char_TYPE) {
+                    car = 'C';
+                } else if (d == ClassHelper.short_TYPE) {
+                    car = 'S';
+                } else if (d == ClassHelper.double_TYPE) {
+                    car = 'D';
+                } else if (d == ClassHelper.float_TYPE) {
+                    car = 'F';
+                } else /* long */ {
+                    car = 'J';
+                }
+                buf.append(car);
+                return buf.toString();
+            } else if (d.isArray()) {
+                buf.append('[');
+                d = d.getComponentType();
+            } else {
+                buf.append('L');
+                String name = d.getName();
+                int len = name.length();
+                for (int i = 0; i < len; ++i) {
+                    char car = name.charAt(i);
+                    buf.append(car == '.' ? '/' : car);
+                }
+                if (end) buf.append(';');
+                return buf.toString();
+            }
+        }
+    }
+
+    /**
+     * @return an array of ASM internal names of the type
+     */
+    public static String[] getClassInternalNames(ClassNode[] names) {
+        int size = names.length;
+        String[] answer = new String[size];
+        for (int i = 0; i < size; i++) {
+            answer[i] = getClassInternalName(names[i]);
+        }
+        return answer;
+    }
+
+    protected void pushConstant(boolean value) {
+        if (value) {
+            mv.visitInsn(ICONST_1);
+        } else {
+            mv.visitInsn(ICONST_0);
+        }
+    }
+
+    public void pushConstant(int value) {
+        switch (value) {
+            case 0:
+                mv.visitInsn(ICONST_0);
+                break;
+            case 1:
+                mv.visitInsn(ICONST_1);
+                break;
+            case 2:
+                mv.visitInsn(ICONST_2);
+                break;
+            case 3:
+                mv.visitInsn(ICONST_3);
+                break;
+            case 4:
+                mv.visitInsn(ICONST_4);
+                break;
+            case 5:
+                mv.visitInsn(ICONST_5);
+                break;
+            default:
+                if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
+                    mv.visitIntInsn(BIPUSH, value);
+                } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
+                    mv.visitIntInsn(SIPUSH, value);
+                } else {
+                    mv.visitLdcInsn(new Integer(value));
+                }
+        }
+    }
+
+    public void doCast(Class type) {
+        if (type != Object.class) {
+            if (type.isPrimitive() && type != Void.TYPE) {
+                unbox(type);
+            } else {
+                mv.visitTypeInsn(
+                        CHECKCAST,
+                        type.isArray() ? getTypeDescription(type) : getClassInternalName(type.getName()));
+            }
+        }
+    }
+
+    public void doCast(ClassNode type) {
+        if (type == ClassHelper.OBJECT_TYPE) return;
+        if (ClassHelper.isPrimitiveType(type) && type != ClassHelper.VOID_TYPE) {
+            unbox(type);
+        } else {
+            mv.visitTypeInsn(
+                    CHECKCAST,
+                    type.isArray() ? getTypeDescription(type) : getClassInternalName(type));
+        }
+    }
+
+    public void load(ClassNode type, int idx) {
+        if (type == ClassHelper.double_TYPE) {
+            mv.visitVarInsn(DLOAD, idx);
+        } else if (type == ClassHelper.float_TYPE) {
+            mv.visitVarInsn(FLOAD, idx);
+        } else if (type == ClassHelper.long_TYPE) {
+            mv.visitVarInsn(LLOAD, idx);
+        } else if (
+                type == ClassHelper.boolean_TYPE
+                        || type == ClassHelper.char_TYPE
+                        || type == ClassHelper.byte_TYPE
+                        || type == ClassHelper.int_TYPE
+                        || type == ClassHelper.short_TYPE) {
+            mv.visitVarInsn(ILOAD, idx);
+        } else {
+            mv.visitVarInsn(ALOAD, idx);
+        }
+    }
+
+    public void load(Variable v) {
+        load(v.getType(), v.getIndex());
+    }
+
+    public void store(Variable v, boolean markStart) {
+        ClassNode type = v.getType();
+        unbox(type);
+        int idx = v.getIndex();
+
+        if (type == ClassHelper.double_TYPE) {
+            mv.visitVarInsn(DSTORE, idx);
+        } else if (type == ClassHelper.float_TYPE) {
+            mv.visitVarInsn(FSTORE, idx);
+        } else if (type == ClassHelper.long_TYPE) {
+            mv.visitVarInsn(LSTORE, idx);
+        } else if (
+                type == ClassHelper.boolean_TYPE
+                        || type == ClassHelper.char_TYPE
+                        || type == ClassHelper.byte_TYPE
+                        || type == ClassHelper.int_TYPE
+                        || type == ClassHelper.short_TYPE) {
+            mv.visitVarInsn(ISTORE, idx);
+        } else {
+            mv.visitVarInsn(ASTORE, idx);
+        }
+    }
+
+    public void store(Variable v) {
+        store(v, false);
+    }
+
+    /**
+     * load the constant on the operand stack. primitives auto-boxed.
+     */
+    void loadConstant(Object value) {
+        if (value == null) {
+            mv.visitInsn(ACONST_NULL);
+        } else if (value instanceof String) {
+            mv.visitLdcInsn(value);
+        } else if (value instanceof Character) {
+            String className = "java/lang/Character";
+            mv.visitTypeInsn(NEW, className);
+            mv.visitInsn(DUP);
+            mv.visitLdcInsn(value);
+            String methodType = "(C)V";
+            mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", methodType);
+        } else if (value instanceof Number) {
+            /** todo it would be more efficient to generate class constants */
+            Number n = (Number) value;
+            String className = BytecodeHelper.getClassInternalName(value.getClass().getName());
+            mv.visitTypeInsn(NEW, className);
+            mv.visitInsn(DUP);
+            String methodType;
+            if (n instanceof Integer) {
+                //pushConstant(n.intValue());
+                mv.visitLdcInsn(n);
+                methodType = "(I)V";
+            } else if (n instanceof Double) {
+                mv.visitLdcInsn(n);
+                methodType = "(D)V";
+            } else if (n instanceof Float) {
+                mv.visitLdcInsn(n);
+                methodType = "(F)V";
+            } else if (n instanceof Long) {
+                mv.visitLdcInsn(n);
+                methodType = "(J)V";
+            } else if (n instanceof BigDecimal) {
+                mv.visitLdcInsn(n.toString());
+                methodType = "(Ljava/lang/String;)V";
+            } else if (n instanceof BigInteger) {
+                mv.visitLdcInsn(n.toString());
+                methodType = "(Ljava/lang/String;)V";
+            } else if (n instanceof Short) {
+                mv.visitLdcInsn(n);
+                methodType = "(S)V";
+            } else if (n instanceof Byte) {
+                mv.visitLdcInsn(n);
+                methodType = "(B)V";
+            } else {
+                throw new ClassGeneratorException(
+                        "Cannot generate bytecode for constant: " + value
+                                + " of type: " + value.getClass().getName()
+                                + ".  Numeric constant type not supported.");
+            }
+            mv.visitMethodInsn(INVOKESPECIAL, className, "<init>", methodType);
+        } else if (value instanceof Boolean) {
+            Boolean bool = (Boolean) value;
+            String text = (bool.booleanValue()) ? "TRUE" : "FALSE";
+            mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", text, "Ljava/lang/Boolean;");
+        } else if (value instanceof Class) {
+            Class vc = (Class) value;
+            if (vc.getName().equals("java.lang.Void")) {
+                // load nothing here for void
+            } else {
+                throw new ClassGeneratorException(
+                        "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName());
+            }
+        } else {
+            throw new ClassGeneratorException(
+                    "Cannot generate bytecode for constant: " + value + " of type: " + value.getClass().getName());
+        }
+    }
+
+
+    /**
+     * load the value of the variable on the operand stack. unbox it if it's a reference
+     *
+     * @param variable
+     */
+    public void loadVar(Variable variable) {
+        int index = variable.getIndex();
+        if (variable.isHolder()) {
+            mv.visitVarInsn(ALOAD, index);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;");
+        } else {
+            load(variable);
+            if (variable != Variable.THIS_VARIABLE && variable != Variable.SUPER_VARIABLE) {
+                box(variable.getType());
+            }
+        }
+    }
+
+    public void storeVar(Variable variable) {
+        String type = variable.getTypeName();
+        int index = variable.getIndex();
+
+        if (variable.isHolder()) {
+            mv.visitVarInsn(ALOAD, index);
+            mv.visitInsn(SWAP);
+            mv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V");
+        } else {
+            store(variable, false);
+        }
+    }
+
+    public void putField(FieldNode fld) {
+        putField(fld, getClassInternalName(fld.getOwner()));
+    }
+
+    public void putField(FieldNode fld, String ownerName) {
+        mv.visitFieldInsn(PUTFIELD, ownerName, fld.getName(), getTypeDescription(fld.getType()));
+    }
+
+    public void swapObjectWith(ClassNode type) {
+        if (type == ClassHelper.long_TYPE || type == ClassHelper.double_TYPE) {
+            mv.visitInsn(DUP_X2);
+            mv.visitInsn(POP);
+        } else {
+            mv.visitInsn(SWAP);
+        }
+    }
+
+    public void swapWithObject(ClassNode type) {
+        if (type == ClassHelper.long_TYPE || type == ClassHelper.double_TYPE) {
+            mv.visitInsn(DUP2_X1);
+            mv.visitInsn(POP2);
+        } else {
+            mv.visitInsn(SWAP);
+        }
+    }
+
+    public static ClassNode boxOnPrimitive(ClassNode type) {
+        if (!type.isArray()) return ClassHelper.getWrapper(type);
+        return boxOnPrimitive(type.getComponentType()).makeArray();
+    }
+
+    /**
+     * convert boolean to Boolean
+     */
+    public void boxBoolean() {
+        Label l0 = new Label();
+        mv.visitJumpInsn(IFEQ, l0);
+        mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "TRUE", "Ljava/lang/Boolean;");
+        Label l1 = new Label();
+        mv.visitJumpInsn(GOTO, l1);
+        mv.visitLabel(l0);
+        mv.visitFieldInsn(GETSTATIC, "java/lang/Boolean", "FALSE", "Ljava/lang/Boolean;");
+        mv.visitLabel(l1);
+    }
+
+    /**
+     * negate a boolean on stack. true->false, false->true
+     */
+    public void negateBoolean() {
+        // code to negate the primitive boolean
+        Label endLabel = new Label();
+        Label falseLabel = new Label();
+        mv.visitJumpInsn(IFNE, falseLabel);
+        mv.visitInsn(ICONST_1);
+        mv.visitJumpInsn(GOTO, endLabel);
+        mv.visitLabel(falseLabel);
+        mv.visitInsn(ICONST_0);
+        mv.visitLabel(endLabel);
+    }
+
+    /**
+     * load a message on the stack and remove it right away. Good for put a mark in the generated bytecode for debugging purpose.
+     *
+     * @param msg
+     */
+    public void mark(String msg) {
+        mv.visitLdcInsn(msg);
+        mv.visitInsn(POP);
+    }
+
+    /**
+     * returns a name that Class.forName() can take. Notablely for arrays:
+     * [I, [Ljava.lang.String; etc
+     * Regular object type:  java.lang.String
+     *
+     * @param name
+     */
+    public static String formatNameForClassLoading(String name) {
+        if (name.equals("int")
+                || name.equals("long")
+                || name.equals("short")
+                || name.equals("float")
+                || name.equals("double")
+                || name.equals("byte")
+                || name.equals("char")
+                || name.equals("boolean")
+                || name.equals("void")
+                ) {
+            return name;
+        }
+
+        if (name == null) {
+            return "java.lang.Object;";
+        }
+
+        if (name.startsWith("[")) {
+            return name.replace('/', '.');
+        }
+
+        if (name.startsWith("L")) {
+            name = name.substring(1);
+            if (name.endsWith(";")) {
+                name = name.substring(0, name.length() - 1);
+            }
+            return name.replace('/', '.');
+        }
+
+        String prefix = "";
+        if (name.endsWith("[]")) { // todo need process multi
+            prefix = "[";
+            name = name.substring(0, name.length() - 2);
+            if (name.equals("int")) {
+                return prefix + "I";
+            } else if (name.equals("long")) {
+                return prefix + "J";
+            } else if (name.equals("short")) {
+                return prefix + "S";
+            } else if (name.equals("float")) {
+                return prefix + "F";
+            } else if (name.equals("double")) {
+                return prefix + "D";
+            } else if (name.equals("byte")) {
+                return prefix + "B";
+            } else if (name.equals("char")) {
+                return prefix + "C";
+            } else if (name.equals("boolean")) {
+                return prefix + "Z";
+            } else {
+                return prefix + "L" + name.replace('/', '.') + ";";
+            }
+        }
+        return name.replace('/', '.');
+
+    }
+
+    public void dup() {
+        mv.visitInsn(DUP);
+    }
+
+    public void doReturn(ClassNode returnType) {
+        if (returnType == ClassHelper.double_TYPE) {
+            mv.visitInsn(DRETURN);
+        } else if (returnType == ClassHelper.float_TYPE) {
+            mv.visitInsn(FRETURN);
+        } else if (returnType == ClassHelper.long_TYPE) {
+            mv.visitInsn(LRETURN);
+        } else if (
+                returnType == ClassHelper.boolean_TYPE
+                        || returnType == ClassHelper.char_TYPE
+                        || returnType == ClassHelper.byte_TYPE
+                        || returnType == ClassHelper.int_TYPE
+                        || returnType == ClassHelper.short_TYPE) {
+            //byte,short,boolean,int are all IRETURN
+            mv.visitInsn(IRETURN);
+        } else if (returnType == ClassHelper.VOID_TYPE) {
+            mv.visitInsn(RETURN);
+        } else {
+            mv.visitInsn(ARETURN);
+        }
+
+    }
+
+    private static boolean hasGenerics(Parameter[] param) {
+        if (param.length == 0) return false;
+        for (int i = 0; i < param.length; i++) {
+            ClassNode type = param[i].getType();
+            if (type.getGenericsTypes() != null) return true;
+        }
+        return false;
+    }
+
+    public static String getGenericsMethodSignature(MethodNode node) {
+        GenericsType[] generics = node.getGenericsTypes();
+        Parameter[] param = node.getParameters();
+        ClassNode returnType = node.getReturnType();
+
+        if (generics == null && !hasGenerics(param) && returnType.getGenericsTypes() == null) return null;
+
+        StringBuffer ret = new StringBuffer(100);
+        getGenericsTypeSpec(ret, generics);
+
+        GenericsType[] paramTypes = new GenericsType[param.length];
+        for (int i = 0; i < param.length; i++) {
+            ClassNode pType = param[i].getType();
+            if (pType.getGenericsTypes() == null || !pType.isGenericsPlaceHolder()) {
+                paramTypes[i] = new GenericsType(pType);
+            } else {
+                paramTypes[i] = pType.getGenericsTypes()[0];
+            }
+        }
+        addSubTypes(ret, paramTypes, "(", ")");
+        if (returnType.isGenericsPlaceHolder()) {
+            addSubTypes(ret, returnType.getGenericsTypes(), "", "");
+        } else {
+            writeGenericsBounds(ret, new GenericsType(returnType), false);
+        }
+        return ret.toString();
+    }
+
+    private static boolean usesGenericsInClassSignature(ClassNode node) {
+        if (!node.isUsingGenerics()) return false;
+        if (node.getGenericsTypes() != null) return true;
+        ClassNode sclass = node.getUnresolvedSuperClass(false);
+        if (sclass.isUsingGenerics()) return true;
+        ClassNode[] interfaces = node.getInterfaces();
+        if (interfaces != null) {
+            for (int i = 0; i < interfaces.length; i++) {
+                if (interfaces[i].isUsingGenerics()) return true;
+            }
+        }
+
+        return false;
+    }
+
+    public static String getGenericsSignature(ClassNode node) {
+        if (!usesGenericsInClassSignature(node)) return null;
+        GenericsType[] genericsTypes = node.getGenericsTypes();
+        StringBuffer ret = new StringBuffer(100);
+        getGenericsTypeSpec(ret, genericsTypes);
+        GenericsType extendsPart = new GenericsType(node.getUnresolvedSuperClass(false));
+        writeGenericsBounds(ret, extendsPart, true);
+        ClassNode[] interfaces = node.getInterfaces();
+        for (int i = 0; i < interfaces.length; i++) {
+            GenericsType interfacePart = new GenericsType(interfaces[i]);
+            writeGenericsBounds(ret, interfacePart, false);
+        }
+        return ret.toString();
+    }
+
+    private static void getGenericsTypeSpec(StringBuffer ret, GenericsType[] genericsTypes) {
+        if (genericsTypes == null) return;
+        ret.append('<');
+        for (int i = 0; i < genericsTypes.length; i++) {
+            String name = genericsTypes[i].getName();
+            ret.append(name);
+            ret.append(':');
+            writeGenericsBounds(ret, genericsTypes[i], true);
+        }
+        ret.append('>');
+    }
+
+    public static String getGenericsBounds(ClassNode type) {
+        GenericsType[] genericsTypes = type.getGenericsTypes();
+        if (genericsTypes == null) return null;
+        StringBuffer ret = new StringBuffer(100);
+        if (type.isGenericsPlaceHolder()) {
+            addSubTypes(ret, type.getGenericsTypes(), "", "");
+        } else {
+            GenericsType gt = new GenericsType(type);
+            writeGenericsBounds(ret, gt, false);
+        }
+
+        return ret.toString();
+    }
+
+    private static void writeGenericsBoundType(StringBuffer ret, ClassNode printType, boolean writeInterfaceMarker) {
+        if (writeInterfaceMarker && printType.isInterface()) ret.append(":");
+        ret.append(getTypeDescription(printType, false));
+        addSubTypes(ret, printType.getGenericsTypes(), "<", ">");
+        if (!ClassHelper.isPrimitiveType(printType)) ret.append(";");
+    }
+
+    private static void writeGenericsBounds(StringBuffer ret, GenericsType type, boolean writeInterfaceMarker) {
+        if (type.getUpperBounds() != null) {
+            ClassNode[] bounds = type.getUpperBounds();
+            for (int i = 0; i < bounds.length; i++) {
+                writeGenericsBoundType(ret, bounds[i], writeInterfaceMarker);
+            }
+        } else if (type.getLowerBound() != null) {
+            writeGenericsBoundType(ret, type.getLowerBound(), writeInterfaceMarker);
+        } else {
+            writeGenericsBoundType(ret, type.getType(), writeInterfaceMarker);
+        }
+    }
+
+    private static void addSubTypes(StringBuffer ret, GenericsType[] types, String start, String end) {
+        if (types == null) return;
+        ret.append(start);
+        for (int i = 0; i < types.length; i++) {
+            String name = types[i].getName();
+            if (types[i].isPlaceholder()) {
+                ret.append('T');
+                ret.append(name);
+                ret.append(';');
+            } else if (types[i].isWildcard()) {
+                if (types[i].getUpperBounds() != null) {
+                    ret.append('+');
+                    writeGenericsBounds(ret, types[i], false);
+                } else if (types[i].getLowerBound() != null) {
+                    ret.append('-');
+                    writeGenericsBounds(ret, types[i], false);
+                } else {
+                    ret.append('*');
+                }
+            } else {
+                writeGenericsBounds(ret, types[i], false);
+            }
+        }
+        ret.append(end);
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/BytecodeInstruction.java b/groovy/src/main/org/codehaus/groovy/classgen/BytecodeInstruction.java
new file mode 100644
index 0000000..b4b802d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/BytecodeInstruction.java
@@ -0,0 +1,30 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.classgen;

+

+import org.objectweb.asm.MethodVisitor;

+

+/**

+ * Helper class used by the class generator. Usually

+ * an inner class is produced, that contains bytecode

+ * creation code in the visit method.

+ *

+ * @author Jochen Theodorou

+ */

+public abstract class BytecodeInstruction {

+    public abstract void visit(MethodVisitor mv);

+}

diff --git a/groovy/src/main/org/codehaus/groovy/classgen/BytecodeSequence.java b/groovy/src/main/org/codehaus/groovy/classgen/BytecodeSequence.java
new file mode 100644
index 0000000..a0e6959
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/BytecodeSequence.java
@@ -0,0 +1,72 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.classgen;

+

+import java.util.Iterator;

+import java.util.List;

+

+import org.codehaus.groovy.ast.ASTNode;

+import org.codehaus.groovy.ast.GroovyCodeVisitor;

+import org.codehaus.groovy.ast.stmt.Statement;

+

+/**

+ * This class repersents a sequence of BytecodeInstructions

+ * or ASTNodes. The evaluation is dpeending on the type of 

+ * the visitor.

+ * 

+ * @see BytecodeInstruction

+ * @see ASTNode

+ */

+

+public class BytecodeSequence extends Statement {

+    private final List instructions;

+    public BytecodeSequence(List instructions) {

+        this.instructions = instructions;

+    }

+    

+    /**

+     * Delegates to the visit method used for this class.

+     * If the visitor is a ClassGenerator, then 

+     * {@link ClassGenerator#visitBytecodeSequence(BytecodeSequence)}

+     * is called with this instance. If the visitor is no 

+     * ClassGenerator, then this method will call visit on

+     * each ASTNode element sotred by this class. If one 

+     * element is a BytecodeInstruction, then it will be skipped

+     * as it is no ASTNode. 

+     * 

+     * @param visitor the visitor

+     * @see ClassGenerator

+     */

+    public void visit(GroovyCodeVisitor visitor) {

+        if (visitor instanceof ClassGenerator) {

+            ClassGenerator gen = (ClassGenerator) visitor;

+            gen.visitBytecodeSequence(this);

+            return;

+        }

+        for (Iterator iterator = instructions.iterator(); iterator.hasNext();) {

+            Object part = (Object) iterator.next();

+            if (part instanceof ASTNode) {

+                ((ASTNode)part).visit(visitor);

+            }

+        }

+    }

+

+    public List getInstructions() {

+        return instructions;

+    }

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/classgen/ClassCompletionVerifier.java b/groovy/src/main/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
new file mode 100644
index 0000000..7ba8d66
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
@@ -0,0 +1,312 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ *
+ * Contributors:
+ * IBM - Initial API and implementation
+ * Groovy community - subsequent modifications
+ ******************************************************************************/
+package org.codehaus.groovy.classgen;
+
+import java.lang.reflect.Modifier;
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.control.SourceUnit;
+import org.objectweb.asm.Opcodes;
+import org.codehaus.groovy.syntax.Types;
+
+/**
+ * ClassCompletionVerifier
+ */
+public class ClassCompletionVerifier extends ClassCodeVisitorSupport {
+
+    private ClassNode currentClass;
+    private SourceUnit source;
+
+    public ClassCompletionVerifier(SourceUnit source) {
+        this.source = source;
+    }
+
+    public ClassNode getClassNode() {
+        return currentClass;
+    }
+
+    public void visitClass(ClassNode node) {
+        ClassNode oldClass = currentClass;
+        currentClass = node;
+        checkImplementsAndExtends(node);
+        if (source != null && !source.getErrorCollector().hasErrors()) {
+            checkClassForIncorrectModifiers(node);
+            checkClassForOverwritingFinal(node);
+            checkMethodsForIncorrectModifiers(node);
+            checkMethodsForOverwritingFinal(node);
+            checkNoAbstractMethodsNonabstractClass(node);
+        }
+        super.visitClass(node);
+        currentClass = oldClass;
+    }
+
+    private void checkNoAbstractMethodsNonabstractClass(ClassNode node) {
+        if (Modifier.isAbstract(node.getModifiers())) return;
+        List abstractMethods = node.getAbstractMethods();
+        if (abstractMethods == null) return;
+        for (Iterator iter = abstractMethods.iterator(); iter.hasNext();) {
+            MethodNode method = (MethodNode) iter.next();
+            String methodName = method.getTypeDescriptor();
+            addError("Can't have an abstract method in a non-abstract class." +
+                    " The " + getDescription(node) + " must be declared abstract or" +
+                    " the " + getDescription(method) + " must be implemented.", node);
+        }
+    }
+
+    private void checkClassForIncorrectModifiers(ClassNode node) {
+        checkClassForAbstractAndFinal(node);
+        checkClassForOtherModifiers(node);
+    }
+
+    private void checkClassForAbstractAndFinal(ClassNode node) {
+        if (!Modifier.isAbstract(node.getModifiers())) return;
+        if (!Modifier.isFinal(node.getModifiers())) return;
+        if (node.isInterface()) {
+            addError("The " + getDescription(node) +" must not be final. It is by definition abstract.", node);
+        } else {
+            addError("The " + getDescription(node) + " must not be both final and abstract.", node);
+        }
+    }
+
+    private void checkClassForOtherModifiers(ClassNode node) {
+        // TODO: work out why "synchronised" can't be used here
+        checkClassForModifier(node, Modifier.isTransient(node.getModifiers()), "transient");
+        checkClassForModifier(node, Modifier.isVolatile(node.getModifiers()), "volatile");
+    }
+
+    private void checkClassForModifier(ClassNode node, boolean condition, String modifierName) {
+        if (!condition) return;
+        addError("The " + getDescription(node) + " has an incorrect modifier " + modifierName + ".", node);
+    }
+
+    private String getDescription(ClassNode node) {
+        return (node.isInterface() ? "interface" : "class") + " '" + node.getName() + "'";
+    }
+
+    private String getDescription(MethodNode node) {
+        return "method '" + node.getTypeDescriptor() + "'";
+    }
+
+    private String getDescription(FieldNode node) {
+        return "field '" + node.getName() + "'";
+    }
+
+    private void checkAbstractDeclaration(MethodNode methodNode) {
+        if (!Modifier.isAbstract(methodNode.getModifiers())) return;
+        if (Modifier.isAbstract(currentClass.getModifiers())) return;
+        addError("Can't have an abstract method in a non-abstract class." +
+                " The " + getDescription(currentClass) + " must be declared abstract or the method '" +
+                methodNode.getTypeDescriptor() + "' must not be abstract.", methodNode);
+    }
+
+    private void checkClassForOverwritingFinal(ClassNode cn) {
+        ClassNode superCN = cn.getSuperClass();
+        if (superCN == null) return;
+        if (!Modifier.isFinal(superCN.getModifiers())) return;
+        StringBuffer msg = new StringBuffer();
+        msg.append("You are not allowed to overwrite the final ");
+        msg.append(getDescription(superCN));
+        msg.append(".");
+        addError(msg.toString(), cn);
+    }
+
+    private void checkImplementsAndExtends(ClassNode node) {
+        ClassNode cn = node.getSuperClass();
+        if (cn.isInterface() && !node.isInterface()) {
+            addError("You are not allowed to extend the " + getDescription(cn) + ", use implements instead.", node);
+        }
+        ClassNode[] interfaces = node.getInterfaces();
+        for (int i = 0; i < interfaces.length; i++) {
+            cn = interfaces[i];
+            if (!cn.isInterface()) {
+                addError("You are not allowed to implement the " + getDescription(cn) + ", use extends instead.", node);
+            }
+        }
+    }
+
+    private void checkMethodsForIncorrectModifiers(ClassNode cn) {
+        if (!cn.isInterface()) return;
+        List methods = cn.getMethods();
+        for (Iterator cnIter = methods.iterator(); cnIter.hasNext();) {
+            MethodNode method = (MethodNode) cnIter.next();
+            if (Modifier.isFinal(method.getModifiers())) {
+                addError("The " + getDescription(method) + " from " + getDescription(cn) +
+                        " must not be final. It is by definition abstract.", method);
+            }
+            if (Modifier.isStatic(method.getModifiers()) && !isConstructor(method)) {
+                addError("The " + getDescription(method) + " from " + getDescription(cn) +
+                        " must not be static. Only fields may be static in an interface.", method);
+            }
+        }
+    }
+
+    private boolean isConstructor(MethodNode method) {
+        return method.getName().equals("<clinit>");
+    }
+
+    private void checkMethodsForOverwritingFinal(ClassNode cn) {
+        List methods = cn.getMethods();
+        for (Iterator cnIter = methods.iterator(); cnIter.hasNext();) {
+            MethodNode method = (MethodNode) cnIter.next();
+            Parameter[] params = method.getParameters();
+            List superMethods = cn.getSuperClass().getMethods(method.getName());
+            for (Iterator iter = superMethods.iterator(); iter.hasNext();) {
+                MethodNode superMethod = (MethodNode) iter.next();
+                Parameter[] superParams = superMethod.getParameters();
+                if (!hasEqualParameterTypes(params, superParams)) continue;
+                if (!Modifier.isFinal(superMethod.getModifiers())) return;
+                addInvalidUseOfFinalError(method, params, superMethod.getDeclaringClass());
+                return;
+            }
+        }
+    }
+
+    private void addInvalidUseOfFinalError(MethodNode method, Parameter[] parameters, ClassNode superCN) {
+        StringBuffer msg = new StringBuffer();
+        msg.append("You are not allowed to overwrite the final method ").append(method.getName());
+        msg.append("(");
+        boolean needsComma = false;
+        for (int i = 0; i < parameters.length; i++) {
+            if (needsComma) {
+                msg.append(",");
+            } else {
+                needsComma = true;
+            }
+            msg.append(parameters[i].getType());
+        }
+        msg.append(") from ").append(getDescription(superCN));
+        msg.append(".");
+        addError(msg.toString(), method);
+    }
+
+    private boolean hasEqualParameterTypes(Parameter[] first, Parameter[] second) {
+        if (first.length != second.length) return false;
+        for (int i = 0; i < first.length; i++) {
+            String ft = first[i].getType().getName();
+            String st = second[i].getType().getName();
+            if (ft.equals(st)) continue;
+            return false;
+        }
+        return true;
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return source;
+    }
+
+    public void visitConstructorCallExpression(ConstructorCallExpression call) {
+        ClassNode type = call.getType();
+        if (Modifier.isAbstract(type.getModifiers())) {
+            addError("You cannot create an instance from the abstract " + getDescription(type) + ".", call);
+        }
+        super.visitConstructorCallExpression(call);
+    }
+
+    public void visitMethod(MethodNode node) {
+        checkAbstractDeclaration(node);
+        checkRepetitiveMethod(node);
+        checkOverloadingPrivateAndPublic(node);
+        super.visitMethod(node);
+    }
+
+    private void checkOverloadingPrivateAndPublic(MethodNode node) {
+        if (isConstructor(node)) return;
+        List methods = currentClass.getMethods(node.getName());
+        boolean hasPrivate=false;
+        boolean hasPublic=false;
+        for (Iterator iter = methods.iterator(); iter.hasNext();) {
+            MethodNode element = (MethodNode) iter.next();
+            if (element == node) continue;
+            if (!element.getDeclaringClass().equals(node.getDeclaringClass())) continue;
+            int modifiers = element.getModifiers();
+            if (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers)){
+                hasPublic=true;
+            } else {
+                hasPrivate=true;
+            }
+        }
+        if (hasPrivate && hasPublic) {
+            addError("Mixing private and public/protected methods of the same name causes multimethods to be disabled and is forbidden to avoid surprising behaviour. Renaming the private methods will solve the problem.",node);
+        }
+    }
+    
+    private void checkRepetitiveMethod(MethodNode node) {
+        if (isConstructor(node)) return;
+        List methods = currentClass.getMethods(node.getName());
+        for (Iterator iter = methods.iterator(); iter.hasNext();) {
+            MethodNode element = (MethodNode) iter.next();
+            if (element == node) continue;
+            if (!element.getDeclaringClass().equals(node.getDeclaringClass())) continue;
+            Parameter[] p1 = node.getParameters();
+            Parameter[] p2 = element.getParameters();
+            if (p1.length != p2.length) continue;
+            addErrorIfParamsAndReturnTypeEqual(p2, p1, node, element);
+        }
+    }
+
+    private void addErrorIfParamsAndReturnTypeEqual(Parameter[] p2, Parameter[] p1,
+                                                    MethodNode node, MethodNode element) {
+        boolean isEqual = true;
+        for (int i = 0; i < p2.length; i++) {
+            isEqual &= p1[i].getType().equals(p2[i].getType());
+        }
+        isEqual &= node.getReturnType().equals(element.getReturnType());
+        if (isEqual) {
+            addError("Repetitive method name/signature for " + getDescription(node) +
+                    " in " + getDescription(currentClass) + ".", node);
+        }
+    }
+
+    public void visitField(FieldNode node) {
+        if (currentClass.getField(node.getName()) != node) {
+            addError("The " + getDescription(node) + " is declared multiple times.", node);
+        }
+        checkInterfaceFieldModifiers(node);
+        super.visitField(node);
+    }
+
+    private void checkInterfaceFieldModifiers(FieldNode node) {
+        if (!currentClass.isInterface()) return;
+        if ((node.getModifiers() & (Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL)) == 0) {
+            addError("The " + getDescription(node) + " is not 'public final static' but is defined in the " +
+                    getDescription(currentClass) + ".", node);
+        }
+    }
+
+    public void visitBinaryExpression(BinaryExpression expression) {
+        if (expression.getOperation().getType() == Types.LEFT_SQUARE_BRACKET &&
+                expression.getRightExpression() instanceof MapEntryExpression) {
+            addError("You tried to use a map entry for an index operation, this is not allowed. " +
+                    "Maybe something should be set in parentheses or a comma is missing?",
+                    expression.getRightExpression());
+        }
+        super.visitBinaryExpression(expression);
+    }
+
+    public void visitCatchStatement(CatchStatement cs) {
+        if (!(cs.getExceptionType().isDerivedFrom(ClassHelper.make(Throwable.class)))) {
+            addError("Catch statement parameter type is not a subclass of Throwable.", cs);
+        }
+        super.visitCatchStatement(cs);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/ClassGenerator.java b/groovy/src/main/org/codehaus/groovy/classgen/ClassGenerator.java
new file mode 100644
index 0000000..ca87b1e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/ClassGenerator.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.control.SourceUnit;
+import org.objectweb.asm.Opcodes;
+
+import java.util.LinkedList;
+
+/**
+ * Abstract base class for generator of Java class versions of Groovy AST classes
+ *
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @author Russel Winder
+ * @version $Revision$
+ */
+public abstract class ClassGenerator extends ClassCodeVisitorSupport implements Opcodes {
+    protected ClassLoader classLoader;
+    // inner classes created while generating bytecode
+    protected LinkedList innerClasses = new LinkedList();
+
+    public ClassGenerator(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    public LinkedList getInnerClasses() {
+        return innerClasses;
+    }
+
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    /**
+     * A constant that is the ASM representation of the JDK version number for use in the
+     * <code>ClassWriter.visitor</code> method calls.
+     * <p/>
+     * <p>Prior to version 1.5 of ASM, the code generated was always JDK1.3 compliant.  As of ASM version
+     * 1.5 there is an extra (first) parameter to specify the bytecode version to generate.  In
+     * version 1.5 these are in Constants.  The CVS (as at 2004.12.12) and presumably in version 2.0,
+     * the interface Constants is replaced by Opcodes.</p>
+     */
+    public static final int asmJDKVersion = V1_3;
+    //  We can use V1_3 and not org.objectweb.asm.Opcodes.V1_3 because this abstract class
+    //  implements org.objectweb.asm.Opcodes so all its constants are available directly.
+
+
+    protected SourceUnit getSourceUnit() {
+        return null;
+    }
+
+    public void visitBytecodeSequence(BytecodeSequence bytecodeSequence) {
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/ClassGeneratorException.java b/groovy/src/main/org/codehaus/groovy/classgen/ClassGeneratorException.java
new file mode 100644
index 0000000..9c90e0d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/ClassGeneratorException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+/**
+ * An exception thrown by the class generator
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ClassGeneratorException extends RuntimeException {
+
+    public ClassGeneratorException(String message) {
+        super(message);
+    }
+
+    public ClassGeneratorException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/CompileStack.java b/groovy/src/main/org/codehaus/groovy/classgen/CompileStack.java
new file mode 100644
index 0000000..4917137
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/CompileStack.java
@@ -0,0 +1,625 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.VariableScope;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.*;
+
+/**
+ * This class is a helper for AsmClassGenerator. It manages
+ * different aspects of the code of a code block like 
+ * handling labels, defining variables, and scopes. 
+ * After a MethodNode is visited clear should be called, for 
+ * initialization the method init should be used.
+ * <p> 
+ * Some Notes:
+ * <ul>
+ * <li> every push method will require a later pop call
+ * <li> method parameters may define a category 2 variable, so
+ *      don't ignore the type stored in the variable object
+ * <li> the index of the variable may not be as assumed when
+ *      the variable is a parameter of a method because the 
+ *      parameter may be used in a closure, so don't ignore
+ *      the stored variable index
+ * <li> the names of temporary variables can be ignored. The names
+ *      are only used for debugging and do not conflict with each 
+ *      other or normal variables. For accessing the index of the
+ *      variable must be used.
+ * </ul>
+ * 
+ * 
+ * @see org.codehaus.groovy.classgen.AsmClassGenerator
+ * @author Jochen Theodorou
+ */
+public class CompileStack implements Opcodes {
+    /**
+     * @TODO remove optimization of this.foo -> this.@foo
+     * 
+     */
+    
+    // state flag
+    private boolean clear=true;
+    // current scope
+    private VariableScope scope;
+    // current label for continue
+    private Label continueLabel;
+    // current label for break
+    private Label breakLabel;
+    // available variables on stack
+    private Map stackVariables = new HashMap();
+    // index of the last variable on stack
+    private int currentVariableIndex = 1;
+    // index for the next variable on stack
+    private int nextVariableIndex = 1;
+    // currently temporary variables in use
+    private final List temporaryVariables = new LinkedList();
+    // overall used variables for a method/constructor
+    private final LinkedList usedVariables = new LinkedList();
+    // map containing named labels of parenting blocks
+    private Map superBlockNamedLabels = new HashMap();
+    // map containing named labels of current block
+    private Map currentBlockNamedLabels = new HashMap();
+    // list containing runnables representing a finally block
+    // such a block is created by synchronized or finally and
+    // must be called for break/continue/return
+    private LinkedList finallyBlocks = new LinkedList();
+    // a list of blocks already visiting. 
+    private final List visitedBlocks = new LinkedList();
+    
+    private Label thisStartLabel, thisEndLabel;
+
+    // current class index
+    private int currentClassIndex , currentMetaClassIndex;
+    
+    private MethodVisitor mv;
+    private BytecodeHelper helper;
+    
+    // helper to handle different stack based variables    
+    private final LinkedList stateStack = new LinkedList();
+    
+    // defines the first variable index useable after
+    // all parameters of a method 
+    private int localVariableOffset;
+    // this is used to store the goals for a "break foo" call
+    // in a loop where foo is a label.
+	private final Map namedLoopBreakLabel = new HashMap();
+	//this is used to store the goals for a "continue foo" call
+    // in a loop where foo is a label.
+	private final Map namedLoopContinueLabel = new HashMap();
+    private String className;
+	
+    private class StateStackElement {
+        final VariableScope scope;
+        final Label continueLabel;
+        final Label breakLabel;
+        Label finallyLabel;
+        final int lastVariableIndex;
+        final int nextVariableIndex;
+        final Map stackVariables;
+        List temporaryVariables = new LinkedList();
+        List usedVariables = new LinkedList();
+        final Map superBlockNamedLabels;
+        final Map currentBlockNamedLabels;
+        final LinkedList finallyBlocks;
+        
+        StateStackElement() {
+            scope = CompileStack.this.scope;
+            continueLabel = CompileStack.this.continueLabel;
+            breakLabel = CompileStack.this.breakLabel;
+            lastVariableIndex = CompileStack.this.currentVariableIndex;
+            stackVariables = CompileStack.this.stackVariables;
+            temporaryVariables = CompileStack.this.temporaryVariables;
+            nextVariableIndex = CompileStack.this.nextVariableIndex;
+            superBlockNamedLabels = CompileStack.this.superBlockNamedLabels;
+            currentBlockNamedLabels = CompileStack.this.currentBlockNamedLabels;
+            finallyBlocks = CompileStack.this.finallyBlocks;
+        }
+    }
+    
+    private void pushState() {
+        stateStack.add(new StateStackElement());
+        stackVariables = new HashMap(stackVariables);
+        finallyBlocks = new LinkedList(finallyBlocks);
+    }
+    
+    private void popState() {
+        if (stateStack.size()==0) {
+             throw new GroovyBugError("Tried to do a pop on the compile stack without push.");
+        }
+        StateStackElement element = (StateStackElement) stateStack.removeLast();
+        scope = element.scope;
+        continueLabel = element.continueLabel;
+        breakLabel = element.breakLabel;
+        currentVariableIndex = element.lastVariableIndex;
+        stackVariables = element.stackVariables;
+        nextVariableIndex = element.nextVariableIndex;
+        finallyBlocks = element.finallyBlocks;
+    }
+    
+    public Label getContinueLabel() {
+        return continueLabel;
+    }
+
+    public Label getBreakLabel() {
+        return breakLabel;
+    }
+
+    public void removeVar(int tempIndex) {
+        for (Iterator iter = temporaryVariables.iterator(); iter.hasNext();) {
+            Variable element = (Variable) iter.next();
+            if (element.getIndex()==tempIndex) {
+                iter.remove();
+                return;
+            }
+        }
+        throw new GroovyBugError("CompileStack#removeVar: tried to remove a temporary variable with a non existent index");
+    }
+
+    private void setEndLabels(){
+        Label endLabel = new Label();
+        mv.visitLabel(endLabel);
+        for (Iterator iter = stackVariables.values().iterator(); iter.hasNext();) {
+            Variable var = (Variable) iter.next();
+            var.setEndLabel(endLabel);
+        }
+        thisEndLabel = endLabel;
+    }
+    
+    public void pop() {
+        setEndLabels();
+        popState();
+    }
+
+    public VariableScope getScope() {
+        return scope;
+    }
+
+    /**
+     * creates a temporary variable. 
+     * 
+     * @param var defines type and name
+     * @param store defines if the toplevel argument of the stack should be stored
+     * @return the index used for this temporary variable
+     */
+    public int defineTemporaryVariable(org.codehaus.groovy.ast.Variable var, boolean store) {
+        return defineTemporaryVariable(var.getName(), var.getType(),store);
+    }
+
+    public Variable getVariable(String variableName ) {
+        return getVariable(variableName,true);
+    }
+    
+    public Variable getVariable(String variableName, boolean mustExist) {
+        if (variableName.equals("this")) return Variable.THIS_VARIABLE;
+        if (variableName.equals("super")) return Variable.SUPER_VARIABLE;
+        Variable v = (Variable) stackVariables.get(variableName);
+        if (v==null && mustExist)  throw new GroovyBugError("tried to get a variable with the name "+variableName+" as stack variable, but a variable with this name was not created");
+        return v;
+    }
+
+    /**
+     * creates a temporary variable. 
+     * 
+     * @param name defines type and name
+     * @param store defines if the toplevel argument of the stack should be stored
+     * @return the index used for this temporary variable
+     */
+    public int defineTemporaryVariable(String name,boolean store) {
+        return defineTemporaryVariable(name, ClassHelper.DYNAMIC_TYPE,store);
+    }
+
+    /**
+     * creates a temporary variable. 
+     * 
+     * @param name defines the name
+     * @param node defines the node
+     * @param store defines if the toplevel argument of the stack should be stored 
+     * @return the index used for this temporary variable
+     */
+    public int defineTemporaryVariable(String name, ClassNode node, boolean store) {
+        Variable answer = defineVar(name,node,false);
+        temporaryVariables.add(answer);
+        usedVariables.removeLast();
+        
+        if (store) mv.visitVarInsn(ASTORE, currentVariableIndex);
+        
+        return answer.getIndex();
+    }
+    
+    private void resetVariableIndex(boolean isStatic) {
+        if (!isStatic) {
+            currentVariableIndex=1;
+            nextVariableIndex=1;
+        } else {
+            currentVariableIndex=0;
+            nextVariableIndex=0;
+        }
+    }
+  
+    /**
+     * Clears the state of the class. This method should be called 
+     * after a MethodNode is visited. Note that a call to init will
+     * fail if clear is not called before
+     */
+    public void clear() {
+        if (stateStack.size()>1) {
+            int size = stateStack.size()-1;
+            throw new GroovyBugError("the compile stack contains "+size+" more push instruction"+(size==1?"":"s")+" than pops.");
+        }
+        clear = true;
+        // br experiment with local var table so debuggers can retrieve variable names
+        if (true) {//AsmClassGenerator.CREATE_DEBUG_INFO) {
+            if (thisEndLabel==null) setEndLabels();
+            
+            if (!scope.isInStaticContext()) {
+                // write "this"
+                mv.visitLocalVariable("this", className, null, thisStartLabel, thisEndLabel, 0);
+            }
+           
+            for (Iterator iterator = usedVariables.iterator(); iterator.hasNext();) {
+                Variable v = (Variable) iterator.next();
+                String type = BytecodeHelper.getTypeDescription(v.getType());
+                Label start = v.getStartLabel();
+                Label end = v.getEndLabel();
+                mv.visitLocalVariable(v.getName(), type, null, start, end, v.getIndex());
+            }
+        }
+        pop();
+        stackVariables.clear();
+        usedVariables.clear();
+        scope = null;
+        mv=null;
+        resetVariableIndex(false);
+        superBlockNamedLabels.clear();
+        currentBlockNamedLabels.clear();
+        namedLoopBreakLabel.clear();
+        namedLoopContinueLabel.clear();
+        continueLabel=null;
+        breakLabel=null;
+        helper = null;
+        thisStartLabel=null;
+        thisEndLabel=null;
+    }
+    
+    /**
+     * initializes this class for a MethodNode. This method will
+     * automatically define varibales for the method parameters
+     * and will create references if needed. the created variables
+     * can be get by getVariable
+     * 
+     */
+    protected void init(VariableScope el, Parameter[] parameters, MethodVisitor mv, ClassNode cn) {
+        if (!clear) throw new GroovyBugError("CompileStack#init called without calling clear before");
+        clear=false;
+        pushVariableScope(el);
+        this.mv = mv;
+        this.helper = new BytecodeHelper(mv);
+        defineMethodVariables(parameters,el.isInStaticContext());
+        this.className = BytecodeHelper.getTypeDescription(cn);
+        currentClassIndex = -1; currentMetaClassIndex = -1;
+    }
+
+    /**
+     * Causes the statestack to add an element and sets
+     * the given scope as new current variable scope. Creates 
+     * a element for the state stack so pop has to be called later
+     */
+    protected void pushVariableScope(VariableScope el) {
+        pushState();
+        scope = el;
+        superBlockNamedLabels = new HashMap(superBlockNamedLabels);
+        superBlockNamedLabels.putAll(currentBlockNamedLabels);
+        currentBlockNamedLabels = new HashMap();
+    }
+    
+    /**
+     * Should be called when decending into a loop that defines
+     * also a scope. Calls pushVariableScope and prepares labels 
+     * for a loop structure. Creates a element for the state stack
+     * so pop has to be called later 
+     */
+    protected void pushLoop(VariableScope el, String labelName) {
+        pushVariableScope(el);
+        initLoopLabels(labelName);
+    }
+
+    private void initLoopLabels(String labelName) {
+        continueLabel = new Label();
+        breakLabel = new Label();
+        if (labelName!=null) {
+        	namedLoopBreakLabel.put(labelName,breakLabel);
+        	namedLoopContinueLabel.put(labelName,continueLabel);
+        }
+    }
+    
+    /**
+     * Should be called when decending into a loop that does 
+     * not define a scope. Creates a element for the state stack
+     * so pop has to be called later
+     */
+    protected void pushLoop(String labelName) {
+        pushState();
+        initLoopLabels(labelName);
+    }
+    
+    /**
+     * Used for <code>break foo</code> inside a loop to end the
+     * execution of the marked loop. This method will return the
+     * break label of the loop if there is one found for the name.
+     * If not, the current break label is returned.
+     */
+    protected Label getNamedBreakLabel(String name) {
+    	Label label = getBreakLabel();
+    	Label endLabel = null;
+        if (name!=null) endLabel = (Label) namedLoopBreakLabel.get(name);
+    	if (endLabel!=null) label = endLabel;
+        return label;
+    }
+    
+    /**
+     * Used for <code>continue foo</code> inside a loop to continue
+     * the execution of the marked loop. This method will return 
+     * the break label of the loop if there is one found for the 
+     * name. If not, getLabel is used.
+     */
+    protected Label getNamedContinueLabel(String name) {
+    	Label label = getLabel(name);
+    	Label endLabel = null;
+        if (name!=null) endLabel = (Label) namedLoopContinueLabel.get(name);
+    	if (endLabel!=null) label = endLabel;
+        return label;
+    }    
+    
+    /**
+     * Creates a new break label and a element for the state stack
+     * so pop has to be called later
+     */
+    protected Label pushSwitch(){
+        pushState();
+        breakLabel = new Label();
+        return breakLabel;
+    }
+    
+    /**
+     * because a boolean Expression may not be evaluated completly
+     * it is important to keep the registers clean
+     */
+    protected void pushBooleanExpression(){
+        pushState();
+    }
+    
+    private Variable defineVar(String name, ClassNode type, boolean methodParameterUsedInClosure) {
+        makeNextVariableID(type);
+        int index = currentVariableIndex;
+        if (methodParameterUsedInClosure) {
+            index = localVariableOffset++;
+            type = ClassHelper.getWrapper(type);
+        }
+        Variable answer = new Variable(index, type, name);
+        usedVariables.add(answer);
+        answer.setHolder(methodParameterUsedInClosure);
+        return answer;
+    }
+    
+    private void makeLocalVariablesOffset(Parameter[] paras,boolean isInStaticContext) {
+        resetVariableIndex(isInStaticContext);
+        
+        for (int i = 0; i < paras.length; i++) {
+            makeNextVariableID(paras[i].getType());
+        }
+        localVariableOffset = nextVariableIndex;
+        
+        resetVariableIndex(isInStaticContext);
+    }
+    
+    private void defineMethodVariables(Parameter[] paras,boolean isInStaticContext) {
+        Label startLabel  = new Label();
+        thisStartLabel = startLabel;
+        mv.visitLabel(startLabel);
+        
+        makeLocalVariablesOffset(paras,isInStaticContext);      
+        
+        boolean hasHolder = false;
+        for (int i = 0; i < paras.length; i++) {
+            String name = paras[i].getName();
+            Variable answer;
+            ClassNode type = paras[i].getType();
+            if (paras[i].isClosureSharedVariable()) {
+                answer = defineVar(name, type, true);
+                helper.load(type,currentVariableIndex);
+                helper.box(type);
+                createReference(answer);
+                hasHolder = true;
+            } else {
+                answer = defineVar(name,type,false);
+            }
+            answer.setStartLabel(startLabel);
+            stackVariables.put(name, answer);
+        }
+        
+        if (hasHolder) {
+            nextVariableIndex = localVariableOffset;
+        }
+    }
+
+    private void createReference(Variable reference) {
+        mv.visitTypeInsn(NEW, "groovy/lang/Reference");
+        mv.visitInsn(DUP_X1);
+        mv.visitInsn(SWAP);
+        mv.visitMethodInsn(INVOKESPECIAL, "groovy/lang/Reference", "<init>", "(Ljava/lang/Object;)V");
+        mv.visitVarInsn(ASTORE, reference.getIndex());
+    }
+    
+    /**
+     * Defines a new Variable using an AST variable.
+     * @param initFromStack if true the last element of the 
+     *                      stack will be used to initilize
+     *                      the new variable. If false null
+     *                      will be used.
+     */
+    public Variable defineVariable(org.codehaus.groovy.ast.Variable v, boolean initFromStack) {
+        String name = v.getName();
+        Variable answer = defineVar(name,v.getType(),false);
+        if (v.isClosureSharedVariable()) answer.setHolder(true);
+        stackVariables.put(name, answer);
+        
+        Label startLabel  = new Label();
+        answer.setStartLabel(startLabel);
+        if (answer.isHolder())  {
+            if (!initFromStack) mv.visitInsn(ACONST_NULL);
+            createReference(answer);
+        } else {
+            if (!initFromStack) mv.visitInsn(ACONST_NULL);
+            mv.visitVarInsn(ASTORE, currentVariableIndex);            
+        } 
+        mv.visitLabel(startLabel);
+        return answer;
+    }
+
+    /**
+     * @param name the name of the variable of interest
+     * @return true if a variable is already defined
+     */
+    public boolean containsVariable(String name) {
+        return stackVariables.containsKey(name);
+    }
+    
+    /**
+     * Calculates the index of the next free register stores ir
+     * and sets the current variable index to the old value
+     */
+    private void makeNextVariableID(ClassNode type) {
+        currentVariableIndex = nextVariableIndex;
+        if (type==ClassHelper.long_TYPE || type==ClassHelper.double_TYPE) {
+            nextVariableIndex++;
+        }
+        nextVariableIndex++;
+    }
+    
+    /**
+     * Returns the label for the given name 
+     */
+    public Label getLabel(String name) {
+        if (name==null) return null;
+        Label l = (Label) superBlockNamedLabels.get(name);
+        if (l==null) l = createLocalLabel(name);
+        return l;
+    }
+    
+    /**
+     * creates a new named label
+     */
+    public Label createLocalLabel(String name) {
+        Label l = (Label) currentBlockNamedLabels.get(name);
+        if (l==null) {
+            l = new Label();
+            currentBlockNamedLabels.put(name,l);
+        }
+        return l;
+    }
+    
+    public int getCurrentClassIndex(){
+        return currentClassIndex;
+    }
+    
+    public void setCurrentClassIndex(int index){
+        currentClassIndex=index;
+    }
+    
+    public int getCurrentMetaClassIndex(){
+        return currentMetaClassIndex;
+    }
+    
+    public void setCurrentMetaClassIndex(int index){
+        currentMetaClassIndex=index;
+    }
+
+    public void applyFinallyBlocks(Label label, boolean isBreakLabel) {
+        // first find the state defining the label. That is the state
+        // directly after the state not knowing this label. If no state
+        // in the list knows that label, then the defining state is the
+        // current state.
+        StateStackElement result = null;
+        for (ListIterator iter = stateStack.listIterator(stateStack.size()); iter.hasPrevious();) {
+            StateStackElement element = (StateStackElement) iter.previous();
+            if (!element.currentBlockNamedLabels.values().contains(label)) {
+                if (isBreakLabel && element.breakLabel != label) {
+                    result = element;
+                    break;
+                }
+                if (!isBreakLabel && element.continueLabel != label) {
+                    result = element;
+                    break;
+                }
+            }
+        }
+        
+        List blocksToRemove;
+        if (result==null) {
+            // all Blocks do know the label, so use all finally blocks
+            blocksToRemove = Collections.EMPTY_LIST;
+        } else {
+            blocksToRemove = result.finallyBlocks;
+        }
+        
+        ArrayList blocks = new ArrayList(finallyBlocks);
+        blocks.removeAll(blocksToRemove);
+        applyFinallyBlocks(blocks);
+    }
+
+    private void applyFinallyBlocks(List blocks) {
+        for (Iterator iter = blocks.iterator(); iter.hasNext();) {
+            Runnable block = (Runnable) iter.next();
+            if (visitedBlocks.contains(block)) continue;
+            block.run();
+        }     
+    }
+    
+    public void applyFinallyBlocks() {
+        applyFinallyBlocks(finallyBlocks); 
+    }
+
+    public boolean hasFinallyBlocks() {
+        return !finallyBlocks.isEmpty();
+    }
+
+    public void pushFinallyBlock(Runnable block) {
+        finallyBlocks.addFirst(block);
+        pushState();
+    }
+
+    public void popFinallyBlock() {
+        popState();
+        finallyBlocks.removeFirst();
+    }
+
+    public void pushFinallyBlockVisit(Runnable block) {
+        visitedBlocks.add(block);
+    }
+    
+    public void popFinallyBlockVisit(Runnable block) {
+        visitedBlocks.remove(block);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/DummyClassGenerator.java b/groovy/src/main/org/codehaus/groovy/classgen/DummyClassGenerator.java
new file mode 100644
index 0000000..aa8c270
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/DummyClassGenerator.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyRuntimeException;
+import org.codehaus.groovy.ast.*;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodVisitor;
+
+import java.util.Iterator;
+
+/**
+ * To generate a class that has all the fields and methods, except that fields are not initilized
+ * and methods are empty. It's intended for being used as a place holder during code generation
+ * of reference to the "this" class itself.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
+ * @version $Revision$
+ */
+public class DummyClassGenerator extends ClassGenerator {
+
+    private ClassVisitor cv;
+    private MethodVisitor mv;
+    private GeneratorContext context;
+
+    private String sourceFile;
+
+    // current class details
+    private ClassNode classNode;
+    private String internalClassName;
+    private String internalBaseClassName;
+
+
+    public DummyClassGenerator(
+            GeneratorContext context,
+            ClassVisitor classVisitor,
+            ClassLoader classLoader,
+            String sourceFile) {
+        super(classLoader);
+        this.context = context;
+        this.cv = classVisitor;
+        this.sourceFile = sourceFile;
+    }
+
+    // GroovyClassVisitor interface
+    //-------------------------------------------------------------------------
+    public void visitClass(ClassNode classNode) {
+        try {
+            this.classNode = classNode;
+            this.internalClassName = BytecodeHelper.getClassInternalName(classNode);
+
+            //System.out.println("Generating class: " + classNode.getName());
+
+            this.internalBaseClassName = BytecodeHelper.getClassInternalName(classNode.getSuperClass());
+
+            cv.visit(
+                    asmJDKVersion,
+                    classNode.getModifiers(),
+                    internalClassName,
+                    (String) null,
+                    internalBaseClassName,
+                    BytecodeHelper.getClassInternalNames(classNode.getInterfaces())
+            );
+
+            classNode.visitContents(this);
+
+            for (Iterator iter = innerClasses.iterator(); iter.hasNext();) {
+                ClassNode innerClass = (ClassNode) iter.next();
+                ClassNode innerClassType = innerClass;
+                String innerClassInternalName = BytecodeHelper.getClassInternalName(innerClassType);
+                String outerClassName = internalClassName; // default for inner classes
+                MethodNode enclosingMethod = innerClass.getEnclosingMethod();
+                if (enclosingMethod != null) {
+                    // local inner classes do not specify the outer class name
+                    outerClassName = null;
+                }
+                cv.visitInnerClass(
+                        innerClassInternalName,
+                        outerClassName,
+                        innerClassType.getName(),
+                        innerClass.getModifiers());
+            }
+            cv.visitEnd();
+        }
+        catch (GroovyRuntimeException e) {
+            e.setModule(classNode.getModule());
+            throw e;
+        }
+    }
+
+    public void visitConstructor(ConstructorNode node) {
+
+        visitParameters(node, node.getParameters());
+
+        String methodType = BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, node.getParameters());
+        mv = cv.visitMethod(node.getModifiers(), "<init>", methodType, null, null);
+        mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+        mv.visitInsn(DUP);
+        mv.visitLdcInsn("not intended for execution");
+        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
+        mv.visitInsn(ATHROW);
+        mv.visitMaxs(0, 0);
+    }
+
+    public void visitMethod(MethodNode node) {
+
+        visitParameters(node, node.getParameters());
+
+        String methodType = BytecodeHelper.getMethodDescriptor(node.getReturnType(), node.getParameters());
+        mv = cv.visitMethod(node.getModifiers(), node.getName(), methodType, null, null);
+
+        mv.visitTypeInsn(NEW, "java/lang/RuntimeException");
+        mv.visitInsn(DUP);
+        mv.visitLdcInsn("not intended for execution");
+        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/RuntimeException", "<init>", "(Ljava/lang/String;)V");
+        mv.visitInsn(ATHROW);
+
+        mv.visitMaxs(0, 0);
+    }
+
+    public void visitField(FieldNode fieldNode) {
+
+        cv.visitField(
+                fieldNode.getModifiers(),
+                fieldNode.getName(),
+                BytecodeHelper.getTypeDescription(fieldNode.getType()),
+                null, //fieldValue,  //br  all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer.
+                null);
+    }
+
+    /**
+     * Creates a getter, setter and field
+     */
+    public void visitProperty(PropertyNode statement) {
+    }
+
+    protected CompileUnit getCompileUnit() {
+        CompileUnit answer = classNode.getCompileUnit();
+        if (answer == null) {
+            answer = context.getCompileUnit();
+        }
+        return answer;
+    }
+
+    protected void visitParameters(ASTNode node, Parameter[] parameters) {
+        for (int i = 0, size = parameters.length; i < size; i++) {
+            visitParameter(node, parameters[i]);
+        }
+    }
+
+    protected void visitParameter(ASTNode node, Parameter parameter) {
+    }
+
+
+    public void visitAnnotations(AnnotatedNode node) {
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/EnumVisitor.java b/groovy/src/main/org/codehaus/groovy/classgen/EnumVisitor.java
new file mode 100644
index 0000000..b423b15
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/EnumVisitor.java
@@ -0,0 +1,235 @@
+package org.codehaus.groovy.classgen;

+

+import java.util.ArrayList;

+import java.util.Iterator;

+import java.util.List;

+

+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;

+import org.codehaus.groovy.ast.ClassHelper;

+import org.codehaus.groovy.ast.ClassNode;

+import org.codehaus.groovy.ast.CodeVisitorSupport;

+import org.codehaus.groovy.ast.ConstructorNode;

+import org.codehaus.groovy.ast.FieldNode;

+import org.codehaus.groovy.ast.MethodNode;

+import org.codehaus.groovy.ast.Parameter;

+import org.codehaus.groovy.ast.expr.ArgumentListExpression;

+import org.codehaus.groovy.ast.expr.ArrayExpression;

+import org.codehaus.groovy.ast.expr.BinaryExpression;

+import org.codehaus.groovy.ast.expr.ClassExpression;

+import org.codehaus.groovy.ast.expr.ConstantExpression;

+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;

+import org.codehaus.groovy.ast.expr.Expression;

+import org.codehaus.groovy.ast.expr.FieldExpression;

+import org.codehaus.groovy.ast.expr.ListExpression;

+import org.codehaus.groovy.ast.expr.MethodCallExpression;

+import org.codehaus.groovy.ast.expr.SpreadExpression;

+import org.codehaus.groovy.ast.expr.VariableExpression;

+import org.codehaus.groovy.ast.stmt.BlockStatement;

+import org.codehaus.groovy.ast.stmt.ExpressionStatement;

+import org.codehaus.groovy.ast.stmt.ReturnStatement;

+import org.codehaus.groovy.ast.stmt.Statement;

+import org.codehaus.groovy.control.CompilationUnit;

+import org.codehaus.groovy.control.SourceUnit;

+import org.codehaus.groovy.syntax.Token;

+import org.codehaus.groovy.syntax.Types;

+import org.objectweb.asm.Opcodes;

+

+public class EnumVisitor extends ClassCodeVisitorSupport{

+

+    // some constants for modifiers

+    private static final int FS = Opcodes.ACC_FINAL | Opcodes.ACC_STATIC;

+    private static final int PS = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC;

+    private static final int PUBLIC_FS = Opcodes.ACC_PUBLIC | FS;

+    private static final int PRIVATE_FS = Opcodes.ACC_PRIVATE | FS;

+    

+    private final CompilationUnit compilationUnit;

+    private final SourceUnit sourceUnit;

+    

+    

+    public EnumVisitor(CompilationUnit cu, SourceUnit su) {

+        compilationUnit = cu;

+        sourceUnit = su;

+    }    

+    

+    public void visitClass(ClassNode node) {

+        if (!isEnum(node)) return;

+        completeEnum(node);

+    }

+

+    protected SourceUnit getSourceUnit() {

+        return sourceUnit;

+    }

+    

+    private boolean isEnum(ClassNode node) {

+       return (node.getModifiers()&Opcodes.ACC_ENUM) != 0;

+    }

+

+    private void completeEnum(final ClassNode enumClass) {

+        ClassNode enumArray = enumClass.makeArray();

+

+        // create values field

+        FieldNode values = new FieldNode("$VALUES",PRIVATE_FS,enumArray,enumClass,null);

+        values.setSynthetic(true);      

+        {

+            // create values() method

+            MethodNode valuesMethod = new MethodNode("values",PUBLIC_FS,enumArray,new Parameter[0],ClassNode.EMPTY_ARRAY,null);

+            valuesMethod.setSynthetic(true);

+            BlockStatement code = new BlockStatement();

+            code.addStatement(

+                    new ReturnStatement(

+                            new MethodCallExpression(new FieldExpression(values),"clone",MethodCallExpression.NO_ARGUMENTS)

+                    )

+            );

+            valuesMethod.setCode(code);

+            enumClass.addMethod(valuesMethod);

+        }

+        

+        {        

+            // create valueOf

+            Parameter stringParameter = new Parameter(ClassHelper.STRING_TYPE,"name");

+            MethodNode valueOfMethod = new MethodNode("valueOf",PS,enumClass,new Parameter[]{stringParameter},ClassNode.EMPTY_ARRAY,null);

+            ArgumentListExpression callArguments = new ArgumentListExpression();

+            callArguments.addExpression(new ClassExpression(enumClass));

+            callArguments.addExpression(new VariableExpression("name"));

+

+            BlockStatement code = new BlockStatement();

+            code.addStatement(

+                    new ReturnStatement(

+                            new MethodCallExpression(new ClassExpression(ClassHelper.Enum_Type),"valueOf",callArguments)

+                    )

+            );

+            valueOfMethod.setCode(code);

+            valueOfMethod.setSynthetic(true);

+            enumClass.addMethod(valueOfMethod);

+        }

+        addConstructor(enumClass);

+        {

+            // constructor helper

+            // This method is used instead of calling the constructor as

+            // calling the constrcutor may require a table with MetaClass

+            // selecting the constructor for each enum value. So instead we

+            // use this method to have a central point for constructor selection

+            // and only one table. The whole construction is needed because 

+            // Reflection forbids access to the enum constructor.

+            // code:

+            // def $INIT(Object[] para) {

+            //  return this(*para)

+            // }            

+            Parameter[] parameter = new Parameter[]{new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "para")};

+            MethodNode initMethod = new MethodNode("$INIT",PRIVATE_FS,enumClass,parameter,ClassNode.EMPTY_ARRAY,null);

+            initMethod.setSynthetic(true);

+            ConstructorCallExpression cce = new ConstructorCallExpression(

+                    ClassNode.THIS,

+                    new ArgumentListExpression(

+                            new SpreadExpression(new VariableExpression("para"))

+                    )

+            );

+            BlockStatement code = new BlockStatement();

+            code.addStatement(new ReturnStatement(cce));

+            initMethod.setCode(code);

+            enumClass.addMethod(initMethod);

+        }

+        

+        {

+            // static init

+            List fields = enumClass.getFields();

+            List arrayInit = new ArrayList();

+            int value = -1;

+            Token assign = Token.newSymbol(Types.ASSIGN, -1, -1);

+            List block = new ArrayList();

+            for (Iterator iterator = fields.iterator(); iterator.hasNext();) {

+                FieldNode field = (FieldNode) iterator.next();

+                if ((field.getModifiers()&Opcodes.ACC_ENUM) == 0) continue;

+                value++;

+                

+                ArgumentListExpression args = new ArgumentListExpression();

+                args.addExpression(new ConstantExpression(field.getName()));

+                args.addExpression(new ConstantExpression(new Integer(value)));

+                if (field.getInitialExpression()!=null) {

+                    ListExpression oldArgs = (ListExpression) field.getInitialExpression();

+                    for (Iterator oldArgsIterator = oldArgs.getExpressions().iterator(); oldArgsIterator.hasNext();) {

+                        Expression exp = (Expression) oldArgsIterator.next();

+                        args.addExpression(exp);

+                    }

+                }

+                field.setInitialValueExpression(null);

+                block.add(

+                        new ExpressionStatement(

+                                new BinaryExpression(

+                                        new FieldExpression(field),

+                                        assign,

+                                        new MethodCallExpression(new ClassExpression(enumClass),"$INIT",args)

+                                )

+                        )

+                );

+                arrayInit.add(new FieldExpression(field));

+            }

+            

+            block.add(

+                    new ExpressionStatement(

+                            new BinaryExpression(new FieldExpression(values),assign,new ArrayExpression(enumClass,arrayInit))

+                    )

+            );

+            enumClass.addStaticInitializerStatements(block, true);

+            enumClass.addField(values);

+        }

+        

+        

+    }

+

+    private void addConstructor(ClassNode enumClass) {

+        // first look if there are declared constructors

+        List ctors = new ArrayList(enumClass.getDeclaredConstructors());

+        if (ctors.size()==0) {

+            // add default constructor

+            ConstructorNode init = new ConstructorNode(Opcodes.ACC_PRIVATE,new Parameter[0],ClassNode.EMPTY_ARRAY,new BlockStatement());

+            enumClass.addConstructor(init);

+            ctors.add(init);

+        } 

+        

+        // for each constructor:

+        // if constructordoes not define a call to super, then transform constructor

+        // to get String,int parameters at beginning and add call super(String,int)  

+        

+        for (Iterator iterator = ctors.iterator(); iterator.hasNext();) {

+            ConstructorNode ctor = (ConstructorNode) iterator.next();

+            if (ctor.firstStatementIsSpecialConstructorCall()) continue;

+            // we need to add parameters

+            Parameter[] oldP = ctor.getParameters();

+            Parameter[] newP = new Parameter[oldP.length+2];

+            String stringParameterName = getUniqueVariableName("__str",ctor.getCode());

+            newP[0] = new Parameter(ClassHelper.STRING_TYPE,stringParameterName);

+            String intParameterName = getUniqueVariableName("__int",ctor.getCode());

+            newP[1] = new Parameter(ClassHelper.int_TYPE,intParameterName);

+            System.arraycopy(oldP, 0, newP, 2, oldP.length);

+            ctor.setParameters(newP);

+            // and a super call

+            ConstructorCallExpression cce = new ConstructorCallExpression(

+                    ClassNode.SUPER,

+                    new ArgumentListExpression(

+                           new VariableExpression(stringParameterName),

+                           new VariableExpression(intParameterName)

+                    )

+            );

+            BlockStatement code = new BlockStatement();

+            code.addStatement(new ExpressionStatement(cce));

+            Statement oldCode = ctor.getCode();

+            if (oldCode!=null) code.addStatement(oldCode);

+            ctor.setCode(code);

+        }

+    }

+

+    private String getUniqueVariableName(final String name, Statement code) {

+        if (code==null) return name;

+        final Object[] found=new Object[1];

+        CodeVisitorSupport cv = new CodeVisitorSupport() {

+            public void visitVariableExpression(VariableExpression expression) {

+                if (expression.getName().equals(name)) found[0]=Boolean.TRUE;

+            }

+        };

+        code.visit(cv);

+        if (found[0]!=null) return getUniqueVariableName("_"+name, code);

+        return name;

+    }

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java b/groovy/src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java
new file mode 100644
index 0000000..70a4655
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java
@@ -0,0 +1,130 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.classgen;

+

+import java.util.Collection;

+import java.util.Iterator;

+

+import org.codehaus.groovy.ast.*;

+import org.codehaus.groovy.control.CompilerConfiguration;

+import org.codehaus.groovy.control.ErrorCollector;

+import org.codehaus.groovy.control.SourceUnit;

+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;

+import org.codehaus.groovy.syntax.SyntaxException;

+

+

+/**

+ * A specialized Groovy AST visitor meant to perform additional verifications upon the

+ * current AST. Currently it does checks on annotated nodes and annotations itself.

+ * 

+ * Current limitations:

+ * - annotations on local variables are not supported

+ * 

+ * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>

+ */

+public class ExtendedVerifier implements GroovyClassVisitor {

+    public static final String JVM_ERROR_MESSAGE = "Please make sure you are running on a JVM >= 1.5";

+

+    private SourceUnit source;

+    private ClassNode currentClass;

+    

+    public ExtendedVerifier(SourceUnit sourceUnit) {

+        this.source = sourceUnit;

+    }

+

+    public void visitClass(ClassNode node) {

+        this.currentClass = node;

+        visitAnnotations(node, AnnotationNode.TYPE_TARGET);

+        node.visitContents(this);

+    }

+

+    public void visitConstructor(ConstructorNode node) {

+        visitAnnotations(node, AnnotationNode.CONSTRUCTOR_TARGET);

+    }

+

+    public void visitField(FieldNode node) {

+        visitAnnotations(node, AnnotationNode.FIELD_TARGET);

+    }

+

+    public void visitMethod(MethodNode node) {

+        visitAnnotations(node, AnnotationNode.METHOD_TARGET);

+        for (int i = 0; i < node.getParameters().length; i++) {

+            Parameter parameter = node.getParameters()[i];

+            visitAnnotations(parameter, AnnotationNode.PARAMETER_TARGET);

+        }

+    }

+

+    public void visitProperty(PropertyNode node) {

+    }

+

+    protected void visitAnnotations(AnnotatedNode node, int target) {

+        if(node.getAnnotations().isEmpty()) {

+            return;

+        }

+        

+        this.currentClass.setAnnotated(true);

+        

+        if(!isAnnotationCompatible()) {

+            addError("Annotations are not supported in the current runtime." + JVM_ERROR_MESSAGE,

+                    node);

+            return;

+        }

+        

+        Collection annotations = node.getAnnotations().values();

+        for(Iterator it = annotations.iterator(); it.hasNext(); ) {

+            AnnotationNode an = (AnnotationNode) it.next();

+

+            AnnotationNode annotation = visitAnnotation(an);

+            if(!annotation.isValid()) {

+                return;

+            }

+            if(!annotation.isTargetAllowed(target)) {

+                addError("Annotation @" + annotation.getClassNode().getName()

+                        + " is not allowed on element " + AnnotationNode.targetToName(target),

+                        annotation);

+            }

+        }

+    }

+    

+    /**

+     * Resolve metadata and details of the annotation.

+     */

+    private AnnotationNode visitAnnotation(AnnotationNode node) {

+        ErrorCollector errorCollector = new ErrorCollector(this.source.getConfiguration());

+        AnnotationVisitor visitor = new AnnotationVisitor(this.source, errorCollector);

+        AnnotationNode solvedAnnotation = visitor.visit(node);

+        this.source.getErrorCollector().addCollectorContents(errorCollector);

+        return solvedAnnotation;

+    }

+

+    /**

+     * Check if the current runtime allows Annotation usage.

+     */

+    protected boolean isAnnotationCompatible() {

+        return CompilerConfiguration.POST_JDK5.equals(this.source.getConfiguration().getTargetBytecode()); 

+    }

+    

+    protected void addError(String msg, ASTNode expr) {

+        this.source.getErrorCollector().addErrorAndContinue(

+            new SyntaxErrorMessage(

+                    new SyntaxException(msg + '\n', expr.getLineNumber(), expr.getColumnNumber()), this.source)

+        );

+    }

+

+    public void visitGenericType(GenericsType genericsType) {

+

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/classgen/GeneratorContext.java b/groovy/src/main/org/codehaus/groovy/classgen/GeneratorContext.java
new file mode 100644
index 0000000..359abd5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/GeneratorContext.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CompileUnit;
+import org.codehaus.groovy.ast.MethodNode;
+
+
+/**
+ * A context shared across generations of a class and its inner classes
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class GeneratorContext {
+
+    private int innerClassIdx = 1;
+    private CompileUnit compileUnit;
+    
+    public GeneratorContext(CompileUnit compileUnit) {
+        this.compileUnit = compileUnit;
+    }
+
+    public int getNextInnerClassIdx() {
+        return innerClassIdx++;
+    }
+    
+    public CompileUnit getCompileUnit() {
+        return compileUnit;
+    }
+
+    public String getNextClosureInnerName(ClassNode owner, ClassNode enclosingClass, MethodNode enclosingMethod) {
+        String ownerShortName = owner.getNameWithoutPackage();
+        String classShortName = enclosingClass.getNameWithoutPackage();
+        if (classShortName.equals(ownerShortName)) {
+            classShortName = "";
+        }
+        else {
+            classShortName += "_";
+        }
+        // remove $
+        int dp = classShortName.lastIndexOf("$");
+        if (dp >= 0) {
+            classShortName = classShortName.substring(++dp);
+        }
+        // remove leading _
+        if (classShortName.startsWith("_")) {
+            classShortName = classShortName.substring(1);
+        }
+        String methodName = "";
+        if (enclosingMethod != null) {
+            methodName = enclosingMethod.getName() + "_";
+
+            if (enclosingClass.isDerivedFrom(ClassHelper.CLOSURE_TYPE)) {
+                methodName = "";
+            }
+            methodName = methodName.replace('<', '_');
+            methodName = methodName.replace('>', '_');
+        }
+        return "_" + classShortName + methodName + "closure" + getNextInnerClassIdx();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/MethodCaller.java b/groovy/src/main/org/codehaus/groovy/classgen/MethodCaller.java
new file mode 100644
index 0000000..5d6d544
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/MethodCaller.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import java.lang.reflect.Method;
+
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * A helper class to invoke methods more easily in ASM
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MethodCaller implements Opcodes {
+
+    private int opcode;
+    private String internalName;
+    private String name;
+    private Class theClass;
+    private String methodDescriptor;
+
+    public static MethodCaller newStatic(Class theClass, String name) {
+        return new MethodCaller(INVOKESTATIC, theClass, name);
+    }
+
+    public static MethodCaller newInterface(Class theClass, String name) {
+        return new MethodCaller(INVOKEINTERFACE, theClass, name);
+    }
+
+    public static MethodCaller newVirtual(Class theClass, String name) {
+        return new MethodCaller(INVOKEVIRTUAL, theClass, name);
+    }
+
+    public MethodCaller(int opcode, Class theClass, String name) {
+        this.opcode = opcode;
+        this.internalName = Type.getInternalName(theClass);
+        this.theClass = theClass;
+        this.name = name;
+
+    }
+
+    public void call(MethodVisitor methodVisitor) {
+        methodVisitor.visitMethodInsn(opcode, internalName, name, getMethodDescriptor());
+    }
+
+    public String getMethodDescriptor() {
+        if (methodDescriptor == null) {
+            Method method = getMethod();
+            methodDescriptor = Type.getMethodDescriptor(method);
+        }
+        return methodDescriptor;
+    }
+
+    protected Method getMethod() {
+        Method[] methods = theClass.getMethods();
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            if (method.getName().equals(name)) {
+                return method;
+            }
+        }
+        throw new ClassGeneratorException("Could not find method: " + name + " on class: " + theClass);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/MethodCallerMultiAdapter.java b/groovy/src/main/org/codehaus/groovy/classgen/MethodCallerMultiAdapter.java
new file mode 100644
index 0000000..0e383a5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/MethodCallerMultiAdapter.java
@@ -0,0 +1,80 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.classgen;

+

+import org.objectweb.asm.MethodVisitor;

+

+public class MethodCallerMultiAdapter {

+    private MethodCaller[] methods;

+    boolean skipSpreadSafeAndSafe;

+

+    public static final int MAX_ARGS = 0;

+

+    public static MethodCallerMultiAdapter newStatic(Class theClass, String baseName, boolean createNArgs, boolean skipSpreadSafeAndSafe) {

+        MethodCallerMultiAdapter mcma = new MethodCallerMultiAdapter();

+        mcma.skipSpreadSafeAndSafe = skipSpreadSafeAndSafe;

+        if (createNArgs) {

+            int numberOfBaseMethods = mcma.numberOfBaseMethods();

+            mcma.methods = new MethodCaller[(MAX_ARGS + 2) * numberOfBaseMethods];

+            for (int i = 0; i <= MAX_ARGS; i++) {

+                mcma.methods[i * numberOfBaseMethods] = MethodCaller.newStatic(theClass, baseName + i);

+                if (skipSpreadSafeAndSafe) continue;

+                mcma.methods[i * numberOfBaseMethods + 1] = MethodCaller.newStatic(theClass, baseName + i + "Safe");

+                mcma.methods[i * numberOfBaseMethods + 2] = MethodCaller.newStatic(theClass, baseName + i + "SpreadSafe");

+            }

+            mcma.methods[(MAX_ARGS + 1) * numberOfBaseMethods] = MethodCaller.newStatic(theClass, baseName + "N");

+            if (!skipSpreadSafeAndSafe) {

+                mcma.methods[(MAX_ARGS + 1) * numberOfBaseMethods + 1] = MethodCaller.newStatic(theClass, baseName + "N" + "Safe");

+                mcma.methods[(MAX_ARGS + 1) * numberOfBaseMethods + 2] = MethodCaller.newStatic(theClass, baseName + "N" + "SpreadSafe");

+            }

+

+        } else if (!skipSpreadSafeAndSafe) {

+            mcma.methods = new MethodCaller[]{

+                    MethodCaller.newStatic(theClass, baseName),

+                    MethodCaller.newStatic(theClass, baseName + "Safe"),

+                    MethodCaller.newStatic(theClass, baseName + "SpreadSafe")

+            };

+        } else {

+            mcma.methods = new MethodCaller[]{

+                    MethodCaller.newStatic(theClass, baseName)

+            };

+        }

+        return mcma;

+    }

+

+    /**

+     * @param methodVisitor

+     * @param numberOfArguments a value >0 describing how many arguments are additionally used for the method call

+     * @param safe

+     * @param spreadSafe

+     */

+    public void call(MethodVisitor methodVisitor, int numberOfArguments, boolean safe, boolean spreadSafe) {

+        int offset = 0;

+        if (safe && !skipSpreadSafeAndSafe) offset = 1;

+        if (spreadSafe && !skipSpreadSafeAndSafe) offset = 2;

+        if (numberOfArguments > MAX_ARGS || numberOfArguments < 0) {

+            offset += (MAX_ARGS + 1) * numberOfBaseMethods();

+        } else {

+            offset += numberOfArguments * numberOfBaseMethods();

+        }

+        methods[offset].call(methodVisitor);

+    }

+

+    private int numberOfBaseMethods() {

+        if (skipSpreadSafeAndSafe) return 1;

+        return 3;

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/ReflectorGenerator.java b/groovy/src/main/org/codehaus/groovy/classgen/ReflectorGenerator.java
new file mode 100644
index 0000000..e3265d5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/ReflectorGenerator.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.CachedMethod;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Code generates a Reflector
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ReflectorGenerator implements Opcodes {
+
+    private List methods;
+    private ClassVisitor cv;
+    private BytecodeHelper helper = new BytecodeHelper(null);
+    private String classInternalName;
+
+    private static List m_names = new ArrayList();
+
+    private static String get_m_name (int i) {
+      while (i >= m_names.size()) {
+        m_names.add("m" + m_names.size());
+      }
+
+      return (String) m_names.get(i);
+    }
+
+    public ReflectorGenerator(List methods) {
+        this.methods = new ArrayList(methods.size());
+        for (Iterator it = methods.iterator(); it.hasNext(); ) {
+            CachedMethod method = (CachedMethod) it.next();
+            if (method.canBeCalledByReflector())
+              this.methods.add(method);
+        }
+    }
+
+    public void generate(ClassVisitor cv, String className) {
+        this.cv = cv;
+
+        classInternalName = BytecodeHelper.getClassInternalName(className);
+        cv.visit(ClassGenerator.asmJDKVersion, ACC_PUBLIC + ACC_SUPER, classInternalName, null, "org/codehaus/groovy/runtime/Reflector", null);
+
+        cv.visitField(ACC_PUBLIC + ACC_STATIC, "accessor", "Ljava/lang/Object;", null, null);
+
+        MethodVisitor mvInit = cv.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
+        mvInit.visitVarInsn(ALOAD, 0);
+        mvInit.visitMethodInsn(INVOKESPECIAL, "org/codehaus/groovy/runtime/Reflector", "<init>", "()V");
+        mvInit.visitInsn(RETURN);
+        mvInit.visitMaxs(1, 1);
+
+        MethodVisitor mvClinit = cv.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
+        mvClinit.visitTypeInsn(NEW, classInternalName);
+        mvClinit.visitInsn(DUP);
+        mvClinit.visitMethodInsn(INVOKESPECIAL, classInternalName, "<init>", "()V");
+        mvClinit.visitFieldInsn(PUTSTATIC, classInternalName, "accessor", "Ljava/lang/Object;");
+        mvClinit.visitInsn(RETURN);
+        mvClinit.visitMaxs(1, 1);
+
+        generateInvokeMethod();
+
+        cv.visitEnd();
+    }
+
+    protected void generateInvokeMethod() {
+        int methodCount = methods.size();
+
+        MethodVisitor mv = cv.visitMethod(
+                ACC_PUBLIC,
+                "invoke",
+                "(Lorg/codehaus/groovy/reflection/CachedMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
+                null,
+                null);
+
+        // load parameters for the helper method call
+        mv.visitVarInsn(ALOAD, 0);
+        mv.visitVarInsn(ALOAD, 1);
+        mv.visitVarInsn(ALOAD, 2);
+        mv.visitVarInsn(ALOAD, 3);
+
+        // get method number for switch
+        mv.visitVarInsn(ALOAD, 1);
+        mv.visitMethodInsn(INVOKEVIRTUAL, "org/codehaus/groovy/reflection/CachedMethod", "getMethodIndex", "()I");
+
+        // init meta methods with number
+        Label defaultLabel = new Label();
+        Label[] labels = new Label[methodCount];
+        int[] indices = new int[methodCount];
+        for (int i = 0; i < methodCount; i++) {
+            labels[i] = new Label();
+            CachedMethod method = (CachedMethod) methods.get(i);
+            method.setMethodIndex(indices[i] = i+1);
+        }
+
+        // do switch
+        mv.visitLookupSwitchInsn(defaultLabel, indices, labels);
+        // create switch cases
+        for (int i = 0; i < methodCount; i++) {
+            // call helper for invocation
+            mv.visitLabel(labels[i]);
+            mv.visitMethodInsn(
+                    INVOKESPECIAL,
+                    classInternalName,
+                    get_m_name(i),
+                    "(Lorg/codehaus/groovy/reflection/CachedMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
+            mv.visitInsn(ARETURN);
+        }
+
+        // call helper for error
+        mv.visitLabel(defaultLabel);
+        mv.visitMethodInsn(
+                INVOKEVIRTUAL,
+                classInternalName,
+                "noSuchMethod",
+                "(Lorg/codehaus/groovy/reflection/CachedMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;");
+        mv.visitInsn(ARETURN);
+        // end method
+        mv.visitMaxs(4, 4);
+        mv.visitEnd();
+
+        // create helper methods m*
+        for (int i = 0; i < methodCount; i++) {
+            mv = cv.visitMethod(
+                    ACC_PRIVATE,
+                    get_m_name(i),
+                    "(Lorg/codehaus/groovy/reflection/CachedMethod;Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;",
+                    null,
+                    null);
+            helper = new BytecodeHelper(mv);
+
+            CachedMethod method = (CachedMethod) methods.get(i);
+            invokeMethod(method, mv);
+            if (method.getReturnType() == void.class) {
+                mv.visitInsn(ACONST_NULL);
+            }
+            mv.visitInsn(ARETURN);
+            mv.visitMaxs(0, 0);
+            mv.visitEnd();
+        }
+    }
+
+    protected void invokeMethod(CachedMethod method, MethodVisitor mv) {
+        // compute class to make the call on
+        Class callClass = method.getDeclaringClass();
+        boolean useInterface = callClass.isInterface();
+//        if (callClass == null) {
+//            callClass = method.getCallClass();
+//        } else {
+//            useInterface = true;
+//        }
+        // get bytecode information
+        String type = BytecodeHelper.getClassInternalName(callClass.getName());
+        String descriptor = BytecodeHelper.getMethodDescriptor(method.getReturnType(), method.getNativeParameterTypes());
+
+        // make call
+        if (method.isStatic()) {
+            loadParameters(method, 3, mv);
+            mv.visitMethodInsn(INVOKESTATIC, type, method.getName(), descriptor);
+        } else {
+            mv.visitVarInsn(ALOAD, 2);
+            helper.doCast(callClass);
+            loadParameters(method, 3, mv);
+            mv.visitMethodInsn((useInterface) ? INVOKEINTERFACE : INVOKEVIRTUAL, type, method.getName(), descriptor);
+        }
+
+        helper.box(method.getReturnType());
+    }
+
+    protected void loadParameters(CachedMethod method, int argumentIndex, MethodVisitor mv) {
+        CachedClass[] parameters = method.getParameterTypes();
+        int size = parameters.length;
+        for (int i = 0; i < size; i++) {
+            // unpack argument from Object[]
+            mv.visitVarInsn(ALOAD, argumentIndex);
+            helper.pushConstant(i);
+            mv.visitInsn(AALOAD);
+
+            // cast argument to parameter class, inclusive unboxing
+            // for methods with primitive types
+            Class type = parameters[i].getCachedClass();
+            if (type.isPrimitive()) {
+                helper.unbox(type);
+            } else {
+                helper.doCast(type);
+            }
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/RuntimeIncompleteClassException.java b/groovy/src/main/org/codehaus/groovy/classgen/RuntimeIncompleteClassException.java
new file mode 100644
index 0000000..0a46eb0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/RuntimeIncompleteClassException.java
@@ -0,0 +1,32 @@
+/*******************************************************************************
+ * Copyright (c) 2004 IBM Corporation and others.
+ * All rights reserved.   This program and the accompanying materials
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ * IBM - Initial API and implementation
+ ******************************************************************************/
+package org.codehaus.groovy.classgen;
+
+import java.util.List;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.syntax.RuntimeParserException;
+
+/**
+ * RuntimeIncompleteClassException
+ * 
+ */
+public class RuntimeIncompleteClassException extends RuntimeParserException {
+
+    /**
+     * @param classnames names of classes
+     * @param node the node containing the error
+     */
+    public RuntimeIncompleteClassException(List classnames, ASTNode node) {
+        super("Incomplete class: does not implement abstract methods: " + classnames, node);
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/Variable.java b/groovy/src/main/org/codehaus/groovy/classgen/Variable.java
new file mode 100644
index 0000000..f58a542
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/Variable.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.objectweb.asm.Label;
+
+/**
+ * Represents compile time variable metadata while compiling a method.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @version $Revision$
+ */
+public class Variable {
+    
+    public static final Variable THIS_VARIABLE = new Variable();
+    public static final Variable SUPER_VARIABLE = new Variable();
+
+    private int index;
+    private ClassNode type;
+    private String name;
+    private boolean holder;
+    private boolean property;
+
+    // br for setting on the LocalVariableTable in the class file
+    // these fields should probably go to jvm Operand class
+    private Label startLabel = null;
+    private Label endLabel = null;
+    private boolean dynamicTyped;
+
+    private Variable(){
+        dynamicTyped = true;
+        index=0;
+        holder=false;
+        property=false;
+    }
+    
+    public Variable(int index, ClassNode type, String name) {
+        this.index = index;
+        this.type = type;
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public ClassNode getType() {
+        return type;
+    }
+    
+    public String getTypeName() {
+        return type.getName();
+    }
+
+    /**
+     * @return the stack index for this variable
+     */
+    public int getIndex() {
+        return index;
+    }
+
+    /**
+     * @return is this local variable shared in other scopes (and so must use a ValueHolder)
+     */
+    public boolean isHolder() {
+        return holder;
+    }
+
+    public void setHolder(boolean holder) {
+        this.holder = holder;
+    }
+
+    public boolean isProperty() {
+        return property;
+    }
+
+    public void setProperty(boolean property) {
+        this.property = property;
+    }
+    
+    public Label getStartLabel() {
+        return startLabel;
+    }
+
+    public void setStartLabel(Label startLabel) {
+        this.startLabel = startLabel;
+    }
+
+    public Label getEndLabel() {
+        return endLabel;
+    }
+
+    public void setEndLabel(Label endLabel) {
+        this.endLabel = endLabel;
+    }
+
+    public String toString() {
+        // TODO Auto-generated method stub
+        return super.toString() + "[" + type + " " + name + " (" + index + ")";
+    }
+
+    public void setType(ClassNode type) {
+        this.type = type;
+        dynamicTyped |= type==ClassHelper.DYNAMIC_TYPE;
+    }
+
+    public void setDynamicTyped(boolean b) {
+        dynamicTyped = b;
+    }
+    
+    public boolean isDynamicTyped() {
+        return dynamicTyped;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/VariableScopeVisitor.java b/groovy/src/main/org/codehaus/groovy/classgen/VariableScopeVisitor.java
new file mode 100644
index 0000000..94bb4a1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/VariableScopeVisitor.java
@@ -0,0 +1,501 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * goes through an AST and initializes the scopes
+ *
+ * @author Jochen Theodorou
+ */
+public class VariableScopeVisitor extends ClassCodeVisitorSupport {
+    private VariableScope currentScope = null;
+    private VariableScope headScope = new VariableScope();
+    private ClassNode currentClass = null;
+    private SourceUnit source;
+    private boolean inClosure = false;
+    private boolean inPropertyExpression = false;
+    private boolean isSpecialConstructorCall = false;
+
+    private LinkedList stateStack = new LinkedList();
+
+    private class StateStackElement {
+        VariableScope scope;
+        ClassNode clazz;
+        boolean closure;
+
+        StateStackElement() {
+            scope = VariableScopeVisitor.this.currentScope;
+            clazz = VariableScopeVisitor.this.currentClass;
+            closure = VariableScopeVisitor.this.inClosure;
+        }
+    }
+
+    public VariableScopeVisitor(SourceUnit source) {
+        this.source = source;
+        currentScope = headScope;
+    }
+
+    // ------------------------------
+    // helper methods   
+    //------------------------------
+
+    private void pushState(boolean isStatic) {
+        stateStack.add(new StateStackElement());
+        currentScope = new VariableScope(currentScope);
+        currentScope.setInStaticContext(isStatic);
+    }
+
+    private void pushState() {
+        pushState(currentScope.isInStaticContext());
+    }
+
+    private void popState() {
+        // a scope in a closure is never really static
+        // the checking needs this to be as the surrounding
+        // method to correctly check the access to variables.
+        // But a closure and all nested scopes are a result
+        // of calling a non static method, so the context
+        // is not static.
+        if (inClosure) currentScope.setInStaticContext(false);
+
+        StateStackElement element = (StateStackElement) stateStack.removeLast();
+        currentScope = element.scope;
+        currentClass = element.clazz;
+        inClosure = element.closure;
+    }
+
+    private void declare(Parameter[] parameters, ASTNode node) {
+        for (int i = 0; i < parameters.length; i++) {
+            if (parameters[i].hasInitialExpression()) {
+                parameters[i].getInitialExpression().visit(this);
+            }
+            declare(parameters[i], node);
+        }
+    }
+
+    private void declare(VariableExpression expr) {
+        declare(expr, expr);
+    }
+
+    private void declare(Variable var, ASTNode expr) {
+        String scopeType = "scope";
+        String variableType = "variable";
+
+        if (expr.getClass() == FieldNode.class) {
+            scopeType = "class";
+            variableType = "field";
+        } else if (expr.getClass() == PropertyNode.class) {
+            scopeType = "class";
+            variableType = "property";
+        }
+
+        StringBuffer msg = new StringBuffer();
+        msg.append("The current ").append(scopeType);
+        msg.append(" already contains a ").append(variableType);
+        msg.append(" of the name ").append(var.getName());
+
+        if (currentScope.getDeclaredVariable(var.getName()) != null) {
+            addError(msg.toString(), expr);
+            return;
+        }
+
+        for (VariableScope scope = currentScope.getParent(); scope != null; scope = scope.getParent()) {
+            // if we are in a class and no variable is declared until
+            // now, then we can break the loop, because we are allowed
+            // to declare a variable of the same name as a class member
+            if (scope.getClassScope() != null) break;
+
+            if (scope.getDeclaredVariable(var.getName()) != null) {
+                // variable already declared
+                addError(msg.toString(), expr);
+                break;
+            }
+        }
+        // declare the variable even if there was an error to allow more checks
+        currentScope.putDeclaredVariable(var);
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return source;
+    }
+
+    private Variable findClassMember(ClassNode cn, String name) {
+        if (cn == null) return null;
+        if (cn.isScript()) {
+            return new DynamicVariable(name, false);
+        }
+        List l = cn.getFields();
+        for (Iterator iter = l.iterator(); iter.hasNext();) {
+            FieldNode f = (FieldNode) iter.next();
+            if (f.getName().equals(name)) return f;
+        }
+
+        l = cn.getMethods();
+        for (Iterator iter = l.iterator(); iter.hasNext();) {
+            MethodNode f = (MethodNode) iter.next();
+            String methodName = f.getName();
+            String pName = getPropertyName(f);
+            if (pName == null) continue;
+            if (!pName.equals(name)) continue;
+            PropertyNode var = new PropertyNode(pName, f.getModifiers(), getPropertyType(f), cn, null, null, null);
+            return var;
+        }
+
+        l = cn.getProperties();
+        for (Iterator iter = l.iterator(); iter.hasNext();) {
+            PropertyNode f = (PropertyNode) iter.next();
+            if (f.getName().equals(name)) return f;
+        }
+
+        Variable ret = findClassMember(cn.getSuperClass(), name);
+        if (ret != null) return ret;
+        return findClassMember(cn.getOuterClass(), name);
+    }
+
+    private ClassNode getPropertyType(MethodNode m) {
+        String name = m.getName();
+        if (m.getReturnType() != ClassHelper.VOID_TYPE) {
+            return m.getReturnType();
+        }
+        return m.getParameters()[0].getType();
+    }
+
+    private String getPropertyName(MethodNode m) {
+        String name = m.getName();
+        if (!(name.startsWith("set") || name.startsWith("get"))) return null;
+        String pname = name.substring(3);
+        if (pname.length() == 0) return null;
+        String s = pname.substring(0, 1).toLowerCase();
+        String rest = pname.substring(1);
+        pname = s + rest;
+
+        if (name.startsWith("get") && m.getReturnType() == ClassHelper.VOID_TYPE) {
+            return null;
+        }
+        if (name.startsWith("set") && m.getParameters().length != 1) {
+            return null;
+        }
+        return pname;
+    }
+
+    // -------------------------------
+    // different Variable based checks  
+    // -------------------------------
+
+    private Variable checkVariableNameForDeclaration(String name, Expression expression) {
+        if ("super".equals(name) || "this".equals(name)) return null;
+
+        VariableScope scope = currentScope;
+        Variable var = new DynamicVariable(name, currentScope.isInStaticContext());
+        Variable dummyStart = var;
+        // try to find a declaration of a variable
+        VariableScope dynamicScope = null;
+        while (!scope.isRoot()) {
+            if (dynamicScope == null && scope.isResolvingDynamic()) {
+                dynamicScope = scope;
+            }
+
+            Variable var1;
+            var1 = scope.getDeclaredVariable(var.getName());
+
+            if (var1 != null) {
+                var = var1;
+                break;
+            }
+
+            var1 = (Variable) scope.getReferencedLocalVariable(var.getName());
+            if (var1 != null) {
+                var = var1;
+                break;
+            }
+
+            var1 = scope.getReferencedClassVariable(var.getName());
+            if (var1 != null) {
+                var = var1;
+                break;
+            }
+
+            ClassNode classScope = scope.getClassScope();
+            if (classScope != null) {
+                Variable member = findClassMember(classScope, var.getName());
+                if (member != null) {
+                    boolean cc = currentScope.isInStaticContext() || isSpecialConstructorCall;
+                    boolean cm = member.isInStaticContext();
+                    //
+                    // we don't allow access from dynamic context to static context
+                    //
+                    // cm==cc: 
+                    //   we always allow access if the context is in both cases static 
+                    //   or dynamic
+                    // cm==true: 
+                    //   the member is static, which means access is always allowed
+                    // cm||cm==cc:
+                    //   is false only for the case cc==true and cm==false, which means
+                    //   the member is a dynamic context, but the current scope is static.
+                    //
+                    // One example for (cm||cm==cc)==false is a static method trying to 
+                    // access a non static field.
+                    //
+                    if (cm || cm == cc) var = member;
+                }
+                break;
+            }
+            scope = scope.getParent();
+        }
+
+        VariableScope end = scope;
+
+        if (scope.isRoot() && dynamicScope == null) {
+            // no matching scope found
+            declare(var, expression);
+            addError("The variable " + var.getName() +
+                    " is undefined in the current scope", expression);
+        } else if (scope.isRoot() && dynamicScope != null) {
+            // no matching scope found, but there was a scope that
+            // resolves dynamic
+            scope = dynamicScope;
+        }
+
+        if (!scope.isRoot()) {
+            scope = currentScope;
+            while (scope != end) {
+                Map references = null;
+                if (end.isClassScope() || end.isRoot() ||
+                        (end.isReferencedClassVariable(name) && end.getDeclaredVariable(name) == null)) {
+                    scope.putReferencedClassVariable(var);
+                } else {
+                    var.setClosureSharedVariable(var.isClosureSharedVariable() || inClosure);
+                    scope.putReferencedLocalVariable(var);
+                }
+                scope = scope.getParent();
+            }
+            if (end.isResolvingDynamic()) {
+                if (end.getDeclaredVariable(var.getName()) == null) {
+                    end.putDeclaredVariable(var);
+                }
+            }
+        }
+
+        return var;
+    }
+
+    /**
+     * a property on "this", like this.x is transformed to a
+     * direct field access, so we need to check the
+     * static context here
+     */
+    private void checkPropertyOnExplicitThis(PropertyExpression pe) {
+        if (!currentScope.isInStaticContext()) return;
+        Expression object = pe.getObjectExpression();
+        if (!(object instanceof VariableExpression)) return;
+        VariableExpression ve = (VariableExpression) object;
+        if (!ve.getName().equals("this")) return;
+        String name = pe.getPropertyAsString();
+        if (name == null) return;
+        Variable member = findClassMember(currentClass, name);
+        if (member == null) return;
+        checkVariableContextAccess(member, pe);
+    }
+
+    private void checkVariableContextAccess(Variable v, Expression expr) {
+        if (inPropertyExpression || v.isInStaticContext() || !currentScope.isInStaticContext()) return;
+
+        String msg = v.getName() +
+                " is declared in a dynamic context, but you tried to" +
+                " access it from a static context.";
+        addError(msg, expr);
+
+        // declare a static variable to be able to continue the check
+        DynamicVariable v2 = new DynamicVariable(v.getName(), currentScope.isInStaticContext());
+        currentScope.putDeclaredVariable(v2);
+    }
+
+    // ------------------------------
+    // code visit  
+    // ------------------------------
+
+    public void visitBlockStatement(BlockStatement block) {
+        pushState();
+        block.setVariableScope(currentScope);
+        super.visitBlockStatement(block);
+        popState();
+    }
+
+    public void visitForLoop(ForStatement forLoop) {
+        pushState();
+        forLoop.setVariableScope(currentScope);
+        Parameter p = (Parameter) forLoop.getVariable();
+        p.setInStaticContext(currentScope.isInStaticContext());
+        if (p != ForStatement.FOR_LOOP_DUMMY) declare(p, forLoop);
+        super.visitForLoop(forLoop);
+        popState();
+    }
+
+    public void visitDeclarationExpression(DeclarationExpression expression) {
+        // visit right side first to avoid the usage of a 
+        // variable before its declaration
+        expression.getRightExpression().visit(this);
+        // no need to visit left side, just get the variable name
+        VariableExpression vex = expression.getVariableExpression();
+        vex.setInStaticContext(currentScope.isInStaticContext());
+        declare(vex);
+        vex.setAccessedVariable(vex);
+    }
+
+    public void visitVariableExpression(VariableExpression expression) {
+        String name = expression.getName();
+        Variable v = checkVariableNameForDeclaration(name, expression);
+        if (v == null) return;
+        expression.setAccessedVariable(v);
+        checkVariableContextAccess(v, expression);
+    }
+
+    public void visitPropertyExpression(PropertyExpression expression) {
+        boolean ipe = inPropertyExpression;
+        inPropertyExpression = true;
+        expression.getObjectExpression().visit(this);
+        inPropertyExpression = false;
+        expression.getProperty().visit(this);
+        checkPropertyOnExplicitThis(expression);
+        inPropertyExpression = ipe;
+    }
+
+    public void visitClosureExpression(ClosureExpression expression) {
+        pushState();
+
+        inClosure = true;
+        // as result of the Paris meeting Closure resolves
+        // always dynamically
+        currentScope.setDynamicResolving(true);
+
+        expression.setVariableScope(currentScope);
+
+        if (expression.isParameterSpecified()) {
+            Parameter[] parameters = expression.getParameters();
+            for (int i = 0; i < parameters.length; i++) {
+                parameters[i].setInStaticContext(currentScope.isInStaticContext());
+                if (parameters[i].hasInitialExpression()) {
+                    parameters[i].getInitialExpression().visit(this);
+                }
+                declare(parameters[i], expression);                
+            }
+        } else if (expression.getParameters() != null) {
+            DynamicVariable var = new DynamicVariable("it", currentScope.isInStaticContext());
+            currentScope.putDeclaredVariable(var);
+        }
+
+        super.visitClosureExpression(expression);
+        popState();
+    }
+
+    public void visitCatchStatement(CatchStatement statement) {
+        pushState();
+        Parameter p = (Parameter) statement.getVariable();
+        p.setInStaticContext(currentScope.isInStaticContext());
+        declare(p, statement);
+        super.visitCatchStatement(statement);
+        popState();
+    }
+
+    public void visitFieldExpression(FieldExpression expression) {
+        String name = expression.getFieldName();
+        //TODO: change that to get the correct scope
+        Variable v = checkVariableNameForDeclaration(name, expression);
+        checkVariableContextAccess(v, expression);
+    }
+
+    // ------------------------------
+    // class visit  
+    // ------------------------------
+
+    public void visitClass(ClassNode node) {
+        pushState();
+
+        currentClass = node;
+        boolean dynamicMode = node.isScript();
+        currentScope.setDynamicResolving(dynamicMode);
+        currentScope.setClassScope(node);
+
+        super.visitClass(node);
+        popState();
+    }
+
+    protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+        pushState(node.isStatic());
+
+        node.setVariableScope(currentScope);
+
+        // GROOVY-2156
+        Parameter[] parameters = node.getParameters();
+        for (int i = 0; i < parameters.length; i++) {
+            Parameter parameter = parameters[i];
+            visitAnnotations(parameter);
+        }
+
+        declare(node.getParameters(), node);
+
+        super.visitConstructorOrMethod(node, isConstructor);
+        popState();
+    }
+
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        if (call.isImplicitThis() && call.getMethod() instanceof ConstantExpression) {
+            Object value = ((ConstantExpression) call.getMethod()).getText();
+            if (!(value instanceof String)) {
+                throw new GroovyBugError("tried to make a method call with a non-String constant method name.");
+            }
+            String methodName = (String) value;
+            Variable v = checkVariableNameForDeclaration(methodName, call);
+            if (v != null && !(v instanceof DynamicVariable)) {
+                checkVariableContextAccess(v, call);
+            }
+        }
+        super.visitMethodCallExpression(call);
+    }
+
+    public void visitConstructorCallExpression(ConstructorCallExpression call) {
+        isSpecialConstructorCall = call.isSpecialCall();
+        super.visitConstructorCallExpression(call);
+        isSpecialConstructorCall = false;
+    }
+
+    public void visitProperty(PropertyNode node) {
+        pushState(node.isStatic());
+        super.visitProperty(node);
+        popState();
+    }
+
+    public void visitField(FieldNode node) {
+        pushState(node.isStatic());
+        super.visitField(node);
+        popState();
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/Verifier.java b/groovy/src/main/org/codehaus/groovy/classgen/Verifier.java
new file mode 100644
index 0000000..213fe25
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/Verifier.java
@@ -0,0 +1,820 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.MetaClass;
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.*;
+import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
+import org.codehaus.groovy.syntax.RuntimeParserException;
+import org.codehaus.groovy.syntax.Token;
+import org.codehaus.groovy.syntax.Types;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/**
+ * Verifies the AST node and adds any defaulted AST code before
+ * bytecode generation occurs.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class Verifier implements GroovyClassVisitor, Opcodes {
+
+    public static final String __TIMESTAMP = "__timeStamp";
+    public static final String __TIMESTAMP__ = "__timeStamp__239_neverHappen";
+	private ClassNode classNode;
+    private MethodNode methodNode;
+
+    public ClassNode getClassNode() {
+        return classNode;
+    }
+
+    public MethodNode getMethodNode() {
+        return methodNode;
+    }
+
+    /**
+     * add code to implement GroovyObject
+     * @param node
+     */
+    public void visitClass(ClassNode node) {
+        this.classNode = node;
+        
+        if ((classNode.getModifiers() & Opcodes.ACC_INTERFACE) >0) {
+            //interfaces have no construcotrs, but this code expects one, 
+            //so create a dummy and don't add it to the class node
+            ConstructorNode dummy = new ConstructorNode(0,null);
+            addInitialization(node, dummy);
+            node.visitContents(this);
+            return;
+        }
+        
+        addDefaultParameterMethods(node);
+        addDefaultParameterConstructors(node);
+
+        if (!node.isDerivedFromGroovyObject()) {
+            node.addInterface(ClassHelper.make(GroovyObject.class));
+
+            // lets add a new field for the metaclass
+            StaticMethodCallExpression initMetaClassCall =
+                new StaticMethodCallExpression(
+                    ClassHelper.make(ScriptBytecodeAdapter.class),
+                    "initMetaClass",
+                    VariableExpression.THIS_EXPRESSION);
+
+            PropertyNode metaClassProperty =
+                node.addProperty("metaClass", ACC_PUBLIC, ClassHelper.make(MetaClass.class), initMetaClassCall, null, null);
+            metaClassProperty.setSynthetic(true);
+            FieldNode metaClassField = metaClassProperty.getField();
+            metaClassField.setModifiers(metaClassField.getModifiers() | ACC_TRANSIENT);
+
+            FieldExpression metaClassVar = new FieldExpression(metaClassField);
+            IfStatement initMetaClassField =
+                new IfStatement(
+                    new BooleanExpression(
+                        new BinaryExpression(metaClassVar, Token.newSymbol( Types.COMPARE_EQUAL, -1, -1), ConstantExpression.NULL)),
+                    new ExpressionStatement(new BinaryExpression(metaClassVar, Token.newSymbol( Types.EQUAL, -1, -1), initMetaClassCall)),
+                    EmptyStatement.INSTANCE);
+
+            node.addSyntheticMethod(
+                "getMetaClass",
+                ACC_PUBLIC,
+                ClassHelper.make(MetaClass.class),
+                Parameter.EMPTY_ARRAY,
+                ClassNode.EMPTY_ARRAY,
+                new BlockStatement(new Statement[] { initMetaClassField, new ReturnStatement(metaClassVar)}, new VariableScope())
+            );
+
+            // @todo we should check if the base class implements the invokeMethod method
+
+            // lets add the invokeMethod implementation
+            ClassNode superClass = node.getSuperClass();
+            boolean addDelegateObject =
+                (node instanceof InnerClassNode && superClass.equals(ClassHelper.CLOSURE_TYPE))
+                    || superClass.equals(ClassHelper.GSTRING_TYPE);
+
+            // don't do anything as the base class implements the invokeMethod
+            if (!addDelegateObject) {
+                
+                VariableExpression vMethods = new VariableExpression("method");
+                VariableExpression vArguments = new VariableExpression("arguments");
+                VariableScope blockScope = new VariableScope();
+                blockScope.putReferencedLocalVariable(vMethods);
+                blockScope.putReferencedLocalVariable(vArguments);
+                
+                node.addSyntheticMethod(
+                    "invokeMethod",
+                    ACC_PUBLIC,
+                    ClassHelper.OBJECT_TYPE,
+                    new Parameter[] {
+                        new Parameter(ClassHelper.STRING_TYPE, "method"),
+                        new Parameter(ClassHelper.OBJECT_TYPE, "arguments")
+                    },
+                    ClassNode.EMPTY_ARRAY,    
+                    new BlockStatement(
+                        new Statement[] {
+                            initMetaClassField,
+                            new ReturnStatement(
+                                new MethodCallExpression(
+                                    metaClassVar,
+                                    "invokeMethod",
+                                    new ArgumentListExpression(
+                                            VariableExpression.THIS_EXPRESSION,
+                                            vMethods,
+                                            vArguments
+                                        )
+                                    )
+                                )
+                        },
+                        blockScope
+                    )
+                );
+                
+
+                if (!node.isScript()) {
+                    node.addSyntheticMethod(
+                        "getProperty",
+                        ACC_PUBLIC,
+                        ClassHelper.OBJECT_TYPE,
+                        new Parameter[] { new Parameter(ClassHelper.STRING_TYPE, "property")},
+                        ClassNode.EMPTY_ARRAY,
+                        new BlockStatement(
+                            new Statement[] {
+                                initMetaClassField,
+                                new ReturnStatement(
+                                    new MethodCallExpression(
+                                        metaClassVar,
+                                        "getProperty",
+                                        new ArgumentListExpression(
+                                                VariableExpression.THIS_EXPRESSION,
+                                                new VariableExpression("property"))))
+                            },
+                            new VariableScope()
+                        ));
+                    VariableExpression vProp = new VariableExpression("property");
+                    VariableExpression vValue = new VariableExpression("value");
+                    blockScope = new VariableScope();
+                    blockScope.putReferencedLocalVariable(vProp);
+                    blockScope.putReferencedLocalVariable(vValue);
+                    
+                    node.addSyntheticMethod(
+                        "setProperty",
+                        ACC_PUBLIC,
+                        ClassHelper.VOID_TYPE,
+                        new Parameter[] {
+                            new Parameter(ClassHelper.STRING_TYPE, "property"),
+                            new Parameter(ClassHelper.OBJECT_TYPE, "value")
+                        },
+                        ClassNode.EMPTY_ARRAY,
+                        new BlockStatement(
+                            new Statement[] {
+                                initMetaClassField,
+                                new ExpressionStatement(
+                                    new MethodCallExpression(
+                                        metaClassVar,
+                                        "setProperty",
+                                        new ArgumentListExpression(
+                                                VariableExpression.THIS_EXPRESSION,
+                                                vProp,
+                                                vValue)))
+                            },
+                            blockScope
+                    ));
+                }
+            }
+        }
+
+        if (node.getDeclaredConstructors().isEmpty()) {
+            ConstructorNode constructor = new ConstructorNode(ACC_PUBLIC, null);
+            constructor.setSynthetic(true);
+            node.addConstructor(constructor);
+        }
+        
+        if (!(node instanceof InnerClassNode)) {// add a static timestamp field to the class
+            addTimeStamp(node);
+        }
+        
+        addInitialization(node);
+        checkReturnInObjectInitializer(node.getObjectInitializerStatements());
+        node.getObjectInitializerStatements().clear();
+        addCovariantMethods(node);
+        node.visitContents(this);
+    }
+    
+    protected void addTimeStamp(ClassNode node) {
+        FieldNode timeTagField = new FieldNode(
+                Verifier.__TIMESTAMP,
+                Modifier.PUBLIC | Modifier.STATIC,
+                ClassHelper.Long_TYPE,
+                //"",
+                node,
+                new ConstantExpression(new Long(System.currentTimeMillis())));
+        // alternatively , FieldNode timeTagField = SourceUnit.createFieldNode("public static final long __timeStamp = " + System.currentTimeMillis() + "L");
+        timeTagField.setSynthetic(true);
+        node.addField(timeTagField);
+
+        timeTagField = new FieldNode(
+                Verifier.__TIMESTAMP__ + String.valueOf(System.currentTimeMillis()),
+                Modifier.PUBLIC | Modifier.STATIC,
+                ClassHelper.Long_TYPE,
+                //"",
+                node,
+                new ConstantExpression(new Long(0)));
+        // alternatively , FieldNode timeTagField = SourceUnit.createFieldNode("public static final long __timeStamp = " + System.currentTimeMillis() + "L");
+        timeTagField.setSynthetic(true);
+        node.addField(timeTagField);
+    }
+
+    private void checkReturnInObjectInitializer(List init) {
+        CodeVisitorSupport cvs = new CodeVisitorSupport() {
+            public void visitReturnStatement(ReturnStatement statement) {
+                throw new RuntimeParserException("'return' is not allowed in object initializer",statement);
+            }
+        };
+        for (Iterator iterator = init.iterator(); iterator.hasNext();) {
+            Statement stm = (Statement) iterator.next();
+            stm.visit(cvs);
+        }
+    }
+
+    public void visitConstructor(ConstructorNode node) {
+        CodeVisitorSupport checkSuper = new CodeVisitorSupport() {
+            boolean firstMethodCall = true;
+            String type=null;
+            public void visitMethodCallExpression(MethodCallExpression call) {
+                if (!firstMethodCall) return;
+                firstMethodCall = false;
+                String name = call.getMethodAsString();
+                // the name might not be null if the method name is a GString for example
+                if (name==null) return;
+                if (!name.equals("super") && !name.equals("this")) return;
+                type=name;
+                call.getArguments().visit(this);
+                type=null;
+            }
+            public void visitVariableExpression(VariableExpression expression) {
+                if (type==null) return;
+                String name = expression.getName();
+                if (!name.equals("this") && !name.equals("super")) return;
+                throw new RuntimeParserException("cannot reference "+name+" inside of "+type+"(....) before supertype constructor has been called",expression);
+            }            
+        };
+        Statement s = node.getCode();
+        //todo why can a statement can be null?
+        if (s == null) return;
+        s.visit(checkSuper);
+    }
+
+    public void visitMethod(MethodNode node) {
+        this.methodNode = node;
+        Statement statement = node.getCode();
+        if (!node.isVoidMethod()) {
+            if (statement instanceof ExpressionStatement) {
+                ExpressionStatement expStmt = (ExpressionStatement) statement;
+                node.setCode(new ReturnStatement(expStmt.getExpression()));
+            }
+            else if (statement instanceof BlockStatement) {
+                BlockStatement block = (BlockStatement) statement;
+
+                // lets copy the list so we create a new block
+                List list = new ArrayList(block.getStatements());
+                if (!list.isEmpty()) {
+                    int idx = list.size() - 1;
+                    Statement last = (Statement) list.get(idx);
+                    if (last instanceof ExpressionStatement) {
+                        ExpressionStatement expStmt = (ExpressionStatement) last;
+                        list.set(idx, new ReturnStatement(expStmt));
+                    }
+                    else if (!(last instanceof ReturnStatement)) {
+                        list.add(new ReturnStatement(ConstantExpression.NULL));
+                    }
+                }
+                else {
+                    list.add(new ReturnStatement(ConstantExpression.NULL));
+                }
+
+                node.setCode(new BlockStatement(filterStatements(list),block.getVariableScope()));
+            }
+        }
+        else if (!node.isAbstract()) {
+        	BlockStatement newBlock = new BlockStatement();
+            if (statement instanceof BlockStatement) {
+                newBlock.addStatements(filterStatements(((BlockStatement)statement).getStatements()));
+            }
+            else {
+                newBlock.addStatement(filterStatement(statement));
+            }
+            newBlock.addStatement(ReturnStatement.RETURN_NULL_OR_VOID);
+            node.setCode(newBlock);
+        }
+        if (node.getName().equals("main") && node.isStatic()) {
+            Parameter[] params = node.getParameters();
+            if (params.length == 1) {
+                Parameter param = params[0];
+                if (param.getType() == null || param.getType()==ClassHelper.OBJECT_TYPE) {
+                    param.setType(ClassHelper.STRING_TYPE.makeArray());
+                }
+            }
+        }
+        statement = node.getCode();
+        if (statement!=null) statement.visit(new VerifierCodeVisitor(this));
+    }
+
+    public void visitField(FieldNode node) {
+    }
+
+    public void visitProperty(PropertyNode node) {
+        String name = node.getName();
+        FieldNode field = node.getField();
+
+        String getterName = "get" + capitalize(name);
+        String setterName = "set" + capitalize(name);
+
+        Statement getterBlock = node.getGetterBlock();
+        if (getterBlock == null) {
+            if (!node.isPrivate() && classNode.getGetterMethod(getterName) == null) {
+                getterBlock = createGetterBlock(node, field);
+            }
+        }
+        Statement setterBlock = node.getSetterBlock();
+        if (setterBlock == null) {
+            if (!node.isPrivate() && (node.getModifiers()&ACC_FINAL)==0 && classNode.getSetterMethod(setterName) == null) {
+                setterBlock = createSetterBlock(node, field);
+            }
+        }
+
+        if (getterBlock != null) {
+            MethodNode getter =
+                new MethodNode(getterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
+            getter.setSynthetic(true);
+            classNode.addMethod(getter);
+            visitMethod(getter);
+
+            if (ClassHelper.boolean_TYPE==node.getType() || ClassHelper.Boolean_TYPE==node.getType()) {
+                String secondGetterName = "is" + capitalize(name);
+                MethodNode secondGetter =
+                    new MethodNode(secondGetterName, node.getModifiers(), node.getType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, getterBlock);
+                secondGetter.setSynthetic(true);
+                classNode.addMethod(secondGetter);
+                visitMethod(secondGetter);
+            }
+        }
+        if (setterBlock != null) {
+            Parameter[] setterParameterTypes = { new Parameter(node.getType(), "value")};
+            MethodNode setter =
+                new MethodNode(setterName, node.getModifiers(), ClassHelper.VOID_TYPE, setterParameterTypes, ClassNode.EMPTY_ARRAY, setterBlock);
+            setter.setSynthetic(true);
+            classNode.addMethod(setter);
+            visitMethod(setter);
+        }
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+    
+    private interface DefaultArgsAction {
+        void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method);
+    }
+    
+    /**
+     * Creates a new helper method for each combination of default parameter expressions 
+     */
+    protected void addDefaultParameterMethods(final ClassNode node) {
+        List methods = new ArrayList(node.getMethods());
+        addDefaultParameters(methods, new DefaultArgsAction(){
+            public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) {
+                MethodCallExpression expression = new MethodCallExpression(VariableExpression.THIS_EXPRESSION, method.getName(), arguments);
+                expression.setImplicitThis(true);
+                Statement code = null;
+                if (method.isVoidMethod()) {
+                    code = new ExpressionStatement(expression);
+                } else {
+                    code = new ReturnStatement(expression);
+                }
+                node.addMethod(method.getName(), method.getModifiers(), method.getReturnType(), newParams, method.getExceptions(), code);
+            }
+        });
+    }
+    
+    protected void addDefaultParameterConstructors(final ClassNode node) {
+        List methods = new ArrayList(node.getDeclaredConstructors());
+        addDefaultParameters(methods, new DefaultArgsAction(){
+            public void call(ArgumentListExpression arguments, Parameter[] newParams, MethodNode method) {
+                ConstructorNode ctor = (ConstructorNode) method;
+                ConstructorCallExpression expression = new ConstructorCallExpression(ClassNode.THIS, arguments);
+                Statement code = new ExpressionStatement(expression);
+                node.addConstructor(ctor.getModifiers(), newParams, ctor.getExceptions(), code);
+            }
+        });
+    }
+
+    /**
+     * Creates a new helper method for each combination of default parameter expressions 
+     */
+    protected void addDefaultParameters(List methods, DefaultArgsAction action) {
+        for (Iterator iter = methods.iterator(); iter.hasNext();) {
+            MethodNode method = (MethodNode) iter.next();
+            if (method.hasDefaultValue()) {
+                Parameter[] parameters = method.getParameters();
+                int counter = 0;
+                List paramValues = new ArrayList();
+                int size = parameters.length;
+                for (int i = size - 1; i >= 0; i--) {
+                    Parameter parameter = parameters[i];
+                    if (parameter != null && parameter.hasInitialExpression()) {
+                        paramValues.add(new Integer(i));
+                        paramValues.add(parameter.getInitialExpression());
+                        counter++;
+                    }
+                }
+
+                for (int j = 1; j <= counter; j++) {
+                    Parameter[] newParams =  new Parameter[parameters.length - j];
+                    ArgumentListExpression arguments = new ArgumentListExpression();
+                    int index = 0;
+                    int k = 1;
+                    for (int i = 0; i < parameters.length; i++) {
+                        if (k > counter - j && parameters[i] != null && parameters[i].hasInitialExpression()) {
+                            arguments.addExpression(parameters[i].getInitialExpression());
+                            k++;
+                        }
+                        else if (parameters[i] != null && parameters[i].hasInitialExpression()) {
+                            newParams[index++] = parameters[i];
+                            arguments.addExpression(new VariableExpression(parameters[i].getName()));
+                            k++;
+                        }
+                        else {
+                            newParams[index++] = parameters[i];
+                            arguments.addExpression(new VariableExpression(parameters[i].getName()));
+                        }
+                    }
+                    if (parameters.length>0 && parameters[parameters.length-1].getType().isArray()) {
+                        // vargs call... better expand the argument:
+                        Expression exp = arguments.getExpression(parameters.length-1);
+                        SpreadExpression se = new SpreadExpression(exp);
+                        arguments.getExpressions().set(parameters.length-1, se);
+                    }
+                    action.call(arguments,newParams,method);
+                }
+                
+                for (int i = 0; i < parameters.length; i++) {
+                    // remove default expression
+                    parameters[i].setInitialExpression(null);
+                }
+            }
+        }
+    }
+
+    protected void addClosureCode(InnerClassNode node) {
+        // add a new invoke
+    }
+
+    protected void addInitialization(ClassNode node) {
+        for (Iterator iter = node.getDeclaredConstructors().iterator(); iter.hasNext();) {
+            addInitialization(node, (ConstructorNode) iter.next());
+        }
+    }
+
+    protected void addInitialization(ClassNode node, ConstructorNode constructorNode) {
+        Statement firstStatement = constructorNode.getFirstStatement();
+        ConstructorCallExpression first = getFirstIfSpecialConstructorCall(firstStatement);
+        
+        // in case of this(...) let the other constructor do the intit
+        if (first!=null && first.isThisCall()) return;
+        
+        List statements = new ArrayList();
+        List staticStatements = new ArrayList();
+        for (Iterator iter = node.getFields().iterator(); iter.hasNext();) {
+            addFieldInitialization(statements, staticStatements, (FieldNode) iter.next());
+        }
+        statements.addAll(node.getObjectInitializerStatements());
+        if (!statements.isEmpty()) {
+            Statement code = constructorNode.getCode();
+            BlockStatement block = new BlockStatement();
+            List otherStatements = block.getStatements();
+            if (code instanceof BlockStatement) {
+                block = (BlockStatement) code;
+                otherStatements=block.getStatements();
+            }
+            else if (code != null) {
+                otherStatements.add(code);
+            }
+            if (!otherStatements.isEmpty()) {
+                if (first!=null) {
+                    // it is super(..) since this(..) is already covered
+                    otherStatements.remove(0);
+                    statements.add(0, firstStatement);
+                } 
+                statements.addAll(otherStatements);
+            }
+            constructorNode.setCode(new BlockStatement(statements, block.getVariableScope()));
+        }
+
+        if (!staticStatements.isEmpty()) {
+            node.addStaticInitializerStatements(staticStatements,true);
+        }
+    }
+
+    private ConstructorCallExpression getFirstIfSpecialConstructorCall(Statement code) {
+        if (code == null || !(code instanceof ExpressionStatement)) return null;
+
+        Expression expression = ((ExpressionStatement)code).getExpression();
+        if (!(expression instanceof ConstructorCallExpression)) return null;
+        ConstructorCallExpression cce = (ConstructorCallExpression) expression;
+        if (cce.isSpecialCall()) return cce;
+        return null;
+    }
+
+    protected void addFieldInitialization(
+        List list,
+        List staticList,
+        FieldNode fieldNode) {
+        Expression expression = fieldNode.getInitialExpression();
+        if (expression != null) {
+            ExpressionStatement statement =
+                new ExpressionStatement(
+                    new BinaryExpression(
+                        new FieldExpression(fieldNode),
+                        Token.newSymbol(Types.EQUAL, fieldNode.getLineNumber(), fieldNode.getColumnNumber()),
+                        expression));
+            if (fieldNode.isStatic()) {
+                staticList.add(statement);
+            }
+            else {
+                list.add(statement);
+            }
+        }
+    }
+
+    /**
+     * Capitalizes the start of the given bean property name
+     */
+    public static String capitalize(String name) {
+        return name.substring(0, 1).toUpperCase() + name.substring(1, name.length());
+    }
+
+    protected Statement createGetterBlock(PropertyNode propertyNode, FieldNode field) {
+        Expression expression = new FieldExpression(field);
+        return new ReturnStatement(expression);
+    }
+
+    protected Statement createSetterBlock(PropertyNode propertyNode, FieldNode field) {
+        Expression expression = new FieldExpression(field);
+        return new ExpressionStatement(
+            new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("value")));
+    }
+
+    /**
+     * Filters the given statements
+     */
+    protected List filterStatements(List list) {
+        List answer = new ArrayList(list.size());
+        for (Iterator iter = list.iterator(); iter.hasNext();) {
+            answer.add(filterStatement((Statement) iter.next()));
+        }
+        return answer;
+    }
+
+    protected Statement filterStatement(Statement statement) {
+        if (statement instanceof ExpressionStatement) {
+            ExpressionStatement expStmt = (ExpressionStatement) statement;
+            Expression expression = expStmt.getExpression();
+            if (expression instanceof ClosureExpression) {
+                ClosureExpression closureExp = (ClosureExpression) expression;
+                if (!closureExp.isParameterSpecified()) {
+                    return closureExp.getCode();
+                }
+            }
+        }
+        return statement;
+    }
+
+    public void visitGenericType(GenericsType genericsType) {
+
+    }
+
+    public static long getTimestamp (Class clazz) {
+        final Field[] fields = clazz.getFields();
+        for (int i = 0; i != fields.length; ++i ) {
+           if (Modifier.isStatic(fields[i].getModifiers())) {
+               final String name = fields[i].getName();
+               if (name.startsWith(__TIMESTAMP__)) {
+                 try {
+                     return Long.decode(name.substring(__TIMESTAMP__.length())).longValue();
+                 }
+                 catch (NumberFormatException e) {
+                     return Long.MAX_VALUE;
+                 }
+             }
+           }
+        }
+        return Long.MAX_VALUE;
+    }
+    
+    protected void addCovariantMethods(ClassNode classNode) {
+        Map methodsToAdd = new HashMap();
+        List declaredMethods = new ArrayList(classNode.getMethods());
+        Map genericsSpec = new HashMap();
+        
+        // remove staic methods from declaredMethods
+        for (Iterator methodsIterator = declaredMethods.iterator(); methodsIterator.hasNext();) {
+            MethodNode m = (MethodNode) methodsIterator.next();
+            if (m.isStatic()) methodsIterator.remove();
+        }
+        
+        addCovariantMethods(classNode, declaredMethods, methodsToAdd, genericsSpec);
+       
+        for (Iterator it = methodsToAdd.values().iterator(); it.hasNext();) {
+            MethodNode method = (MethodNode) it.next();
+            classNode.addMethod(method);
+        }
+    }
+    
+    private void addCovariantMethods(ClassNode classNode, List declaredMethods, Map methodsToAdd, Map oldGenericsSpec) {
+        ClassNode sn = classNode.getUnresolvedSuperClass(false);
+        if (sn!=null && sn.redirect()!=ClassHelper.OBJECT_TYPE) {
+            Map genericsSpec = createGenericsSpec(sn,oldGenericsSpec);
+            for (Iterator it = declaredMethods.iterator(); it.hasNext();) {
+                MethodNode method = (MethodNode) it.next();
+                if (method.isStatic()) continue;
+                storeMissingCovariantMethods(sn,method,methodsToAdd,genericsSpec);
+                addCovariantMethods(sn,declaredMethods,methodsToAdd,genericsSpec);
+            }
+        }
+        
+        ClassNode[] interfaces = classNode.getInterfaces();
+        for (int i=0; i<interfaces.length; i++) {            
+            Map genericsSpec = createGenericsSpec(interfaces[i],oldGenericsSpec);
+            for (Iterator it = declaredMethods.iterator(); it.hasNext();) {
+                MethodNode method = (MethodNode) it.next();
+                if (method.isStatic()) continue;
+                storeMissingCovariantMethods(interfaces[i],method,methodsToAdd,genericsSpec);
+                addCovariantMethods(sn,declaredMethods,methodsToAdd,genericsSpec);
+            }
+        }        
+    }
+    
+    private MethodNode getCovariantImplementation(final MethodNode oldMethod, final MethodNode overridingMethod, Map genericsSpec) {
+        if (!oldMethod.getName().equals(overridingMethod.getName())) return null;
+        if (!equalParameters(overridingMethod,oldMethod,genericsSpec)) return null;
+        ClassNode mr = overridingMethod.getReturnType();
+        ClassNode omr = oldMethod.getReturnType();
+        if (mr.equals(omr)) return null;
+        ClassNode testmr = correctToGenericsSpec(genericsSpec,omr);
+        if (!mr.isDerivedFrom(testmr)) {
+            throw new RuntimeParserException(
+                    "the return type is incompatible with "+
+                    oldMethod.getTypeDescriptor()+
+                    " in "+oldMethod.getDeclaringClass().getName(),
+                    overridingMethod);
+        }
+        if ((oldMethod.getModifiers()&ACC_FINAL)!=0) {
+            throw new RuntimeParserException(
+                    "cannot override final method "+
+                    oldMethod.getTypeDescriptor()+
+                    " in "+oldMethod.getDeclaringClass().getName(),
+                    overridingMethod);
+        }
+        if (oldMethod.isStatic() != overridingMethod.isStatic()){
+            throw new RuntimeParserException(
+                    "cannot override method "+
+                    oldMethod.getTypeDescriptor()+
+                    " in "+oldMethod.getDeclaringClass().getName()+
+                    " with disparate static modifier",
+                    overridingMethod);
+        }
+        
+        MethodNode newMethod = new MethodNode(
+                oldMethod.getName(),
+                overridingMethod.getModifiers() | ACC_SYNTHETIC | ACC_BRIDGE,
+                oldMethod.getReturnType().getPlainNodeReference(),
+                cleanParameters(oldMethod.getParameters()),
+                oldMethod.getExceptions(),
+                null
+        );
+        List instructions = new ArrayList(1);
+        instructions.add (
+                new BytecodeInstruction() {
+                    public void visit(MethodVisitor mv) {
+                        BytecodeHelper helper = new BytecodeHelper(mv);
+                        mv.visitVarInsn(ALOAD,0);
+                        Parameter[] para = oldMethod.getParameters();
+                        Parameter[] goal = overridingMethod.getParameters();
+                        for (int i = 0; i < para.length; i++) {
+                            helper.load(para[i].getType(), i+1);
+                            if (!para[i].getType().equals(goal[i].getType())) {
+                                helper.doCast(goal[i].getType());
+                            }
+                        }
+                        mv.visitMethodInsn(
+                                INVOKEVIRTUAL, 
+                                BytecodeHelper.getClassInternalName(classNode),
+                                overridingMethod.getName(),
+                                BytecodeHelper.getMethodDescriptor(overridingMethod.getReturnType(), overridingMethod.getParameters()));
+                        helper.doReturn(oldMethod.getReturnType());
+                    }
+                }
+
+        );
+        newMethod.setCode(new BytecodeSequence(instructions));
+        return newMethod;
+    }
+    
+    private Parameter[] cleanParameters(Parameter[] parameters) {
+        Parameter[] params = new Parameter[parameters.length];
+        for (int i = 0; i < params.length; i++) {
+            params[i] = new Parameter(parameters[i].getType().getPlainNodeReference(),parameters[i].getName());
+        }
+        return params;
+    }
+
+    private void storeMissingCovariantMethods(ClassNode current, MethodNode method, Map methodsToAdd, Map genericsSpec) {
+        List methods = current.getMethods();
+        for (Iterator sit = methods.iterator(); sit.hasNext();) {
+            MethodNode toOverride = (MethodNode) sit.next();
+            MethodNode bridgeMethod = getCovariantImplementation(toOverride,method,genericsSpec);
+            if (bridgeMethod==null) continue;
+            methodsToAdd.put (bridgeMethod.getTypeDescriptor(),bridgeMethod);
+            return;
+        }
+    }
+    
+    private ClassNode correctToGenericsSpec(Map genericsSpec, GenericsType type) {
+        ClassNode ret = null;
+        if (type.isPlaceholder()){
+            String name = type.getName();
+            ret = (ClassNode) genericsSpec.get(name);
+        }
+        if (ret==null) ret = type.getType();
+        return ret;
+    }
+    
+    private ClassNode correctToGenericsSpec(Map genericsSpec, ClassNode type) {
+        if (type.isGenericsPlaceHolder()){
+            String name = type.getGenericsTypes()[0].getName();
+            type = (ClassNode) genericsSpec.get(name);
+        }
+        if (type==null) type = ClassHelper.OBJECT_TYPE;
+        return type;
+    }
+    
+    private boolean equalParameters(MethodNode m1, MethodNode m2, Map genericsSpec) {
+        Parameter[] p1 = m1.getParameters();
+        Parameter[] p2 = m2.getParameters();
+        if (p1.length!=p2.length) return false;
+        for (int i = 0; i < p2.length; i++) {
+            ClassNode type = p2[i].getType();
+            type = correctToGenericsSpec(genericsSpec,type);
+            if (!p1[i].getType().equals(type)) return false;
+        }
+        return true;
+    }
+    
+    private Map createGenericsSpec(ClassNode current, Map oldSpec) {
+        Map ret = new HashMap(oldSpec);
+        // ret contains the type specs, what we now need is the type spec for the 
+        // current class. To get that we first apply the type parameters to the 
+        // current class and then use the type names of the current class to reset 
+        // the map. Example:
+        //   class A<V,W,X>{}
+        //   class B<T extends Number> extends A<T,Long,String> {}
+        // first we have:    T->Number
+        // we apply it to A<T,Long,String> -> A<Number,Long,String>
+        // resulting in:     V->Number,W->Long,X->String
+
+        GenericsType[] sgts = current.getGenericsTypes();
+        if (sgts!=null) {
+            ClassNode[] spec = new ClassNode[sgts.length];
+            for (int i = 0; i < spec.length; i++) {
+                spec[i]=correctToGenericsSpec(ret, sgts[i]);
+            }
+            GenericsType[] newGts = current.redirect().getGenericsTypes();
+            ret.clear();
+            for (int i = 0; i < spec.length; i++) {
+                ret.put(newGts[i].getName(), spec[i]);
+            }            
+        }
+        return ret;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/VerifierCodeVisitor.java b/groovy/src/main/org/codehaus/groovy/classgen/VerifierCodeVisitor.java
new file mode 100644
index 0000000..c41d996
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/VerifierCodeVisitor.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.classgen;
+
+import java.util.Iterator;
+import java.util.List;
+
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.syntax.RuntimeParserException;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Verifies the method code
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class VerifierCodeVisitor extends CodeVisitorSupport implements Opcodes {
+
+    private Verifier verifier;
+
+    VerifierCodeVisitor(Verifier verifier) {
+        this.verifier = verifier;
+    }
+
+    public void visitMethodCallExpression(MethodCallExpression call) {
+        super.visitMethodCallExpression(call);
+    }
+
+    public void visitForLoop(ForStatement expression) {
+        assertValidIdentifier(expression.getVariable().getName(), "for loop variable name", expression);
+        super.visitForLoop(expression);
+    }
+
+    public void visitPropertyExpression(PropertyExpression expression) {
+        // assertValidIdentifier(expression.getProperty(), "property name", expression);  // This has been commented out to fix the issue Groovy-843
+        super.visitPropertyExpression(expression);
+    }
+
+    public void visitFieldExpression(FieldExpression expression) {
+        if (!expression.getField().isSynthetic()) {
+            assertValidIdentifier(expression.getFieldName(), "field name", expression);
+        }
+        super.visitFieldExpression(expression);
+    }
+
+    public void visitVariableExpression(VariableExpression expression) {
+        assertValidIdentifier(expression.getName(), "variable name", expression);
+        super.visitVariableExpression(expression);
+    }
+
+    public void visitBinaryExpression(BinaryExpression expression) {
+        /*
+        if (verifier.getClassNode().isScript() && expression.getOperation().getType() == Token.EQUAL) {
+            // lets turn variable assignments into property assignments
+            Expression left = expression.getLeftExpression();
+            if (left instanceof VariableExpression) {
+                VariableExpression varExp = (VariableExpression) left;
+
+                //System.out.println("Converting variable expression: " + varExp.getVariable());
+
+                PropertyExpression propExp =
+                    new PropertyExpression(VariableExpression.THIS_EXPRESSION, varExp.getVariable());
+                expression.setLeftExpression(propExp);
+            }
+        }
+        */
+        super.visitBinaryExpression(expression);
+    }
+
+    public static void assertValidIdentifier(String name, String message, ASTNode node) {
+        int size = name.length();
+        if (size <= 0) {
+            throw new RuntimeParserException("Invalid " + message + ". Identifier must not be empty", node);
+        }
+        char firstCh = name.charAt(0);
+        if (!Character.isJavaIdentifierStart(firstCh) || firstCh == '$') {
+            throw new RuntimeParserException("Invalid " + message + ". Must start with a letter but was: " + name, node);
+        }
+
+        for (int i = 1; i < size; i++) {
+            char ch = name.charAt(i);
+            if (!Character.isJavaIdentifierPart(ch)) {
+                throw new RuntimeParserException("Invalid " + message + ". Invalid character at position: " + (i + 1) + " of value:  " + ch + " in name: " + name, node);
+            }
+        }
+    }
+    
+    public void visitListExpression(ListExpression expression) {
+        List expressions = expression.getExpressions();
+        for (Iterator iter = expressions.iterator(); iter.hasNext();) {
+            Object element = iter.next();
+            if (element instanceof MapEntryExpression) {
+                throw new RuntimeParserException ("no map entry allowed at this place",(Expression) element);
+            }
+        }
+        super.visitListExpression(expression);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/classgen/package.html b/groovy/src/main/org/codehaus/groovy/classgen/package.html
new file mode 100644
index 0000000..2069221
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/classgen/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.classgen.*</title>
+  </head>
+  <body>
+    <p>Generates Java classes for Groovy classes using ASM.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/control/CompilationFailedException.java b/groovy/src/main/org/codehaus/groovy/control/CompilationFailedException.java
new file mode 100644
index 0000000..f373931
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/CompilationFailedException.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control;
+
+import groovy.lang.GroovyRuntimeException;
+
+
+/**
+ * Thrown when compilation fails from source errors.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @version $Id$
+ */
+
+public class CompilationFailedException extends GroovyRuntimeException {
+
+    protected int phase;   // The phase in which the failures occurred
+    protected ProcessingUnit unit;    // The *Unit object this exception wraps
+
+    public CompilationFailedException(int phase, ProcessingUnit unit, Throwable cause) {
+        super(Phases.getDescription(phase) + " failed", cause);
+        this.phase = phase;
+        this.unit = unit;
+    }
+
+
+    public CompilationFailedException(int phase, ProcessingUnit unit) {
+        super(Phases.getDescription(phase) + " failed");
+        this.phase = phase;
+        this.unit = unit;
+    }
+
+
+    /**
+     * Formats the error data as a String.
+     */
+
+    /*public String toString() {
+        StringWriter data = new StringWriter();
+        PrintWriter writer = new PrintWriter(data);
+        Janitor janitor = new Janitor();
+
+        try {
+            unit.getErrorReporter().write(writer, janitor);
+        }
+        finally {
+            janitor.cleanup();
+        }
+
+        return data.toString();
+    }*/
+
+
+    /**
+     * Returns the ProcessingUnit in which the error occurred.
+     */
+
+    public ProcessingUnit getUnit() {
+        return this.unit;
+    }
+
+}
+
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/CompilationUnit.java b/groovy/src/main/org/codehaus/groovy/control/CompilationUnit.java
new file mode 100644
index 0000000..8213901
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/CompilationUnit.java
@@ -0,0 +1,949 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.codehaus.groovy.control;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.security.CodeSource;
+import java.util.*;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CompileUnit;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.classgen.AsmClassGenerator;
+import org.codehaus.groovy.classgen.ClassCompletionVerifier;
+import org.codehaus.groovy.classgen.ClassGenerator;
+import org.codehaus.groovy.classgen.EnumVisitor;
+import org.codehaus.groovy.classgen.ExtendedVerifier;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.classgen.VariableScopeVisitor;
+import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.control.io.InputStreamReaderSource;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.control.messages.ExceptionMessage;
+import org.codehaus.groovy.control.messages.Message;
+import org.codehaus.groovy.control.messages.SimpleMessage;
+import org.codehaus.groovy.syntax.SyntaxException;
+import org.codehaus.groovy.tools.GroovyClass;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyRuntimeException;
+
+/**
+ * Collects all compilation data as it is generated by the compiler system.
+ * Allows additional source units to be added and compilation run again (to
+ * affect only the deltas).
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @version $Id$
+ */
+
+public class CompilationUnit extends ProcessingUnit {
+
+    //---------------------------------------------------------------------------
+    // CONSTRUCTION AND SUCH
+
+
+    protected Map sources;    // The SourceUnits from which this unit is built
+    protected Map summariesBySourceName;      // Summary of each SourceUnit
+    protected Map summariesByPublicClassName;       // Summary of each SourceUnit
+    protected Map classSourcesByPublicClassName;    // Summary of each Class
+    protected List names;      // Names for each SourceUnit in sources.
+    protected LinkedList queuedSources;
+
+    protected CompileUnit ast;        // The overall AST for this CompilationUnit.
+    protected List generatedClasses;    // The classes generated during classgen.
+
+    protected Verifier verifier;   // For use by verify().
+
+    protected boolean debug;      // Controls behaviour of classgen() and other routines.
+    protected boolean configured; // Set true after the first configure() operation
+
+    protected ClassgenCallback classgenCallback;  // A callback for use during classgen()
+    protected ProgressCallback progressCallback;  // A callback for use during compile()
+    protected ResolveVisitor resolveVisitor;
+    protected StaticImportVisitor staticImportVisitor;
+
+    LinkedList[] phaseOperations;
+
+
+    /**
+     * Initializes the CompilationUnit with defaults.
+     */
+    public CompilationUnit() {
+        this(null, null, null);
+    }
+
+
+    /**
+     * Initializes the CompilationUnit with defaults except for class loader.
+     */
+    public CompilationUnit(GroovyClassLoader loader) {
+        this(null, null, loader);
+    }
+
+
+    /**
+     * Initializes the CompilationUnit with no security considerations.
+     */
+    public CompilationUnit(CompilerConfiguration configuration) {
+        this(configuration, null, null);
+    }
+
+    /**
+     * Initializes the CompilationUnit with a CodeSource for controlling
+     * security stuff and a class loader for loading classes.
+     */
+    public CompilationUnit(CompilerConfiguration configuration, CodeSource security, GroovyClassLoader loader) {
+        super(configuration, loader, null);
+        this.names = new ArrayList();
+        this.queuedSources = new LinkedList();
+        this.sources = new HashMap();
+        this.summariesBySourceName = new HashMap();
+        this.summariesByPublicClassName = new HashMap();
+        this.classSourcesByPublicClassName = new HashMap();
+
+        this.ast = new CompileUnit(this.classLoader, security, this.configuration);
+        this.generatedClasses = new ArrayList();
+
+
+        this.verifier = new Verifier();
+        this.resolveVisitor = new ResolveVisitor(this);
+        this.staticImportVisitor = new StaticImportVisitor(this);
+
+        phaseOperations = new LinkedList[Phases.ALL + 1];
+        for (int i = 0; i < phaseOperations.length; i++) {
+            phaseOperations[i] = new LinkedList();
+        }
+        addPhaseOperation(new SourceUnitOperation() {
+            public void call(SourceUnit source) throws CompilationFailedException {
+                source.parse();
+            }
+        }, Phases.PARSING);
+        addPhaseOperation(convert, Phases.CONVERSION);
+        addPhaseOperation(new PrimaryClassNodeOperation() {
+            public void call(SourceUnit source, GeneratorContext context,
+                             ClassNode classNode) throws CompilationFailedException {
+                EnumVisitor ev = new EnumVisitor(CompilationUnit.this, source);
+                ev.visitClass(classNode);
+            }
+        }, Phases.CONVERSION);
+        addPhaseOperation(resolve, Phases.SEMANTIC_ANALYSIS);
+        addPhaseOperation(staticImport, Phases.SEMANTIC_ANALYSIS);
+        addPhaseOperation(compileCompleteCheck, Phases.CANONICALIZATION);
+        addPhaseOperation(classgen, Phases.CLASS_GENERATION);
+        addPhaseOperation(output);
+
+        this.classgenCallback = null;
+    }
+
+    public void addPhaseOperation(SourceUnitOperation op, int phase) {
+        if (phase < 0 || phase > Phases.ALL) throw new IllegalArgumentException("phase " + phase + " is unknown");
+        phaseOperations[phase].add(op);
+    }
+
+    public void addPhaseOperation(PrimaryClassNodeOperation op, int phase) {
+        if (phase < 0 || phase > Phases.ALL) throw new IllegalArgumentException("phase " + phase + " is unknown");
+        phaseOperations[phase].add(op);
+    }
+
+    public void addPhaseOperation(GroovyClassOperation op) {
+        phaseOperations[Phases.OUTPUT].addFirst(op);
+    }
+
+
+    /**
+     * Configures its debugging mode and classloader classpath from a given compiler configuration.
+     * This cannot be done more than once due to limitations in {@link java.net.URLClassLoader URLClassLoader}.
+     */
+    public void configure(CompilerConfiguration configuration) {
+        super.configure(configuration);
+        this.debug = configuration.getDebug();
+
+        if (!this.configured && this.classLoader instanceof GroovyClassLoader) {
+            appendCompilerConfigurationClasspathToClassLoader(configuration, (GroovyClassLoader) this.classLoader);
+        }
+
+        this.configured = true;
+    }
+
+    private void appendCompilerConfigurationClasspathToClassLoader(CompilerConfiguration configuration, GroovyClassLoader classLoader) {
+        /*for (Iterator iterator = configuration.getClasspath().iterator(); iterator.hasNext(); ) {
+            classLoader.addClasspath((String) iterator.next());
+        }*/
+    }
+
+    /**
+     * Returns the CompileUnit that roots our AST.
+     */
+    public CompileUnit getAST() {
+        return this.ast;
+    }
+
+    /**
+     * Get the source summaries
+     */
+    public Map getSummariesBySourceName() {
+        return summariesBySourceName;
+    }
+
+    public Map getSummariesByPublicClassName() {
+        return summariesByPublicClassName;
+    }
+
+    public Map getClassSourcesByPublicClassName() {
+        return classSourcesByPublicClassName;
+    }
+
+    public boolean isPublicClass(String className) {
+        return summariesByPublicClassName.containsKey(className);
+    }
+
+
+    /**
+     * Get the GroovyClasses generated by compile().
+     */
+    public List getClasses() {
+        return generatedClasses;
+    }
+
+
+    /**
+     * Convenience routine to get the first ClassNode, for
+     * when you are sure there is only one.
+     */
+    public ClassNode getFirstClassNode() {
+        return (ClassNode) ((ModuleNode) this.ast.getModules().get(0)).getClasses().get(0);
+    }
+
+
+    /**
+     * Convenience routine to get the named ClassNode.
+     */
+    public ClassNode getClassNode(final String name) {
+        final ClassNode[] result = new ClassNode[]{null};
+        PrimaryClassNodeOperation handler = new PrimaryClassNodeOperation() {
+            public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
+                if (classNode.getName().equals(name)) {
+                    result[0] = classNode;
+                }
+            }
+        };
+
+        try {
+            applyToPrimaryClassNodes(handler);
+        } catch (CompilationFailedException e) {
+            if (debug) e.printStackTrace();
+        }
+        return result[0];
+    }
+
+    //---------------------------------------------------------------------------
+    // SOURCE CREATION
+
+
+    /**
+     * Adds a set of file paths to the unit.
+     */
+    public void addSources(String[] paths) {
+        for (int i = 0; i < paths.length; i++) {
+            File file = new File(paths[i]);
+            addSource(file);
+        }
+    }
+
+
+    /**
+     * Adds a set of source files to the unit.
+     */
+    public void addSources(File[] files) {
+        for (int i = 0; i < files.length; i++) {
+            addSource(files[i]);
+        }
+    }
+
+
+    /**
+     * Adds a source file to the unit.
+     */
+    public SourceUnit addSource(File file) {
+        return addSource(new SourceUnit(file, configuration, classLoader, getErrorCollector()));
+    }
+
+    /**
+     * Adds a source file to the unit.
+     */
+    public SourceUnit addSource(URL url) {
+        return addSource(new SourceUnit(url, configuration, classLoader, getErrorCollector()));
+    }
+
+
+    /**
+     * Adds a InputStream source to the unit.
+     */
+    public SourceUnit addSource(String name, InputStream stream) {
+        ReaderSource source = new InputStreamReaderSource(stream, configuration);
+        return addSource(new SourceUnit(name, source, configuration, classLoader, getErrorCollector()));
+    }
+
+
+    /**
+     * Adds a SourceUnit to the unit.
+     */
+    public SourceUnit addSource(SourceUnit source) {
+        String name = source.getName();
+        source.setClassLoader(this.classLoader);
+        for (Iterator iter = queuedSources.iterator(); iter.hasNext();) {
+            SourceUnit su = (SourceUnit) iter.next();
+            if (name.equals(su.getName())) return su;
+        }
+        queuedSources.add(source);
+        return source;
+    }
+
+
+    /**
+     * Returns an iterator on the unit's SourceUnits.
+     */
+    public Iterator iterator() {
+        return new Iterator() {
+            Iterator nameIterator = names.iterator();
+
+
+            public boolean hasNext() {
+                return nameIterator.hasNext();
+            }
+
+
+            public Object next() {
+                String name = (String) nameIterator.next();
+                return sources.get(name);
+            }
+
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+
+    /**
+     * Adds a ClassNode directly to the unit (ie. without source).
+     * WARNING: the source is needed for error reporting, using
+     * this method without setting a SourceUnit will cause
+     * NullPinterExceptions
+     */
+    public void addClassNode(ClassNode node) {
+        ModuleNode module = new ModuleNode(this.ast);
+        this.ast.addModule(module);
+        module.addClass(node);
+    }
+
+    //---------------------------------------------------------------------------
+    // EXTERNAL CALLBACKS
+
+
+    /**
+     * A callback interface you can use to "accompany" the classgen()
+     * code as it traverses the ClassNode tree.  You will be called-back
+     * for each primary and inner class.  Use setClassgenCallback() before
+     * running compile() to set your callback.
+     */
+    public abstract static class ClassgenCallback {
+        public abstract void call(ClassVisitor writer, ClassNode node) throws CompilationFailedException;
+    }
+
+
+    /**
+     * Sets a ClassgenCallback.  You can have only one, and setting
+     * it to null removes any existing setting.
+     */
+    public void setClassgenCallback(ClassgenCallback visitor) {
+        this.classgenCallback = visitor;
+    }
+
+
+    /**
+     * A callback interface you can use to get a callback after every
+     * unit of the compile process.  You will be called-back with a
+     * ProcessingUnit and a phase indicator.  Use setProgressCallback()
+     * before running compile() to set your callback.
+     */
+    public abstract static class ProgressCallback {
+
+        public abstract void call(ProcessingUnit context, int phase) throws CompilationFailedException;
+    }
+
+    /**
+     * Sets a ProgressCallback.  You can have only one, and setting
+     * it to null removes any existing setting.
+     */
+    public void setProgressCallback(ProgressCallback callback) {
+        this.progressCallback = callback;
+    }
+
+    //---------------------------------------------------------------------------
+    // ACTIONS
+
+
+    /**
+     * Synonym for compile(Phases.ALL).
+     */
+    public void compile() throws CompilationFailedException {
+        compile(Phases.ALL);
+    }
+
+    /**
+     * Compiles the compilation unit from sources.
+     */
+    public void compile(int throughPhase) throws CompilationFailedException {
+        //
+        // To support delta compilations, we always restart
+        // the compiler.  The individual passes are responsible
+        // for not reprocessing old code.
+        gotoPhase(Phases.INITIALIZATION);
+        throughPhase = Math.min(throughPhase, Phases.ALL);
+
+        while (throughPhase >= phase && phase <= Phases.ALL) {
+
+            for (Iterator it = phaseOperations[phase].iterator(); it.hasNext();) {
+                Object operation = it.next();
+                if (operation instanceof PrimaryClassNodeOperation) {
+                    applyToPrimaryClassNodes((PrimaryClassNodeOperation) operation);
+                } else if (operation instanceof SourceUnitOperation) {
+                    applyToSourceUnits((SourceUnitOperation) operation);
+                } else {
+                    applyToGeneratedGroovyClasses((GroovyClassOperation) operation);
+                }
+            }
+
+            if (progressCallback != null) progressCallback.call(this, phase);
+            completePhase();
+            applyToSourceUnits(mark);
+
+            if (dequeued()) continue;
+
+            gotoPhase(phase + 1);
+
+            if (phase == Phases.CLASS_GENERATION) {
+                sortClasses();
+            }
+        }
+
+        errorCollector.failIfErrors();
+    }
+
+    private void sortClasses() throws CompilationFailedException {
+        Iterator modules = this.ast.getModules().iterator();
+        while (modules.hasNext()) {
+            ModuleNode module = (ModuleNode) modules.next();
+
+            // before we actually do the sorting we should check
+            // for cyclic references
+            List classes = module.getClasses();
+            for (Iterator iter = classes.iterator(); iter.hasNext();) {
+                ClassNode start = (ClassNode) iter.next();
+                ClassNode cn = start;
+                Set parents = new HashSet();
+                do {
+                    if (parents.contains(cn.getName())) {
+                        getErrorCollector().addErrorAndContinue(
+                                new SimpleMessage("cyclic inheritance involving " + cn.getName() + " in class " + start.getName(), this)
+                        );
+                        cn = null;
+                    } else {
+                        parents.add(cn.getName());
+                        cn = cn.getSuperClass();
+                    }
+                } while (cn != null);
+            }
+            errorCollector.failIfErrors();
+            module.sortClasses();
+
+        }
+    }
+
+
+    /**
+     * Dequeues any source units add through addSource and resets the compiler phase
+     * to initialization.
+     * <p/>
+     * Note: this does not mean a file is recompiled. If a SoucreUnit has already passed
+     * a phase it is skipped until a higher phase is reached.
+     *
+     * @return true if there was a queued source
+     * @throws CompilationFailedException
+     */
+    protected boolean dequeued() throws CompilationFailedException {
+        boolean dequeue = !queuedSources.isEmpty();
+        while (!queuedSources.isEmpty()) {
+            SourceUnit su = (SourceUnit) queuedSources.removeFirst();
+            String name = su.getName();
+            names.add(name);
+            sources.put(name, su);
+        }
+        if (dequeue) {
+            gotoPhase(Phases.INITIALIZATION);
+        }
+        return dequeue;
+    }
+
+    /**
+     * Resolves all types
+     */
+    private final SourceUnitOperation resolve = new SourceUnitOperation() {
+        public void call(SourceUnit source) throws CompilationFailedException {
+            List classes = source.ast.getClasses();
+            for (Iterator it = classes.iterator(); it.hasNext();) {
+                ClassNode node = (ClassNode) it.next();
+
+                VariableScopeVisitor scopeVisitor = new VariableScopeVisitor(source);
+                scopeVisitor.visitClass(node);
+
+                resolveVisitor.startResolving(node, source);
+
+                GenericsVisitor genericsVisitor = new GenericsVisitor(source);
+                genericsVisitor.visitClass(node);
+            }
+
+        }
+    };
+
+    private PrimaryClassNodeOperation staticImport = new PrimaryClassNodeOperation() {
+        public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
+            staticImportVisitor.visitClass(classNode, source);
+        }
+    };
+
+    /**
+     * Runs convert() on a single SourceUnit.
+     */
+    private SourceUnitOperation convert = new SourceUnitOperation() {
+        public void call(SourceUnit source) throws CompilationFailedException {
+            source.convert();
+            CompilationUnit.this.ast.addModule(source.getAST());
+
+
+            if (CompilationUnit.this.progressCallback != null) {
+                CompilationUnit.this.progressCallback.call(source, CompilationUnit.this.phase);
+            }
+        }
+    };
+
+    private GroovyClassOperation output = new GroovyClassOperation() {
+        public void call(GroovyClass gclass) throws CompilationFailedException {
+            boolean failures = false;
+            String name = gclass.getName().replace('.', File.separatorChar) + ".class";
+            File path = new File(configuration.getTargetDirectory(), name);
+
+            //
+            // Ensure the path is ready for the file
+            //
+            File directory = path.getParentFile();
+            if (directory != null && !directory.exists()) {
+                directory.mkdirs();
+            }
+
+            //
+            // Create the file and write out the data
+            //
+            byte[] bytes = gclass.getBytes();
+
+            FileOutputStream stream = null;
+            try {
+                stream = new FileOutputStream(path);
+                stream.write(bytes, 0, bytes.length);
+            } catch (IOException e) {
+                getErrorCollector().addError(Message.create(e.getMessage(), CompilationUnit.this));
+                failures = true;
+            } finally {
+                if (stream != null) {
+                    try {
+                        stream.close();
+                    } catch (Exception e) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+    };
+
+    /* checks if all needed classes are compiled before generating the bytecode */
+    private SourceUnitOperation compileCompleteCheck = new SourceUnitOperation() {
+        public void call(SourceUnit source) throws CompilationFailedException {
+            List classes = source.ast.getClasses();
+            for (Iterator it = classes.iterator(); it.hasNext();) {
+                ClassNode node = (ClassNode) it.next();
+                CompileUnit cu = node.getCompileUnit();
+                for (Iterator iter = cu.iterateClassNodeToCompile(); iter.hasNext();) {
+                    String name = (String) iter.next();
+                    SourceUnit su = ast.getScriptSourceLocation(name);
+                    List classesInSourceUnit = su.ast.getClasses();
+                    StringBuffer message = new StringBuffer();
+                    message
+                            .append("Compilation incomplete: expected to find the class ")
+                            .append(name)
+                            .append(" in ")
+                            .append(su.getName());
+                    if (classesInSourceUnit.isEmpty()) {
+                        message.append(", but the file seems not to contain any classes");
+                    } else {
+                        message.append(", but the file contains the classes: ");
+                        boolean first = true;
+                        for (Iterator suClassesIter = classesInSourceUnit
+                                .iterator(); suClassesIter.hasNext();) {
+                            ClassNode cn = (ClassNode) suClassesIter.next();
+                            if (!first) {
+                                message.append(", ");
+                            } else {
+                                first = false;
+                            }
+                            message.append(cn.getName());
+                        }
+                    }
+
+                    getErrorCollector().addErrorAndContinue(
+                            new SimpleMessage(message.toString(), CompilationUnit.this)
+                    );
+                    iter.remove();
+                }
+            }
+        }
+    };
+
+
+    /**
+     * Runs classgen() on a single ClassNode.
+     */
+    private PrimaryClassNodeOperation classgen = new PrimaryClassNodeOperation() {
+        public boolean needSortedInput() {
+            return true;
+        }
+
+        public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {
+
+            //
+            // Run the Verifier on the outer class
+            //
+            try {
+                verifier.visitClass(classNode);
+            } catch (GroovyRuntimeException rpe) {
+                ASTNode node = rpe.getNode();
+                getErrorCollector().addError(
+                        new SyntaxException(rpe.getMessage(), null, node.getLineNumber(), node.getColumnNumber()),
+                        source
+                );
+            }
+
+            LabelVerifier lv = new LabelVerifier(source);
+            lv.visitClass(classNode);
+
+            ClassCompletionVerifier completionVerifier = new ClassCompletionVerifier(source);
+            completionVerifier.visitClass(classNode);
+
+            ExtendedVerifier xverifier = new ExtendedVerifier(source);
+            xverifier.visitClass(classNode);
+
+            // because the class may be generated even if a error was found
+            // and that class may have an invalid format we fail here if needed
+            getErrorCollector().failIfErrors();
+
+            //
+            // Prep the generator machinery
+            //
+            ClassVisitor visitor = createClassVisitor();
+
+
+            String sourceName = (source == null ? classNode.getModule().getDescription() : source.getName());
+            // only show the file name and its extension like javac does in its stacktraces rather than the full path
+            // also takes care of both \ and / depending on the host compiling environment
+            if (sourceName != null)
+                sourceName = sourceName.substring(Math.max(sourceName.lastIndexOf('\\'), sourceName.lastIndexOf('/')) + 1);
+            ClassGenerator generator = new AsmClassGenerator(context, visitor, classLoader, sourceName);
+
+            //
+            // Run the generation and create the class (if required)
+            //
+            generator.visitClass(classNode);
+
+
+            byte[] bytes = ((ClassWriter) visitor).toByteArray();
+            generatedClasses.add(new GroovyClass(classNode.getName(), bytes));
+
+            //
+            // Handle any callback that's been set
+            //
+            if (CompilationUnit.this.classgenCallback != null) {
+                classgenCallback.call(visitor, classNode);
+            }
+
+            //
+            // Recurse for inner classes
+            //
+            LinkedList innerClasses = generator.getInnerClasses();
+            while (!innerClasses.isEmpty()) {
+                classgen.call(source, context, (ClassNode) innerClasses.removeFirst());
+            }
+        }
+    };
+
+
+    protected ClassVisitor createClassVisitor() {
+        return new ClassWriter(true);
+    }
+
+    //---------------------------------------------------------------------------
+    // PHASE HANDLING
+
+
+    /**
+     * Updates the phase marker on all sources.
+     */
+    protected void mark() throws CompilationFailedException {
+        applyToSourceUnits(mark);
+    }
+
+
+    /**
+     * Marks a single SourceUnit with the current phase,
+     * if it isn't already there yet.
+     */
+    private SourceUnitOperation mark = new SourceUnitOperation() {
+        public void call(SourceUnit source) throws CompilationFailedException {
+            if (source.phase < phase) {
+                source.gotoPhase(phase);
+            }
+
+
+            if (source.phase == phase && phaseComplete && !source.phaseComplete) {
+                source.completePhase();
+            }
+        }
+    };
+
+    //---------------------------------------------------------------------------
+    // LOOP SIMPLIFICATION FOR SourceUnit OPERATIONS
+
+
+    /**
+     * An callback interface for use in the applyToSourceUnits loop driver.
+     */
+    public abstract static class SourceUnitOperation {
+        public abstract void call(SourceUnit source) throws CompilationFailedException;
+    }
+
+
+    /**
+     * A loop driver for applying operations to all SourceUnits.
+     * Automatically skips units that have already been processed
+     * through the current phase.
+     */
+    public void applyToSourceUnits(SourceUnitOperation body) throws CompilationFailedException {
+        Iterator keys = names.iterator();
+        while (keys.hasNext()) {
+            String name = (String) keys.next();
+            SourceUnit source = (SourceUnit) sources.get(name);
+            if ((source.phase < phase) || (source.phase == phase && !source.phaseComplete)) {
+                try {
+                    body.call(source);
+                } catch (CompilationFailedException e) {
+                    throw e;
+                } catch (Exception e) {
+                    GroovyBugError gbe = new GroovyBugError(e);
+                    changeBugText(gbe, source);
+                    throw gbe;
+                } catch (GroovyBugError e) {
+                    changeBugText(e, source);
+                    throw e;
+                }
+            }
+        }
+
+
+        getErrorCollector().failIfErrors();
+    }
+
+    //---------------------------------------------------------------------------
+    // LOOP SIMPLIFICATION FOR PRIMARY ClassNode OPERATIONS
+
+
+    /**
+     * An callback interface for use in the applyToSourceUnits loop driver.
+     */
+    public abstract static class PrimaryClassNodeOperation {
+        public abstract void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException;
+
+        public boolean needSortedInput() {
+            return false;
+        }
+    }
+
+    public abstract static class GroovyClassOperation {
+        public abstract void call(GroovyClass gclass) throws CompilationFailedException;
+    }
+
+    private List getPrimaryClassNodes(boolean sort) {
+        List unsorted = new ArrayList();
+        Iterator modules = this.ast.getModules().iterator();
+        while (modules.hasNext()) {
+            ModuleNode module = (ModuleNode) modules.next();
+
+            Iterator classNodes = module.getClasses().iterator();
+            while (classNodes.hasNext()) {
+                ClassNode classNode = (ClassNode) classNodes.next();
+                unsorted.add(classNode);
+            }
+        }
+
+        if (sort == false) return unsorted;
+
+        int[] indexClass = new int[unsorted.size()];
+        int[] indexInterface = new int[unsorted.size()];
+        {
+            int i = 0;
+            for (Iterator iter = unsorted.iterator(); iter.hasNext(); i++) {
+                ClassNode node = (ClassNode) iter.next();
+                int count = 0;
+                ClassNode element = node;
+                while (element != null) {
+                    count++;
+                    element = element.getSuperClass();
+                }
+                if (node.isInterface()) {
+                    indexInterface[i] = count;
+                    indexClass[i] = -1;
+                } else {
+                    indexClass[i] = count;
+                    indexInterface[i] = -1;
+                }
+            }
+        }
+
+        List sorted = getSorted(indexInterface, unsorted);
+        sorted.addAll(getSorted(indexClass, unsorted));
+
+        return sorted;
+    }
+
+    private List getSorted(int[] index, List unsorted) {
+        List sorted = new ArrayList(unsorted.size());
+        int start = 0;
+        for (int i = 0; i < unsorted.size(); i++) {
+            int min = -1;
+            for (int j = 0; j < unsorted.size(); j++) {
+                if (index[j] == -1) continue;
+                if (min == -1) {
+                    min = j;
+                } else if (index[j] < index[min]) {
+                    min = j;
+                }
+            }
+            if (min == -1) break;
+            sorted.add(unsorted.get(min));
+            index[min] = -1;
+        }
+        return sorted;
+    }
+
+    /**
+     * A loop driver for applying operations to all primary ClassNodes in
+     * our AST.  Automatically skips units that have already been processed
+     * through the current phase.
+     */
+    public void applyToPrimaryClassNodes(PrimaryClassNodeOperation body) throws CompilationFailedException {
+        Iterator classNodes = getPrimaryClassNodes(body.needSortedInput()).iterator();
+        while (classNodes.hasNext()) {
+            SourceUnit context = null;
+            try {
+                ClassNode classNode = (ClassNode) classNodes.next();
+                context = classNode.getModule().getContext();
+                if (context == null || context.phase <= phase) {
+                    body.call(context, new GeneratorContext(this.ast), classNode);
+                }
+            } catch (CompilationFailedException e) {
+                // fall thorugh, getErrorREporter().failIfErrors() will triger
+            } catch (NullPointerException npe) {
+                throw npe;
+            } catch (GroovyBugError e) {
+                changeBugText(e, context);
+                throw e;
+            } catch (Exception e) {
+                // check the exception for a nested compilation exception
+                ErrorCollector nestedCollector = null;
+                for (Throwable next = e.getCause(); next != e && next != null; next = next.getCause()) {
+                    if (!(next instanceof MultipleCompilationErrorsException)) continue;
+                    MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) next;
+                    nestedCollector = mcee.collector;
+                    break;
+                }
+
+                if (nestedCollector != null) {
+                    getErrorCollector().addCollectorContents(nestedCollector);
+                } else {
+                    getErrorCollector().addError(new ExceptionMessage(e, configuration.getDebug(), this));
+                }
+            }
+        }
+
+        getErrorCollector().failIfErrors();
+    }
+
+    public void applyToGeneratedGroovyClasses(GroovyClassOperation body) throws CompilationFailedException {
+        if (this.phase != Phases.OUTPUT && !(this.phase == Phases.CLASS_GENERATION && this.phaseComplete)) {
+            throw new GroovyBugError("CompilationUnit not ready for output(). Current phase=" + getPhaseDescription());
+        }
+
+        boolean failures = false;
+
+        Iterator iterator = this.generatedClasses.iterator();
+        while (iterator.hasNext()) {
+            //
+            // Get the class and calculate its filesystem name
+            //
+            GroovyClass gclass = (GroovyClass) iterator.next();
+            try {
+                body.call(gclass);
+            } catch (CompilationFailedException e) {
+                // fall thorugh, getErrorREporter().failIfErrors() will triger
+            } catch (NullPointerException npe) {
+                throw npe;
+            } catch (GroovyBugError e) {
+                changeBugText(e, null);
+                throw e;
+            } catch (Exception e) {
+                GroovyBugError gbe = new GroovyBugError(e);
+                throw gbe;
+            }
+        }
+
+        getErrorCollector().failIfErrors();
+    }
+
+    private void changeBugText(GroovyBugError e, SourceUnit context) {
+        e.setBugText("exception in phase '" + getPhaseDescription() + "' in source unit '" + ((context != null) ? context.getName() : "?") + "' " + e.getBugText());
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/CompilerConfiguration.java b/groovy/src/main/org/codehaus/groovy/control/CompilerConfiguration.java
new file mode 100644
index 0000000..37da33e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/CompilerConfiguration.java
@@ -0,0 +1,662 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control;
+
+import org.codehaus.groovy.control.io.NullWriter;
+import org.codehaus.groovy.control.messages.WarningMessage;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+
+/**
+ * Compilation control flags and coordination stuff.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @author <a href="mailto:jim@pagesmiths.com">Jim White</a>
+ * @version $Id$
+ */
+
+public class CompilerConfiguration {
+
+    private static final String JDK5_CLASSNAME_CHECK = "java.lang.annotation.Annotation";
+
+    /** This (<code>"1.5"</code>) is the value for targetBytecode to compile for a JDK 1.5 or later JVM. **/
+    public static final String POST_JDK5 = "1.5";
+    
+    /** This (<code>"1.4"<code/>) is the value for targetBytecode to compile for a JDK 1.4 JVM. **/
+    public static final String PRE_JDK5 = "1.4";
+
+    // Just call getVMVersion() once.
+    public static final String currentJVMVersion = getVMVersion();
+
+    // Static initializers are executed in text order,
+    // therefore we must do this one last!
+    /**
+     *  A convenience for getting a default configuration.  Do not modify it!
+     *  See {@link #CompilerConfiguration(Properties)} for an example on how to
+     *  make a suitable copy to modify.  But if you're really starting from a
+     *  default context, then you probably just want <code>new CompilerConfiguration()</code>. 
+     */
+    public static final CompilerConfiguration DEFAULT = new CompilerConfiguration();
+    
+    /**
+     * See {@link WarningMessage} for levels.
+     */
+    private int warningLevel;
+    /**
+     * Encoding for source files
+     */
+    private String sourceEncoding;
+    /**
+     * A <code>PrintWriter</code> for communicating with the user
+     */
+    private PrintWriter output;
+    /**
+     * Directory into which to write classes
+     */
+    private File targetDirectory;
+    /**
+     * Classpath for use during compilation
+     */
+    private LinkedList classpath;
+    /**
+     * If true, the compiler should produce action information
+     */
+    private boolean verbose;
+    /**
+     * If true, debugging code should be activated
+     */
+    private boolean debug;
+    /**
+     * The number of non-fatal errors to allow before bailing
+     */
+    private int tolerance;
+    /**
+     * Base class name for scripts (must derive from Script)
+     */
+    private String scriptBaseClass;
+
+    private ParserPluginFactory pluginFactory;
+
+    /**
+     * extension used to find a groovy file
+     */
+    private String defaultScriptExtension;
+    
+    /**
+     * if set to true recompilation is enabled
+     */
+    private boolean recompileGroovySource;
+    
+    /**
+     * sets the minimum of time after a script can be recompiled.
+     */
+    private int minimumRecompilationInterval;
+
+    /**
+     * sets the bytecode version target
+     */
+    private String targetBytecode;
+
+    /**
+     * options for joint compilation (null by default == no joint compilation)
+     */
+    private Map jointCompilationOptions;
+    
+    /**
+     * Sets the Flags to defaults.
+     */
+    public CompilerConfiguration() {
+        //
+        // Set in safe defaults
+
+        setWarningLevel(WarningMessage.LIKELY_ERRORS);
+        setOutput(null);
+        setTargetDirectory((File) null);
+        setClasspath("");
+        setVerbose(false);
+        setDebug(false);
+        setTolerance(10);
+        setScriptBaseClass(null);
+        setRecompileGroovySource(false);
+        setMinimumRecompilationInterval(100);
+        setTargetBytecode(getVMVersion());
+        setDefaultScriptExtension(".groovy");
+
+        //
+        // Source file encoding
+        String encoding = null;
+        try {
+            encoding = System.getProperty("file.encoding", "US-ASCII");
+        } catch (Exception e) {
+            // IGNORE
+        }
+        try {
+            encoding = System.getProperty("groovy.source.encoding", encoding);
+        } catch (Exception e) {
+            // IGNORE
+        }
+        setSourceEncoding(encoding);
+
+        try {
+            setOutput(new PrintWriter(System.err));
+        } catch (Exception e) {
+            // IGNORE
+        }
+
+        try {
+            String target = System.getProperty("groovy.target.directory");
+            if (target != null) {
+                setTargetDirectory(target);
+            }
+        } catch (Exception e) {
+            // IGNORE
+        }
+    }
+    
+    /**
+     * Copy constructor.  Use this if you have a mostly correct configuration
+     * for your compilation but you want to make a some changes programmatically.  
+     * An important reason to prefer this approach is that your code will most
+     * likely be forward compatible with future changes to this configuration API.<br/>
+     * An example of this copy constructor at work:<br/>
+     * <pre>
+     *    // In all likelihood there is already a configuration in your code's context
+     *    // for you to copy, but for the sake of this example we'll use the global default.
+     *    CompilerConfiguration myConfiguration = new CompilerConfiguration(CompilerConfiguration.DEFAULT);
+     *    myConfiguration.setDebug(true);
+     *</pre>
+     * @param configuration The configuration to copy.
+     */
+    public CompilerConfiguration(CompilerConfiguration configuration) {
+        setWarningLevel(configuration.getWarningLevel());
+        setOutput(configuration.getOutput());
+        setTargetDirectory(configuration.getTargetDirectory());
+        setClasspathList(new LinkedList(configuration.getClasspath()));
+        setVerbose(configuration.getVerbose());
+        setDebug(configuration.getDebug());
+        setTolerance(configuration.getTolerance());
+        setScriptBaseClass(configuration.getScriptBaseClass());
+        setRecompileGroovySource(configuration.getRecompileGroovySource());
+        setMinimumRecompilationInterval(configuration.getMinimumRecompilationInterval());
+        setTargetBytecode(configuration.getTargetBytecode());
+        setDefaultScriptExtension(configuration.getDefaultScriptExtension());
+        setSourceEncoding(configuration.getSourceEncoding());
+        setOutput(configuration.getOutput());
+        setTargetDirectory(configuration.getTargetDirectory());
+        Map jointCompilationOptions = configuration.getJointCompilationOptions();
+        if (jointCompilationOptions!=null) {
+            jointCompilationOptions = new HashMap(jointCompilationOptions);
+        }
+        setJointCompilationOptions(jointCompilationOptions);
+        setPluginFactory(configuration.getPluginFactory());
+    }
+
+
+    /**
+     * Sets the Flags to the specified configuration, with defaults
+     * for those not supplied.
+     * Note that those "defaults" here do <em>not</em> include checking the
+     * settings in {@link System#getProperties()} in general, only file.encoding, 
+     * groovy.target.directory and groovy.source.encoding are.<br/>
+     * If you want to set a few flags but keep Groovy's default
+     * configuration behavior then be sure to make your settings in
+     * a Properties that is backed by <code>System.getProperties()</code> (which
+     * is done using the {@link #CompilerConfiguration(Properties)} constructor).<br/>
+     *   That might be done like this:<br/>
+     * <pre>
+     *    Properties myProperties = new Properties(System.getProperties());
+     *    myProperties.setProperty("groovy.output.debug", "true");
+     *    myConfiguration = new CompilerConfiguration(myProperties);
+     * </pre>
+     * And you also have to contend with a possible SecurityException when
+     * getting the system properties (See {@link java.lang.System#getProperties()}).<br/> 
+     * An safer method would be to copy a default
+     * CompilerConfiguration and make your changes there using the
+     * setter.<br/>
+     * <pre>
+     *    // In all likelihood there is already a configuration for you to copy,
+     *    // but for the sake of this example we'll use the global default.
+     *    CompilerConfiguration myConfiguration = new CompilerConfiguration(CompilerConfiguration.DEFAULT);
+     *    myConfiguration.setDebug(true);
+     * </pre>
+     * Another reason to use the copy constructor rather than this one is that you
+     * must call {@link #setOutput}.  Calling <code>setOutput(null)</code> is valid and will
+     * set up a <code>PrintWriter</code> to a bit bucket.  The copy constructor will of course set
+     * the same one as the original.
+     *
+     *<table summary="Groovy Compiler Configuration Properties">
+         <tr>
+            <th>Property Key</th><th>Get/Set Property Name</th>
+         </tr>
+            <tr>
+            <td><code>"groovy.warnings"</code></td><td>{@link #getWarningLevel}</td></tr>
+            <tr><td><code>"groovy.source.encoding"</code></td><td>{@link #getSourceEncoding}</td></tr>
+            <tr><td><code>"groovy.target.directory"</code></td><td>{@link #getTargetDirectory}</td></tr>
+            <tr><td><code>"groovy.target.bytecode"</code></td><td>{@link #getTargetBytecode}</td></tr>
+            <tr><td><code>"groovy.classpath"</code></td><td>{@link #getClasspath}</td></tr>
+            <tr><td><code>"groovy.output.verbose"</code></td><td>{@link #getVerbose}</td></tr>
+            <tr><td><code>"groovy.output.debug"</code></td><td>{@link #getDebug}</td></tr>
+            <tr><td><code>"groovy.errors.tolerance"</code></td><td>{@link #getTolerance}</td></tr>
+            <tr><td><code>"groovy.script.extension"</code></td><td>{@link #getDefaultScriptExtension}</td></tr>
+            <tr><td><code>"groovy.script.base"</code></td><td>{@link #getScriptBaseClass}</td></tr>
+            <tr><td><code>"groovy.recompile"</code></td><td>{@link #getRecompileGroovySource}</td></tr>
+            <tr><td><code>"groovy.recompile.minimumInterval"</code></td><td>{@link #getMinimumRecompilationInterval}</td></tr>
+            <tr><td>
+         </tr>
+     </table>
+     <br/>
+     * @param configuration The properties to get flag values from.
+     */
+    public CompilerConfiguration(Properties configuration) throws ConfigurationException {
+        this();
+        configure(configuration);
+    }
+    
+    /**
+     * Method to configure a this CompilerConfiguration by using Properties.
+     * For a list of available properties look at {link {@link #CompilerConfiguration(Properties)}.
+     * @param configuration The properties to get flag values from.
+     */
+    public void configure(Properties configuration) throws ConfigurationException {
+        String text = null;
+        int numeric = 0;
+
+        //
+        // Warning level
+
+        numeric = getWarningLevel();
+        try {
+            text = configuration.getProperty("groovy.warnings", "likely errors");
+            numeric = Integer.parseInt(text);
+        } catch (NumberFormatException e) {
+            text = text.toLowerCase();
+            if (text.equals("none")) {
+                numeric = WarningMessage.NONE;
+            }
+            else if (text.startsWith("likely")) {
+                numeric = WarningMessage.LIKELY_ERRORS;
+            }
+            else if (text.startsWith("possible")) {
+                numeric = WarningMessage.POSSIBLE_ERRORS;
+            }
+            else if (text.startsWith("paranoia")) {
+                numeric = WarningMessage.PARANOIA;
+            }
+            else {
+                throw new ConfigurationException("unrecogized groovy.warnings: " + text);
+            }
+        }
+        setWarningLevel(numeric);
+
+        // 
+        // Source file encoding 
+        // 
+        text = configuration.getProperty("groovy.source.encoding");
+        if (text != null) setSourceEncoding(text);
+
+
+        //
+        // Target directory for classes
+        //
+        text = configuration.getProperty("groovy.target.directory");
+        if (text != null) setTargetDirectory(text);
+        
+        text = configuration.getProperty("groovy.target.bytecode");
+        if (text != null) setTargetBytecode(text);
+        
+        //
+        // Classpath
+        //
+        text = configuration.getProperty("groovy.classpath");
+        if (text != null) setClasspath(text);
+
+        //
+        // Verbosity
+        //
+        text = configuration.getProperty("groovy.output.verbose");
+        if (text != null && text.equalsIgnoreCase("true")) setVerbose(true);
+
+        //
+        // Debugging
+        //
+        text = configuration.getProperty("groovy.output.debug");
+        if (text != null && text.equalsIgnoreCase("true")) setDebug(true);
+
+        //
+        // Tolerance
+        // 
+        numeric = 10;
+        try {
+            text = configuration.getProperty("groovy.errors.tolerance", "10");
+            numeric = Integer.parseInt(text);
+        } catch (NumberFormatException e) {
+            throw new ConfigurationException(e);
+        }
+        setTolerance(numeric);
+
+
+        //
+        // Script Base Class
+        //
+        text = configuration.getProperty("groovy.script.base");
+        if (text!=null) setScriptBaseClass(text);
+        
+        //
+        // recompilation options
+        //
+        text = configuration.getProperty("groovy.recompile");
+        if (text != null) {
+            setRecompileGroovySource(text.equalsIgnoreCase("true"));
+        }
+        
+        numeric = 100;
+        try {
+            text = configuration.getProperty("groovy.recompile.minimumIntervall");
+            if (text==null) text = configuration.getProperty("groovy.recompile.minimumInterval");
+            if (text!=null) {
+                numeric = Integer.parseInt(text);
+            } else {
+                numeric = 100;
+            }
+        } catch (NumberFormatException e) {
+            throw new ConfigurationException(e);
+        }
+        setMinimumRecompilationInterval(numeric);
+        
+        
+    }
+
+
+    /**
+     * Gets the currently configured warning level.  See WarningMessage
+     * for level details.
+     */
+    public int getWarningLevel() {
+        return this.warningLevel;
+    }
+
+
+    /**
+     * Sets the warning level.  See WarningMessage for level details.
+     */
+    public void setWarningLevel(int level) {
+        if (level < WarningMessage.NONE || level > WarningMessage.PARANOIA) {
+            this.warningLevel = WarningMessage.LIKELY_ERRORS;
+        }
+        else {
+            this.warningLevel = level;
+        }
+    }
+
+
+    /**
+     * Gets the currently configured source file encoding.
+     */
+    public String getSourceEncoding() {
+        return this.sourceEncoding;
+    }
+
+
+    /**
+     * Sets the encoding to be used when reading source files.
+     */
+    public void setSourceEncoding(String encoding) {
+        if (encoding == null) encoding = "US-ASCII";
+        this.sourceEncoding = encoding;
+    }
+
+
+    /**
+     * Gets the currently configured output writer.
+     */
+    public PrintWriter getOutput() {
+        return this.output;
+    }
+
+
+    /**
+     * Sets the output writer.
+     */
+    public void setOutput(PrintWriter output) {
+        if (output == null) {
+            this.output = new PrintWriter(NullWriter.DEFAULT);
+        }
+        else {
+            this.output = output;
+        }
+    }
+
+
+    /**
+     * Gets the target directory for writing classes.
+     */
+    public File getTargetDirectory() {
+        return this.targetDirectory;
+    }
+
+
+    /**
+     * Sets the target directory.
+     */
+    public void setTargetDirectory(String directory) {
+        if (directory != null && directory.length() > 0) {
+            this.targetDirectory = new File(directory);
+        } else {
+            this.targetDirectory = null;
+        }
+    }
+
+
+    /**
+     * Sets the target directory.
+     */
+    public void setTargetDirectory(File directory) {
+        this.targetDirectory = directory;
+    }
+
+
+    /**
+     * Gets the classpath.
+     */
+    public List getClasspath() {
+        return this.classpath;
+    }
+
+
+    /**
+     * Sets the classpath.
+     */
+    public void setClasspath(String classpath) {
+        this.classpath = new LinkedList();
+
+        StringTokenizer tokenizer = new StringTokenizer(classpath, File.pathSeparator);
+        while (tokenizer.hasMoreTokens()) {
+            this.classpath.add(tokenizer.nextToken());
+        }
+    }
+    
+    /**
+     * sets the classpath using a list of Strings
+     * @param l list of strings containg the classpathparts
+     */
+    public void setClasspathList(List l) {
+        this.classpath = new LinkedList(l);
+    }
+
+
+    /**
+     * Returns true if verbose operation has been requested.
+     */
+    public boolean getVerbose() {
+        return this.verbose;
+    }
+
+
+    /**
+     * Turns verbose operation on or off.
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+
+    /**
+     * Returns true if debugging operation has been requested.
+     */
+    public boolean getDebug() {
+        return this.debug;
+    }
+
+
+    /**
+     * Turns debugging operation on or off.
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+
+    /**
+     * Returns the requested error tolerance.
+     */
+    public int getTolerance() {
+        return this.tolerance;
+    }
+
+
+    /**
+     * Sets the error tolerance, which is the number of
+     * non-fatal errors (per unit) that should be tolerated before
+     * compilation is aborted.
+     */
+    public void setTolerance(int tolerance) {
+        this.tolerance = tolerance;
+    }
+
+
+    /**
+     * Gets the name of the base class for scripts.  It must be a subclass
+     * of Script.
+     */
+    public String getScriptBaseClass() {
+        return this.scriptBaseClass;
+    }
+
+
+    /**
+     * Sets the name of the base class for scripts.  It must be a subclass
+     * of Script.
+     */
+    public void setScriptBaseClass(String scriptBaseClass) {
+        this.scriptBaseClass = scriptBaseClass;
+    }
+
+    public ParserPluginFactory getPluginFactory() {
+        if (pluginFactory == null) {
+            pluginFactory = ParserPluginFactory.newInstance(true);
+        }
+        return pluginFactory;
+    }
+
+    public void setPluginFactory(ParserPluginFactory pluginFactory) {
+        this.pluginFactory = pluginFactory;
+    }
+
+    public String getDefaultScriptExtension() {
+        return defaultScriptExtension;
+    }
+
+
+    public void setDefaultScriptExtension(String defaultScriptExtension) {
+        this.defaultScriptExtension = defaultScriptExtension;
+    }
+    
+    public void setRecompileGroovySource(boolean recompile) {
+        recompileGroovySource = recompile;
+    }
+    
+    public boolean getRecompileGroovySource(){
+        return recompileGroovySource;
+    }
+    
+    public void setMinimumRecompilationInterval(int time) {
+        minimumRecompilationInterval = Math.max(0,time);
+    }
+    
+    public int getMinimumRecompilationInterval() {
+        return minimumRecompilationInterval;
+    }
+
+    /**
+     * Allow setting the bytecode compatibility. The parameter can take
+     * one of the values <tt>1.5</tt> or <tt>1.4</tt>. If wrong parameter
+     * then the value will default to VM determined version.
+     * 
+     * @param version the bytecode compatibility mode
+     */
+    public void setTargetBytecode(String version) {
+        if(PRE_JDK5.equals(version) || POST_JDK5.equals(version)) {
+            this.targetBytecode = version;
+        }
+    }
+
+    /**
+     * Retrieves the compiler bytecode compatibility mode.
+     * 
+     * @return bytecode compatibity mode. Can be either <tt>1.5</tt> or <tt>1.4</tt>.
+     */
+    public String getTargetBytecode() {
+        return this.targetBytecode;
+    }
+    
+    private static String getVMVersion() {
+        try {
+            Class.forName(JDK5_CLASSNAME_CHECK);
+            return POST_JDK5;
+        } catch(Exception ex) {
+            // IGNORE
+        }
+        
+        return PRE_JDK5;
+    }
+    
+    /**
+     * Gets the joint compilation options for this configuration.
+     * @return the options
+     */
+    public Map getJointCompilationOptions() {
+        return jointCompilationOptions;
+    }
+    
+    /**
+     * Sets the joint compilation options for this configuration. 
+     * Using null will disable joint compilation.
+     * @param options the options
+     */
+    public void setJointCompilationOptions(Map options) {
+        jointCompilationOptions = options;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/ConfigurationException.java b/groovy/src/main/org/codehaus/groovy/control/ConfigurationException.java
new file mode 100644
index 0000000..0c9eee1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/ConfigurationException.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control;
+
+import org.codehaus.groovy.GroovyExceptionInterface;
+
+
+
+
+/**
+ *  Thrown when configuration data is invalid.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class ConfigurationException extends RuntimeException implements GroovyExceptionInterface
+{
+    
+  //---------------------------------------------------------------------------
+  // CONSTRUCTION AND SUCH
+
+    protected Exception cause;   // The phase in which the failures occurred
+
+    
+   /**
+    *  Initializes the exception from a cause exception.
+    */
+    
+    public ConfigurationException( Exception cause ) 
+    {
+        super( cause.getMessage() );
+        this.cause = cause;
+    }
+    
+    
+   /**
+    *  Initializes the exception with just a message.
+    */
+    
+    public ConfigurationException( String message )
+    {
+        super( message );
+    }
+
+    
+    
+   /**
+    *  Returns the causing exception, if available.
+    */
+    
+    public Throwable getCause()
+    {
+        return cause;
+    }
+    
+    
+   /**
+    *  Its always fatal.
+    */
+    
+    public boolean isFatal()
+    {
+        return true;
+    }
+    
+    
+    
+   /**
+    *  Set fatal is just ignored.
+    */
+    
+    public void setFatal( boolean fatal )
+    {
+    }
+    
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/ErrorCollector.java b/groovy/src/main/org/codehaus/groovy/control/ErrorCollector.java
new file mode 100644
index 0000000..1fa3d83
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/ErrorCollector.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.codehaus.groovy.control.messages.ExceptionMessage;
+import org.codehaus.groovy.control.messages.LocatedMessage;
+import org.codehaus.groovy.control.messages.Message;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.control.messages.WarningMessage;
+import org.codehaus.groovy.syntax.CSTNode;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+/**
+ * A base class for collecting messages and errors during processing.
+ * Each CompilationUnit should have one and SourceUnits should share
+ * their ErrorCollector with the CompilationUnit.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @version $Id$
+ */
+public class ErrorCollector {
+    
+    /**
+     * WarningMessages collected during processing
+     */
+    protected LinkedList warnings;
+    /**
+     * ErrorMessages collected during processing
+     */
+    protected LinkedList errors;
+    /**
+     * Configuration and other settings that control processing
+     */
+    protected CompilerConfiguration configuration;
+
+    /**
+     * Initialize the ErrorReporter.
+     */
+    public ErrorCollector(CompilerConfiguration configuration) {
+        this.warnings = null;
+        this.errors = null;
+        
+        this.configuration = configuration;
+    }
+    
+    public void addCollectorContents(ErrorCollector er) {
+        if (er.errors!=null) {
+            if (errors==null) {
+                errors = er.errors;
+            } else {
+                errors.addAll(er.errors);
+            }
+        }
+        if (er.warnings!=null) {
+            if (warnings==null) {
+                warnings = er.warnings;
+            } else {
+                warnings.addAll(er.warnings);
+            }            
+        }
+    }
+    
+    
+    
+    /**
+     * Adds an error to the message set, but don't fail.
+     */
+    public void addErrorAndContinue(Message message) {
+        if (this.errors == null) {
+            this.errors = new LinkedList();
+        }
+
+        this.errors.add(message);
+    }
+    
+    /**
+     * Adds a non-fatal error to the message set.
+     */
+    public void addError(Message message) throws CompilationFailedException {
+        addErrorAndContinue(message);
+
+        if (errors!=null && this.errors.size() >= configuration.getTolerance()) {
+            failIfErrors();
+        }
+    }
+    
+    /**
+     * Adds an optionally-fatal error to the message set.  Throws
+     * the unit as a PhaseFailedException, if the error is fatal.
+     */
+    public void addError(Message message, boolean fatal) throws CompilationFailedException {
+        if (fatal) {
+            addFatalError(message);
+        }
+        else {
+            addError(message);
+        }
+    }
+
+    
+    /**
+     * Convenience wrapper for addError().
+     */
+    public void addError(SyntaxException error, SourceUnit source) throws CompilationFailedException {
+        addError(Message.create(error, source), error.isFatal());
+    }
+
+
+    /**
+     * Convenience wrapper for addError().
+     */
+    public void addError(String text, CSTNode context, SourceUnit source) throws CompilationFailedException {
+        addError(new LocatedMessage(text, context, source));
+    }
+    
+    
+    /**
+     * Adds a fatal exception to the message set and throws
+     * the unit as a PhaseFailedException.
+     */
+    public void addFatalError(Message message) throws CompilationFailedException {
+        addError(message);
+        failIfErrors();
+    }
+
+
+    public void addException(Exception cause, SourceUnit source) throws CompilationFailedException {
+        addError(new ExceptionMessage(cause,configuration.getDebug(),source));
+        failIfErrors();
+    }
+
+    /**
+     * Returns true if there are any errors pending.
+     */
+    public boolean hasErrors() {
+        return this.errors != null;
+    }
+    
+    /**
+     * Returns true if there are any warnings pending.
+     */
+    public boolean hasWarnings() {
+        return this.warnings != null;
+    }
+    
+    /**
+     * Returns the list of warnings, or null if there are none.
+     */
+    public List getWarnings() {
+        return this.warnings;
+    }
+
+    /**
+     * Returns the list of errors, or null if there are none.
+     */
+    public List getErrors() {
+        return this.errors;
+    }
+
+    /**
+     * Returns the number of warnings.
+     */
+    public int getWarningCount() {
+        return ((this.warnings == null) ? 0 : this.warnings.size());
+    }
+
+    /**
+     * Returns the number of errors.
+     */
+    public int getErrorCount() {
+        return ((this.errors == null) ? 0 : this.errors.size());
+    }
+
+    /**
+     * Returns the specified warning message, or null.
+     */
+    public WarningMessage getWarning(int index) {
+        if (index < getWarningCount()) {
+            return (WarningMessage) this.warnings.get(index);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the specified error message, or null.
+     */
+    public Message getError(int index) {
+        if (index < getErrorCount()) {
+            return (Message) this.errors.get(index);
+        }
+        return null;
+    }
+
+    /**
+     * Returns the last error reported
+     */
+    public Message getLastError() {
+        return (Message) this.errors.getLast();
+    }
+    
+    /**
+     * Convenience routine to return the specified error's
+     * underlying SyntaxException, or null if it isn't one.
+     */
+    public SyntaxException getSyntaxError(int index) {
+        SyntaxException exception = null;
+
+        Message message = getError(index);
+        if (message != null && message instanceof SyntaxErrorMessage) {
+            exception = ((SyntaxErrorMessage) message).getCause();
+        }
+        return exception;
+    }
+
+    /**
+     * Convenience routine to return the specified error's
+     * underlying Exception, or null if it isn't one.
+     */
+    public Exception getException(int index) {
+        Exception exception = null;
+
+        Message message = getError(index);
+        if (message != null) {
+            if (message instanceof ExceptionMessage) {
+                exception = ((ExceptionMessage) message).getCause();
+            }
+            else if (message instanceof SyntaxErrorMessage) {
+                exception = ((SyntaxErrorMessage) message).getCause();
+            }
+        }
+        return exception;
+    }
+
+    /**
+     * Adds a WarningMessage to the message set.
+     */
+    public void addWarning(WarningMessage message) {
+        if (message.isRelevant(configuration.getWarningLevel())) {
+            if (this.warnings == null) {
+                this.warnings = new LinkedList();
+            }
+
+            this.warnings.add(message);
+        }
+    }
+
+
+    /**
+     * Convenience wrapper for addWarning() that won't create an object
+     * unless it is relevant.
+     */
+    public void addWarning(int importance, String text, CSTNode context, SourceUnit source) {
+        if (WarningMessage.isRelevant(importance, configuration.getWarningLevel())) {
+            addWarning(new WarningMessage(importance, text, context, source));
+        }
+    }
+    
+    
+    /**
+     * Convenience wrapper for addWarning() that won't create an object
+     * unless it is relevant.
+     */
+    public void addWarning(int importance, String text, Object data, CSTNode context, SourceUnit source) {
+        if (WarningMessage.isRelevant(importance, configuration.getWarningLevel())) {
+            addWarning(new WarningMessage(importance, text, data, context, source));
+        }
+    }
+   
+
+    /**
+     * Causes the current phase to fail by throwing a
+     * CompilationFailedException.
+     */
+    protected void failIfErrors() throws CompilationFailedException {
+        if (hasErrors()) {
+            throw new MultipleCompilationErrorsException(this);
+        }
+    }
+    
+    //---------------------------------------------------------------------------
+    // OUTPUT
+
+
+    private void write(PrintWriter writer, Janitor janitor, List messages, String txt) {
+        if (messages==null || messages.size()==0) return;
+        Iterator iterator = messages.iterator();
+        while (iterator.hasNext()) {
+            Message message = (Message) iterator.next();
+            message.write(writer, janitor);
+            
+            if (configuration.getDebug() && (message instanceof SyntaxErrorMessage)){
+                SyntaxErrorMessage sem = (SyntaxErrorMessage) message;
+                sem.getCause().printStackTrace(writer);
+            } 
+        }
+
+        writer.println();
+        writer.print(messages.size());
+        writer.print(" "+txt);
+        if (messages.size()>1) writer.print("s");
+        writer.println();
+    }
+    
+    /**
+     * Writes error messages to the specified PrintWriter.
+     */
+    public void write(PrintWriter writer, Janitor janitor) {
+        write(writer,janitor,warnings,"warning");
+        write(writer,janitor,errors,"error");
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/GenericsVisitor.java b/groovy/src/main/org/codehaus/groovy/control/GenericsVisitor.java
new file mode 100644
index 0000000..f8d38da
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/GenericsVisitor.java
@@ -0,0 +1,135 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.control;

+

+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;

+import org.codehaus.groovy.ast.ClassNode;

+import org.codehaus.groovy.ast.GenericsType;

+

+/**

+ * class used to verify correct usage of generics in 

+ * class header (class and superclass declaration) 

+ * @author Jochen Theodorou

+ */

+public class GenericsVisitor extends ClassCodeVisitorSupport {

+    private SourceUnit source;

+    

+    public GenericsVisitor(SourceUnit source) {

+        this.source = source;

+    }

+    

+    protected SourceUnit getSourceUnit() {

+        return source;

+    }

+    

+    public void visitClass(ClassNode node) {

+        boolean error=checkWildcard(node);

+        if (error) return;

+        checkGenericsUsage(node.getUnresolvedSuperClass(false), node.getSuperClass());

+        ClassNode[] interfaces = node.getInterfaces();

+        for (int i = 0; i < interfaces.length; i++) {

+            checkGenericsUsage(interfaces[i], interfaces[i].redirect());

+        }

+    }

+    

+    private boolean checkWildcard(ClassNode cn) {

+        ClassNode sn = cn.getUnresolvedSuperClass(false);

+        if (sn==null) return false;

+        GenericsType[] generics = sn.getGenericsTypes();

+        if (generics==null) return false;

+        boolean error=false;

+        for (int i = 0; i < generics.length; i++) {

+            if(generics[i].isWildcard()) {

+                addError("A supertype may not specifiy a wildcard type",sn);

+                error = true;

+            }

+        }

+        return error;

+    }

+

+    private void checkGenericsUsage(ClassNode n, ClassNode cn) {

+        GenericsType[] nTypes = n.getGenericsTypes();

+        GenericsType[] cnTypes = cn.getGenericsTypes();

+        // raw type usage is always allowed

+        if (nTypes==null) return;

+        // parameterize a type by using all of the parameters only

+        if (cnTypes==null) {

+            addError( "The class "+n.getName()+" refers to the class "+

+                      cn.getName()+" and uses "+nTypes.length+

+                      " parameters, but the referred class takes no parameters", n);

+            return;

+        }

+        if (nTypes.length!=cnTypes.length) {

+            addError( "The class "+n.getName()+" refers to the class "+

+                      cn.getName()+" and uses "+nTypes.length+

+                      " parameters, but the refered class needs "+

+                      cnTypes.length, n);

+            return;

+        }

+        // check bounds

+        for (int i=0; i<nTypes.length; i++) {

+            ClassNode nType = nTypes[i].getType();

+            ClassNode cnType = cnTypes[i].getType();

+            if (!nType.isDerivedFrom(cnType)) {

+                if (cnType.isInterface() && nType.declaresInterface(cnType.getName())) continue;

+                addError("The type "+nTypes[i].getName()+

+                         " is not a valid substitute for the bounded parameter <"+

+                         getPrintName(cnTypes[i])+">",n);

+            }

+        }

+    }

+    

+    private String getPrintName(GenericsType gt) {

+        String ret = gt.getName();

+        ClassNode[] upperBounds = gt.getUpperBounds();

+        ClassNode lowerBound = gt.getLowerBound();

+        if (upperBounds!=null) {

+            ret += " extends ";

+            for (int i = 0; i < upperBounds.length; i++) {

+                ret += getPrintName(upperBounds[i]);

+                if (i+1<upperBounds.length) ret += " & ";

+            }

+        } else if (lowerBound!=null) {

+            ret += " super "+getPrintName(lowerBound);

+        }

+        return ret;

+

+    }

+    

+    private String getPrintName(ClassNode cn) {

+        String ret = cn.getName();

+        GenericsType[] gts = cn.getGenericsTypes();

+        if (gts!=null) {

+            ret += "<";

+            for (int i = 0; i < gts.length; i++) {

+                if (i!=0) ret+=",";

+                ret+=getPrintName(gts[i]);

+            } 

+            ret+=">";

+        }

+        return ret;

+    }

+    

+    private void checkBounds(ClassNode[] given, ClassNode[] restrictions) {

+        if (restrictions==null) return;

+        for (int i=0; i<given.length; i++) {

+            for (int j=0; j<restrictions.length; j++) {

+                if (! given[i].isDerivedFrom(restrictions[j])){}

+            }

+        }

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/control/HasCleanup.java b/groovy/src/main/org/codehaus/groovy/control/HasCleanup.java
new file mode 100644
index 0000000..5a8aacb
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/HasCleanup.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control;
+
+/**
+ *  An interface for things that need to be cleaned up after
+ *  operations complete.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public interface HasCleanup 
+{
+    void cleanup();
+}
+
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/Janitor.java b/groovy/src/main/org/codehaus/groovy/control/Janitor.java
new file mode 100644
index 0000000..e600387
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/Janitor.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ *  An agent that can be used to defer cleanup operations to 
+ *  a later time.  Users much implement the HasCleanup interface.  
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class Janitor implements HasCleanup
+{
+    private final Set pending = new HashSet();   // All objects pending cleanup
+    
+    public void register( HasCleanup object )
+    {
+        pending.add( object );
+    }
+    
+    public void cleanup()
+    {
+        Iterator iterator = pending.iterator();
+        while( iterator.hasNext() )
+        {
+            HasCleanup object = (HasCleanup)iterator.next();
+            
+            try { object.cleanup(); } catch( Exception e ) {
+                // Ignore
+            }
+        }
+        
+        pending.clear();
+    }
+}
+
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/LabelVerifier.java b/groovy/src/main/org/codehaus/groovy/control/LabelVerifier.java
new file mode 100644
index 0000000..98f56b9
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/LabelVerifier.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.stmt.BreakStatement;
+import org.codehaus.groovy.ast.stmt.ContinueStatement;
+import org.codehaus.groovy.ast.stmt.DoWhileStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.stmt.SwitchStatement;
+import org.codehaus.groovy.ast.stmt.WhileStatement;
+
+/**
+ * This class checks the handling of labels in the AST
+ * 
+ * @author Jochen Theodorou
+ */
+public class LabelVerifier extends ClassCodeVisitorSupport {
+
+    private SourceUnit source;
+    private LinkedList visitedLabels;
+    private LinkedList continueLabels;
+    private LinkedList breakLabels;
+    boolean inLoop=false;
+    boolean inSwitch=false;
+    
+    public LabelVerifier(SourceUnit src) {
+        source = src;
+    }
+    
+    protected SourceUnit getSourceUnit() {
+        return source;
+    }
+    
+    private void init(){
+        visitedLabels = new LinkedList();
+        continueLabels = new LinkedList();
+        breakLabels = new LinkedList();
+        inLoop=false;
+        inSwitch=false;
+    }
+    
+    protected void visitClassCodeContainer(Statement code) {
+        init();
+        super.visitClassCodeContainer(code);
+        assertNoLabelsMissed();
+    }
+    
+   public void visitStatement(Statement statement) {
+       String label = statement.getStatementLabel();
+       
+       if (label!=null) {
+           for (Iterator iter = breakLabels.iterator(); iter.hasNext();) {
+               BreakStatement element = (BreakStatement) iter.next();
+               if (element.getLabel().equals(label)) iter.remove();
+           }
+           
+           for (Iterator iter = continueLabels.iterator(); iter.hasNext();) {
+               ContinueStatement element = (ContinueStatement) iter.next();
+               if (element.getLabel().equals(label)) iter.remove();
+           }
+           
+           visitedLabels.add(label);
+       }
+       
+       super.visitStatement(statement);
+}
+    
+    public void visitForLoop(ForStatement forLoop) {
+        boolean oldInLoop = inLoop;
+        inLoop = true;
+        super.visitForLoop(forLoop);
+        inLoop = oldInLoop;
+    }
+    
+    public void visitDoWhileLoop(DoWhileStatement loop) {
+        boolean oldInLoop = inLoop;
+        inLoop = true;
+        super.visitDoWhileLoop(loop);
+        inLoop = oldInLoop;
+    }     
+    
+    public void visitWhileLoop(WhileStatement loop) {
+        boolean oldInLoop = inLoop;
+        inLoop = true;
+        super.visitWhileLoop(loop);
+        inLoop = oldInLoop;
+    }
+    
+    public void visitBreakStatement(BreakStatement statement) {
+        String label = statement.getLabel();
+        boolean hasNamedLabel = label!=null;
+        if (!hasNamedLabel && !inLoop && !inSwitch) {
+            addError("the break statement is only allowed inside loops or switches",statement);
+        } else if (hasNamedLabel && !inLoop) {
+            addError("the break statement with named label is only allowed inside loops",statement);
+        }
+        if (label!=null) {
+            boolean found=false;
+            for (Iterator iter = visitedLabels.iterator(); iter.hasNext();) {
+                String element = (String) iter.next();
+                if (element.equals(label)) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) breakLabels.add(statement);
+        }
+        
+        super.visitBreakStatement(statement);
+    }
+    
+    public void visitContinueStatement(ContinueStatement statement) {
+        String label = statement.getLabel();
+        boolean hasNamedLabel = label!=null;
+        if (!hasNamedLabel && !inLoop) {
+            addError("the continue statement is only allowed inside loops",statement);
+        } 
+        if (label!=null) {
+            boolean found=false;
+            for (Iterator iter = visitedLabels.iterator(); iter.hasNext();) {
+                String element = (String) iter.next();
+                if (element.equals(label)) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) continueLabels.add(statement);
+        }
+        
+        super.visitContinueStatement(statement);
+    }
+    
+    protected void assertNoLabelsMissed() {
+        //TODO: report multiple missing labels of the same name only once
+        for (Iterator iter = continueLabels.iterator(); iter.hasNext();) {
+            ContinueStatement element = (ContinueStatement) iter.next();
+            addError("continue to missing label",element);
+        }
+        for (Iterator iter = breakLabels.iterator(); iter.hasNext();) {
+            BreakStatement element = (BreakStatement) iter.next();
+            addError("break to missing label",element);
+        }
+    }
+    
+    public void visitSwitch(SwitchStatement statement) {
+        inSwitch=true;
+        super.visitSwitch(statement);
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/MultipleCompilationErrorsException.java b/groovy/src/main/org/codehaus/groovy/control/MultipleCompilationErrorsException.java
new file mode 100644
index 0000000..65f73f9
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/MultipleCompilationErrorsException.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * @author Jochen Theodorou
+ */
+public class MultipleCompilationErrorsException extends
+        CompilationFailedException {
+    
+    protected ErrorCollector collector;
+    
+    public MultipleCompilationErrorsException(ErrorCollector ec) {
+        super(0, null);
+        if (ec == null) {
+            CompilerConfiguration config = super.getUnit() != null ?
+                super.getUnit().getConfiguration() :
+                new CompilerConfiguration();
+            collector = new ErrorCollector(config);
+        } else {
+            collector = ec;
+        }
+    }
+
+    public ErrorCollector getErrorCollector() {
+        return collector;
+    }
+    
+    public String getMessage() {
+        
+        StringWriter data = new StringWriter();
+        PrintWriter writer = new PrintWriter(data);
+        Janitor janitor = new Janitor();
+
+        try {
+            collector.write(writer, janitor);
+        }
+        finally {
+            janitor.cleanup();
+        }
+
+        return super.getMessage() + ", " + data.toString();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/ParserPlugin.java b/groovy/src/main/org/codehaus/groovy/control/ParserPlugin.java
new file mode 100644
index 0000000..54d762c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/ParserPlugin.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control;
+
+import org.codehaus.groovy.syntax.Reduction;
+import org.codehaus.groovy.syntax.ParserException;
+import org.codehaus.groovy.ast.ModuleNode;
+
+import java.io.Reader;
+
+/**
+ * A simple extension point to allow us to switch between the classic Groovy parser and the new Antlr based parser
+ * 
+ * @version $Revision$
+ */
+public interface ParserPlugin {
+
+    Reduction parseCST(SourceUnit sourceUnit, Reader reader) throws CompilationFailedException;
+
+    ModuleNode buildAST(SourceUnit sourceUnit, ClassLoader classLoader, Reduction cst) throws ParserException;
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/ParserPluginFactory.java b/groovy/src/main/org/codehaus/groovy/control/ParserPluginFactory.java
new file mode 100644
index 0000000..c0b75fa
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/ParserPluginFactory.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control;
+
+import org.codehaus.groovy.antlr.AntlrParserPluginFactory;
+
+/**
+ * A factory of parser plugin instances
+ *
+ * @version $Revision$
+ */
+public abstract class ParserPluginFactory {
+    public static ParserPluginFactory newInstance(boolean useNewParser) {
+        if (useNewParser) {
+            Class type = null;
+            String name = "org.codehaus.groovy.antlr.AntlrParserPluginFactory";
+            try {
+                type = Class.forName(name);
+            }
+            catch (ClassNotFoundException e) {
+                try {
+                    type = ParserPluginFactory.class.getClassLoader().loadClass(name);
+                }
+                catch (ClassNotFoundException e1) {
+                    ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
+                    if (contextClassLoader != null) {
+                        try {
+                            type = contextClassLoader.loadClass(name);
+                        }
+                        catch (ClassNotFoundException e2) {
+                            // ignore
+                        }
+                    }
+                }
+            }
+
+            if (type != null) {
+                try {
+                    return (ParserPluginFactory) type.newInstance();
+                }
+                catch (Exception e) {
+                    throw new RuntimeException("Could not create AntlrParserPluginFactory: " + e, e);
+                }
+            }
+            // can't find Antlr parser, so lets use the Classic one
+        }
+        return new AntlrParserPluginFactory();
+    }
+
+    public abstract ParserPlugin createParserPlugin();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/Phases.java b/groovy/src/main/org/codehaus/groovy/control/Phases.java
new file mode 100644
index 0000000..7c53a6d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/Phases.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control;
+
+
+
+
+/**
+ *  Compilation phase identifiers.  
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class Phases
+{
+    public static final int INITIALIZATION        = 1;   // Opening of files and such
+    public static final int PARSING               = 2;   // Lexing, parsing, and AST building
+    public static final int CONVERSION            = 3;   // CST to AST conversion
+    public static final int SEMANTIC_ANALYSIS     = 4;   // AST semantic analysis and elucidation
+    public static final int CANONICALIZATION      = 5;   // AST completion
+    public static final int INSTRUCTION_SELECTION = 6;   // Class generation, phase 1
+    public static final int CLASS_GENERATION      = 7;   // Class generation, phase 2
+    public static final int OUTPUT                = 8;   // Output of class to disk
+    public static final int FINALIZATION          = 9;   // Cleanup
+    public static final int ALL                   = 9;   // Synonym for full compilation
+    
+    public static String[] descriptions = {
+          "startup"
+        , "initialization"
+        , "parsing"
+        , "conversion"
+        , "semantic analysis"
+        , "canonicalization"
+        , "instruction selection"
+        , "class generation"
+        , "output"
+        , "cleanup"
+    };
+    
+    
+    
+   /**
+    *  Returns a description of the specified phase.
+    */
+    
+    public static String getDescription( int phase )
+    {
+        return descriptions[phase];
+    }
+    
+}
+
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/ProcessingUnit.java b/groovy/src/main/org/codehaus/groovy/control/ProcessingUnit.java
new file mode 100644
index 0000000..8abd463
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/ProcessingUnit.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control;
+
+import groovy.lang.GroovyClassLoader;
+
+/**
+ * A base class for data structures that can collect messages and errors
+ * during processing.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @version $Id$
+ */
+
+public abstract class ProcessingUnit {
+
+    /**
+     * The current phase
+     */
+    protected int phase;
+    /**
+     * Set true if phase is finished
+     */
+    protected boolean phaseComplete;
+
+    /**
+     * Configuration and other settings that control processing
+     */
+    protected CompilerConfiguration configuration;
+  
+    /**
+     * The ClassLoader to use during processing
+     */
+    protected GroovyClassLoader classLoader;
+    
+    /**
+     * a helper to share errors and report them
+     */
+    protected ErrorCollector errorCollector;
+
+
+    /**
+     * Initialize the ProcessingUnit to the empty state.
+     */
+
+    public ProcessingUnit(CompilerConfiguration configuration, GroovyClassLoader classLoader, ErrorCollector er) {
+
+        this.phase = Phases.INITIALIZATION;
+        this.setClassLoader(classLoader);
+        configure((configuration == null ? new CompilerConfiguration() : configuration));
+        if (er==null) er = new ErrorCollector(getConfiguration());
+        this.errorCollector = er;
+    }
+
+
+    /**
+     * Reconfigures the ProcessingUnit.
+     */
+    public void configure(CompilerConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+
+    public CompilerConfiguration getConfiguration() {
+        return configuration;
+    }
+
+    public void setConfiguration(CompilerConfiguration configuration) {
+        this.configuration = configuration;
+    }
+
+    /**
+     * Returns the class loader in use by this ProcessingUnit.
+     */
+
+    public GroovyClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+
+    /**
+     * Sets the class loader for use by this ProcessingUnit.
+     */
+
+    public void setClassLoader(GroovyClassLoader loader) {
+        ClassLoader parent = Thread.currentThread().getContextClassLoader();
+        if (parent == null) parent = ProcessingUnit.class.getClassLoader();
+        this.classLoader = (loader == null ? new GroovyClassLoader(parent, configuration) : loader);
+    }
+
+
+    /**
+     * Returns the current phase.
+     */
+
+    public int getPhase() {
+        return this.phase;
+    }
+
+
+    /**
+     * Returns the description for the current phase.
+     */
+
+    public String getPhaseDescription() {
+        return Phases.getDescription(this.phase);
+    }
+
+    public ErrorCollector getErrorCollector() {
+        return errorCollector;
+    }
+    
+    //---------------------------------------------------------------------------
+    // PROCESSING
+
+
+    /**
+     * Marks the current phase complete and processes any
+     * errors.
+     */
+
+    public void completePhase() throws CompilationFailedException {       
+        errorCollector.failIfErrors();
+        phaseComplete = true;
+    }
+
+
+    /**
+     * A synonym for <code>gotoPhase( phase + 1 )</code>.
+     */
+    public void nextPhase() throws CompilationFailedException {
+        gotoPhase(this.phase + 1);
+    }
+
+
+    /**
+     * Wraps up any pending operations for the current phase
+     * and switches to the next phase.
+     */
+    public void gotoPhase(int phase) throws CompilationFailedException {
+        if (!this.phaseComplete) {
+            completePhase();
+        }
+
+        this.phase = phase;
+        this.phaseComplete = false;
+    }
+
+}
+
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/ResolveVisitor.java b/groovy/src/main/org/codehaus/groovy/control/ResolveVisitor.java
new file mode 100644
index 0000000..fc17618
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/ResolveVisitor.java
@@ -0,0 +1,847 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control;
+
+import groovy.lang.GroovyClassLoader;
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.CatchStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.classgen.Verifier;
+import org.codehaus.groovy.control.messages.ExceptionMessage;
+import org.codehaus.groovy.syntax.Types;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.*;
+
+/**
+ * Visitor to resolve Types and convert VariableExpression to
+ * ClassExpressions if needed. The ResolveVisitor will try to
+ * find the Class for a ClassExpression and prints an error if
+ * it fails to do so. Constructions like C[], foo as C, (C) foo
+ * will force creation of a ClassExpression for C
+ * <p/>
+ * Note: the method to start the resolving is  startResolving(ClassNode, SourceUnit).
+ *
+ * @author Jochen Theodorou
+ */
+public class ResolveVisitor extends ClassCodeExpressionTransformer {
+    private ClassNode currentClass;
+    // note: BigInteger and BigDecimal are also imported by default
+    public static final String[] DEFAULT_IMPORTS = {"java.lang.", "java.io.", "java.net.", "java.util.", "groovy.lang.", "groovy.util."};
+    private CompilationUnit compilationUnit;
+    private Map cachedClasses = new HashMap();
+    private static final Object NO_CLASS = new Object();
+    private static final Object SCRIPT = new Object();
+    private SourceUnit source;
+    private VariableScope currentScope;
+
+    private boolean isTopLevelProperty = true;
+    private boolean inPropertyExpression = false;
+    private boolean inClosure = false;
+    private boolean isSpecialConstructorCall = false;
+
+    private Map genericParameterNames = new HashMap();
+
+    public ResolveVisitor(CompilationUnit cu) {
+        compilationUnit = cu;
+    }
+
+    public void startResolving(ClassNode node, SourceUnit source) {
+        this.source = source;
+        visitClass(node);
+    }
+
+    protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+        VariableScope oldScope = currentScope;
+        currentScope = node.getVariableScope();
+        Map oldPNames = genericParameterNames;
+        genericParameterNames = new HashMap(genericParameterNames);
+
+        resolveGenericsHeader(node.getGenericsTypes());
+
+        Parameter[] paras = node.getParameters();
+        for (int i = 0; i < paras.length; i++) {
+            Parameter p = paras[i];
+            p.setInitialExpression(transform(p.getInitialExpression()));
+            resolveOrFail(p.getType(), p.getType());
+            visitAnnotations(p);
+        }
+        ClassNode[] exceptions = node.getExceptions();
+        for (int i = 0; i < exceptions.length; i++) {
+            ClassNode t = exceptions[i];
+            resolveOrFail(t, node);
+        }
+        resolveOrFail(node.getReturnType(), node);
+
+        super.visitConstructorOrMethod(node, isConstructor);
+
+        genericParameterNames = oldPNames;
+        currentScope = oldScope;
+    }
+
+    public void visitField(FieldNode node) {
+        ClassNode t = node.getType();
+        resolveOrFail(t, node);
+        super.visitField(node);
+    }
+
+    public void visitProperty(PropertyNode node) {
+        ClassNode t = node.getType();
+        resolveOrFail(t, node);
+        super.visitProperty(node);
+    }
+
+    private boolean resolveToInner (ClassNode type) {
+        String name = type.getName(), saved = name;
+        while (true) {
+            int len = name.lastIndexOf('.');
+            if (len == -1)
+              break;
+            name = name.substring(0,len) + "$" + name.substring(len+1);
+            type.setName(name);
+            if (resolve(type))
+              return true;
+        }
+        type.setName(saved);
+        return false;
+    }
+
+    private void resolveOrFail(ClassNode type, String msg, ASTNode node) {
+        if (resolve(type)) return;
+        if (resolveToInner(type)) return;
+        addError("unable to resolve class " + type.getName() + " " + msg, node);
+    }
+
+    private void resolveOrFail(ClassNode type, ASTNode node, boolean prefereImports) {
+        resolveGenericsTypes(type.getGenericsTypes());
+        if (prefereImports && resolveAliasFromModule(type)) return;
+        resolveOrFail(type, node);
+    }
+
+    private void resolveOrFail(ClassNode type, ASTNode node) {
+        resolveOrFail(type, "", node);
+    }
+
+    private boolean resolve(ClassNode type) {
+        return resolve(type, true, true, true);
+    }
+
+    private boolean resolve(ClassNode type, boolean testModuleImports, boolean testDefaultImports, boolean testStaticInnerClasses) {
+        if (type.isResolved() || type.isPrimaryClassNode()) return true;
+        resolveGenericsTypes(type.getGenericsTypes());
+        if (type.isArray()) {
+            ClassNode element = type.getComponentType();
+            boolean resolved = resolve(element, testModuleImports, testDefaultImports, testStaticInnerClasses);
+            if (resolved) {
+                ClassNode cn = element.makeArray();
+                type.setRedirect(cn);
+            }
+            return resolved;
+        }
+
+        // test if vanilla name is current class name
+        if (currentClass == type) return true;
+
+        if (genericParameterNames.get(type.getName()) != null) {
+            GenericsType gt = (GenericsType) genericParameterNames.get(type.getName());
+            type.setRedirect(gt.getType());
+            type.setGenericsTypes(new GenericsType[]{gt});
+            type.setGenericsPlaceHolder(true);
+            return true;
+        }
+
+        if (currentClass.getNameWithoutPackage().equals(type.getName())) {
+            type.setRedirect(currentClass);
+            return true;
+        }
+
+        return resolveFromModule(type, testModuleImports) ||
+                resolveFromCompileUnit(type) ||
+                resolveFromDefaultImports(type, testDefaultImports) ||
+                resolveFromStaticInnerClasses(type, testStaticInnerClasses) ||
+                resolveFromClassCache(type) ||
+                resolveToClass(type) ||
+                resolveToScript(type);
+
+    }
+
+    private boolean resolveFromClassCache(ClassNode type) {
+        String name = type.getName();
+        Object val = cachedClasses.get(name);
+        if (val == null || val == NO_CLASS) {
+            return false;
+        } else {
+            setClass(type, (Class) val);
+            return true;
+        }
+    }
+
+    // NOTE: copied from GroovyClassLoader
+    private long getTimeStamp(Class cls) {
+        return Verifier.getTimestamp(cls);
+    }
+
+    // NOTE: copied from GroovyClassLoader
+    private boolean isSourceNewer(URL source, Class cls) {
+        try {
+            long lastMod;
+
+            // Special handling for file:// protocol, as getLastModified() often reports
+            // incorrect results (-1)
+            if (source.getProtocol().equals("file")) {
+                // Coerce the file URL to a File
+                String path = source.getPath().replace('/', File.separatorChar).replace('|', ':');
+                File file = new File(path);
+                lastMod = file.lastModified();
+            } else {
+                URLConnection conn = source.openConnection();
+                lastMod = conn.getLastModified();
+                conn.getInputStream().close();
+            }
+            return lastMod > getTimeStamp(cls);
+        } catch (IOException e) {
+            // if the stream can't be opened, let's keep the old reference
+            return false;
+        }
+    }
+
+
+    private boolean resolveToScript(ClassNode type) {
+        String name = type.getName();
+        if (cachedClasses.get(name) == NO_CLASS) return false;
+        if (cachedClasses.get(name) == SCRIPT) cachedClasses.put(name, NO_CLASS);
+        if (name.startsWith("java.")) return type.isResolved();
+        //TODO: don't ignore inner static classes completely
+        if (name.indexOf('$') != -1) return type.isResolved();
+        ModuleNode module = currentClass.getModule();
+        if (module.hasPackageName() && name.indexOf('.') == -1) return type.isResolved();
+        // try to find a script from classpath
+        GroovyClassLoader gcl = compilationUnit.getClassLoader();
+        URL url = null;
+        try {
+            url = gcl.getResourceLoader().loadGroovySource(name);
+        } catch (MalformedURLException e) {
+            // fall through and let the URL be null
+        }
+        if (url != null) {
+            if (type.isResolved()) {
+                Class cls = type.getTypeClass();
+                // if the file is not newer we don't want to recompile
+                if (!isSourceNewer(url, cls)) return true;
+                cachedClasses.remove(type.getName());
+                type.setRedirect(null);
+            }
+            SourceUnit su = compilationUnit.addSource(url);
+            currentClass.getCompileUnit().addClassNodeToCompile(type, su);
+            return true;
+        }
+        // type may be resolved through the classloader before
+        return type.isResolved();
+    }
+
+
+    private boolean resolveFromStaticInnerClasses(ClassNode type, boolean testStaticInnerClasses) {
+        // try to resolve a public static inner class' name
+        testStaticInnerClasses &= type.hasPackageName();
+        if (testStaticInnerClasses) {
+            String name = type.getName();
+            String replacedPointType = name;
+            int lastPoint = replacedPointType.lastIndexOf('.');
+            replacedPointType = new StringBuffer()
+                    .append(replacedPointType.substring(0, lastPoint))
+                    .append("$")
+                    .append(replacedPointType.substring(lastPoint + 1))
+                    .toString();
+            type.setName(replacedPointType);
+            if (resolve(type, false, true, true)) return true;
+            type.setName(name);
+        }
+        return false;
+    }
+
+    private boolean resolveFromDefaultImports(ClassNode type, boolean testDefaultImports) {
+        // test default imports
+        testDefaultImports &= !type.hasPackageName();
+        if (testDefaultImports) {
+            for (int i = 0, size = DEFAULT_IMPORTS.length; i < size; i++) {
+                String packagePrefix = DEFAULT_IMPORTS[i];
+                String name = type.getName();
+                String fqn = packagePrefix + name;
+                type.setName(fqn);
+                if (resolve(type, false, false, false)) return true;
+                type.setName(name);
+            }
+            String name = type.getName();
+            if (name.equals("BigInteger")) {
+                type.setRedirect(ClassHelper.BigInteger_TYPE);
+                return true;
+            } else if (name.equals("BigDecimal")) {
+                type.setRedirect(ClassHelper.BigDecimal_TYPE);
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean resolveFromCompileUnit(ClassNode type) {
+        // look into the compile unit if there is a class with that name
+        CompileUnit compileUnit = currentClass.getCompileUnit();
+        if (compileUnit == null) return false;
+        ClassNode cuClass = compileUnit.getClass(type.getName());
+        if (cuClass != null) {
+            if (type != cuClass) type.setRedirect(cuClass);
+            return true;
+        }
+        return false;
+    }
+
+    private void setClass(ClassNode n, Class cls) {
+        ClassNode cn = ClassHelper.make(cls);
+        n.setRedirect(cn);
+    }
+
+    private void ambiguousClass(ClassNode type, ClassNode iType, String name, boolean resolved) {
+        if (resolved && !type.getName().equals(iType.getName())) {
+            addError("reference to " + name + " is ambiguous, both class " + type.getName() + " and " + iType.getName() + " match", type);
+        } else {
+            type.setRedirect(iType);
+        }
+    }
+
+    private boolean resolveAliasFromModule(ClassNode type) {
+        ModuleNode module = currentClass.getModule();
+        if (module == null) return false;
+        String name = type.getName();
+
+        // check module node imports aliases
+        // the while loop enables a check for inner classes which are not fully imported,
+        // but visible as the surrounding class is imported and the inner class is public/protected static
+        String pname = name;
+        int index = name.length();
+        /*
+         * we have a name foo.bar and an import foo.foo. This means foo.bar is possibly
+         * foo.foo.bar rather than foo.bar. This means to cut at the dot in foo.bar and
+         * foo for import
+         */
+        while (true) {
+            pname = name.substring(0, index);
+            ClassNode aliasedNode = module.getImport(pname);
+            if (aliasedNode != null) {
+                if (pname.length() == name.length()) {
+                    // full match, no need to create a new class
+                    type.setRedirect(aliasedNode);
+                    return true;
+                } else {
+                    //partial match
+                    String newName = aliasedNode.getName() + name.substring(pname.length());
+                    type.setName(newName);
+                    if (resolve(type, true, true, true)) return true;
+                    // was not resolved so it was a fake match
+                    type.setName(name);
+                }
+            }
+            index = pname.lastIndexOf('.');
+            if (index == -1) break;
+        }
+        return false;
+    }
+
+    private boolean resolveFromModule(ClassNode type, boolean testModuleImports) {
+        String name = type.getName();
+        ModuleNode module = currentClass.getModule();
+        if (module == null) return false;
+
+        if (!type.hasPackageName() && module.hasPackageName()) {
+            type.setName(module.getPackageName() + name);
+        }
+        // look into the module node if there is a class with that name
+        List moduleClasses = module.getClasses();
+        for (Iterator iter = moduleClasses.iterator(); iter.hasNext();) {
+            ClassNode mClass = (ClassNode) iter.next();
+            if (mClass.getName().equals(type.getName())) {
+                if (mClass != type) type.setRedirect(mClass);
+                return true;
+            }
+        }
+        type.setName(name);
+
+        if (testModuleImports) {
+            if (resolveAliasFromModule(type)) return true;
+
+            boolean resolved = false;
+            if (module.hasPackageName()) {
+                // check package this class is defined in
+                type.setName(module.getPackageName() + name);
+                resolved = resolve(type, false, false, false);
+            }
+            // check module node imports packages
+            List packages = module.getImportPackages();
+            ClassNode iType = ClassHelper.makeWithoutCaching(name);
+            for (Iterator iter = packages.iterator(); iter.hasNext();) {
+                String packagePrefix = (String) iter.next();
+                String fqn = packagePrefix + name;
+                iType.setName(fqn);
+                if (resolve(iType, false, false, true)) {
+                    ambiguousClass(type, iType, name, resolved);
+                    return true;
+                }
+                iType.setName(name);
+            }
+            if (!resolved) type.setName(name);
+            return resolved;
+        }
+        return false;
+    }
+
+    private boolean resolveToClass(ClassNode type) {
+        String name = type.getName();
+        if (cachedClasses.get(name) == NO_CLASS) return false;
+        if (currentClass.getModule().hasPackageName() && name.indexOf('.') == -1) return false;
+        GroovyClassLoader loader = compilationUnit.getClassLoader();
+        Class cls;
+        try {
+            // NOTE: it's important to do no lookup against script files
+            // here since the GroovyClassLoader would create a new CompilationUnit
+            cls = loader.loadClass(name, false, true);
+        } catch (ClassNotFoundException cnfe) {
+            cachedClasses.put(name, SCRIPT);
+            return false;
+        } catch (CompilationFailedException cfe) {
+            compilationUnit.getErrorCollector().addErrorAndContinue(new ExceptionMessage(cfe, true, source));
+            return false;
+        }
+        //TODO: the case of a NoClassDefFoundError needs a bit more research
+        // a simple recompilation is not possible it seems. The current class
+        // we are searching for is there, so we should mark that somehow.
+        // Basically the missing class needs to be completly compiled before
+        // we can again search for the current name.
+        /*catch (NoClassDefFoundError ncdfe) {
+            cachedClasses.put(name,SCRIPT);
+            return false;
+        }*/
+        if (cls == null) return false;
+        cachedClasses.put(name, cls);
+        setClass(type, cls);
+        //NOTE: we return false here even if we found a class,
+        //but we want to give a possible script a chance to recompile.
+        //this can only be done if the loader was not the instance
+        //defining the class.
+        return cls.getClassLoader() == loader;
+    }
+
+
+    public Expression transform(Expression exp) {
+        if (exp == null) return null;
+        if (exp instanceof VariableExpression) {
+            return transformVariableExpression((VariableExpression) exp);
+        } else if (exp.getClass() == PropertyExpression.class) {
+            return transformPropertyExpression((PropertyExpression) exp);
+        } else if (exp instanceof DeclarationExpression) {
+            return transformDeclarationExpression((DeclarationExpression) exp);
+        } else if (exp instanceof BinaryExpression) {
+            return transformBinaryExpression((BinaryExpression) exp);
+        } else if (exp instanceof MethodCallExpression) {
+            return transformMethodCallExpression((MethodCallExpression) exp);
+        } else if (exp instanceof ClosureExpression) {
+            return transformClosureExpression((ClosureExpression) exp);
+        } else if (exp instanceof ConstructorCallExpression) {
+            return transformConstructorCallExpression((ConstructorCallExpression) exp);
+        } else if (exp instanceof AnnotationConstantExpression) {
+            return transformAnnotationConstantExpression((AnnotationConstantExpression) exp);
+        } else {
+            resolveOrFail(exp.getType(), exp);
+            return exp.transformExpression(this);
+        }
+    }
+
+    private String lookupClassName(PropertyExpression pe) {
+        String name = "";
+        for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {
+            if (it instanceof VariableExpression) {
+                VariableExpression ve = (VariableExpression) it;
+                // stop at super and this
+                if (ve == VariableExpression.SUPER_EXPRESSION || ve == VariableExpression.THIS_EXPRESSION) {
+                    return null;
+                }
+                name = ve.getName() + "." + name;
+                break;
+            }
+            // anything other than PropertyExpressions, ClassExpression or
+            // VariableExpressions will stop resolving
+            else if (!(it.getClass() == PropertyExpression.class)) {
+                return null;
+            } else {
+                PropertyExpression current = (PropertyExpression) it;
+                String propertyPart = current.getPropertyAsString();
+                // the class property stops resolving, dynamic property names too
+                if (propertyPart == null || propertyPart.equals("class")) {
+                    return null;
+                }
+                name = propertyPart + "." + name;
+            }
+        }
+        if (name.length() > 0) return name.substring(0, name.length() - 1);
+        return null;
+    }
+
+    // iterate from the inner most to the outer and check for classes
+    // this check will ignore a .class property, for Example Integer.class will be
+    // a PropertyExpression with the ClassExpression of Integer as objectExpression
+    // and class as property
+    private Expression correctClassClassChain(PropertyExpression pe) {
+        LinkedList stack = new LinkedList();
+        ClassExpression found = null;
+        for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {
+            if (it instanceof ClassExpression) {
+                found = (ClassExpression) it;
+                break;
+            } else if (!(it.getClass() == PropertyExpression.class)) {
+                return pe;
+            }
+            stack.addFirst(it);
+        }
+        if (found == null) return pe;
+
+        if (stack.isEmpty()) return pe;
+        Object stackElement = stack.removeFirst();
+        if (!(stackElement.getClass() == PropertyExpression.class)) return pe;
+        PropertyExpression classPropertyExpression = (PropertyExpression) stackElement;
+        String propertyNamePart = classPropertyExpression.getPropertyAsString();
+        if (propertyNamePart == null || !propertyNamePart.equals("class")) return pe;
+
+        if (stack.isEmpty()) return found;
+        stackElement = stack.removeFirst();
+        if (!(stackElement.getClass() == PropertyExpression.class)) return pe;
+        PropertyExpression classPropertyExpressionContainer = (PropertyExpression) stackElement;
+
+        classPropertyExpressionContainer.setObjectExpression(found);
+        return pe;
+    }
+
+    protected Expression transformPropertyExpression(PropertyExpression pe) {
+        boolean itlp = isTopLevelProperty;
+        boolean ipe = inPropertyExpression;
+
+        Expression objectExpression = pe.getObjectExpression();
+        inPropertyExpression = true;
+        isTopLevelProperty = !(objectExpression.getClass() == PropertyExpression.class);
+        objectExpression = transform(objectExpression);
+        // we handle the property part as if it where not part of the property
+        inPropertyExpression = false;
+        Expression property = transform(pe.getProperty());
+        isTopLevelProperty = itlp;
+        inPropertyExpression = ipe;
+
+        boolean spreadSafe = pe.isSpreadSafe();
+        pe = new PropertyExpression(objectExpression, property, pe.isSafe());
+        pe.setSpreadSafe(spreadSafe);
+
+        String className = lookupClassName(pe);
+        if (className != null) {
+            ClassNode type = ClassHelper.make(className);
+            if (resolve(type)) return new ClassExpression(type);
+        }
+        if (objectExpression instanceof ClassExpression && pe.getPropertyAsString() != null) {
+            // possibly an inner class
+            ClassExpression ce = (ClassExpression) objectExpression;
+            ClassNode type = ClassHelper.make(ce.getType().getName() + "$" + pe.getPropertyAsString());
+            if (resolve(type, false, false, false)) return new ClassExpression(type);
+        }
+        Expression ret = pe;
+        if (isTopLevelProperty) ret = correctClassClassChain(pe);
+        return ret;
+    }
+
+    protected Expression transformVariableExpression(VariableExpression ve) {
+        if (ve.getName().equals("this")) return VariableExpression.THIS_EXPRESSION;
+        if (ve.getName().equals("super")) return VariableExpression.SUPER_EXPRESSION;
+        Variable v = ve.getAccessedVariable();
+        if (v instanceof DynamicVariable) {
+            ClassNode t = ClassHelper.make(ve.getName());
+            if (resolve(t)) {
+                // the name is a type so remove it from the scoping
+                // as it is only a classvariable, it is only in
+                // referencedClassVariables, but must be removed
+                // for each parentscope too
+                for (VariableScope scope = currentScope; scope != null && !scope.isRoot(); scope = scope.getParent()) {
+                    if (scope.isRoot()) break;
+                    if (scope.removeReferencedClassVariable(ve.getName()) == null) break;
+                }
+                ClassExpression ce = new ClassExpression(t);
+                ce.setSourcePosition(ve);
+                return ce;
+            }
+        }
+        resolveOrFail(ve.getType(), ve);
+        return ve;
+    }
+
+    protected Expression transformBinaryExpression(BinaryExpression be) {
+        Expression left = transform(be.getLeftExpression());
+        int type = be.getOperation().getType();
+        if ((type == Types.ASSIGNMENT_OPERATOR || type == Types.EQUAL) &&
+                left instanceof ClassExpression) {
+            ClassExpression ce = (ClassExpression) left;
+            String error = "you tried to assign a value to the class '" + ce.getType().getName() + "'";
+            if (ce.getType().isScript()) {
+                error += ". Do you have a script with this name?";
+            }
+            addError(error, be.getLeftExpression());
+            return be;
+        }
+        if (left instanceof ClassExpression && be.getRightExpression() instanceof ListExpression) {
+            // we have C[] if the list is empty -> should be an array then!
+            ListExpression list = (ListExpression) be.getRightExpression();
+            if (list.getExpressions().isEmpty()) {
+                return new ClassExpression(left.getType().makeArray());
+            }
+        }
+        Expression right = transform(be.getRightExpression());
+        be.setLeftExpression(left);
+        be.setRightExpression(right);
+        return be;
+    }
+
+    protected Expression transformClosureExpression(ClosureExpression ce) {
+        boolean oldInClosure = inClosure;
+        inClosure = true;
+        Parameter[] paras = ce.getParameters();
+        if (paras != null) {
+            for (int i = 0; i < paras.length; i++) {
+                ClassNode t = paras[i].getType();
+                resolveOrFail(t, ce);
+            }
+        }
+        Statement code = ce.getCode();
+        if (code != null) code.visit(this);
+        inClosure = oldInClosure;
+        return ce;
+    }
+
+    protected Expression transformConstructorCallExpression(ConstructorCallExpression cce) {
+        ClassNode type = cce.getType();
+        resolveOrFail(type, cce);
+        isSpecialConstructorCall = cce.isSpecialCall();
+        Expression ret = cce.transformExpression(this);
+        isSpecialConstructorCall = false;
+        return ret;
+    }
+
+    protected Expression transformMethodCallExpression(MethodCallExpression mce) {
+        Expression args = transform(mce.getArguments());
+        Expression method = transform(mce.getMethod());
+        Expression object = transform(mce.getObjectExpression());
+
+        MethodCallExpression result = new MethodCallExpression(object, method, args);
+        result.setSafe(mce.isSafe());
+        result.setImplicitThis(mce.isImplicitThis());
+        result.setSpreadSafe(mce.isSpreadSafe());
+        result.setSourcePosition(mce);
+        return result;
+    }
+
+    protected Expression transformDeclarationExpression(DeclarationExpression de) {
+        Expression oldLeft = de.getLeftExpression();
+        Expression left = transform(oldLeft);
+        if (left != oldLeft) {
+            ClassExpression ce = (ClassExpression) left;
+            addError("you tried to assign a value to " + ce.getType().getName(), oldLeft);
+            return de;
+        }
+        Expression right = transform(de.getRightExpression());
+        if (right == de.getRightExpression()) return de;
+        return new DeclarationExpression((VariableExpression) left, de.getOperation(), right);
+    }
+
+    protected Expression transformAnnotationConstantExpression(AnnotationConstantExpression ace) {
+        AnnotationNode an = (AnnotationNode) ace.getValue();
+        ClassNode type = an.getClassNode();
+        resolveOrFail(type, "unable to find class for annotation", an);
+        for (Iterator iter = an.getMembers().entrySet().iterator(); iter.hasNext();) {
+            Map.Entry member = (Map.Entry) iter.next();
+            Expression memberValue = (Expression) member.getValue();
+            member.setValue(transform(memberValue));
+        }
+
+        return ace;
+    }
+
+    public void visitAnnotations(AnnotatedNode node) {
+        Map annotionMap = node.getAnnotations();
+        if (annotionMap.isEmpty()) return;
+        Iterator it = annotionMap.values().iterator();
+        while (it.hasNext()) {
+            AnnotationNode an = (AnnotationNode) it.next();
+            //skip builtin properties
+            if (an.isBuiltIn()) continue;
+            ClassNode type = an.getClassNode();
+            resolveOrFail(type, "unable to find class for annotation", an);
+            for (Iterator iter = an.getMembers().entrySet().iterator(); iter.hasNext();) {
+                Map.Entry member = (Map.Entry) iter.next();
+                Expression memberValue = (Expression) member.getValue();
+                member.setValue(transform(memberValue));
+            }
+        }
+    }
+
+    public void visitClass(ClassNode node) {
+        ClassNode oldNode = currentClass;
+        currentClass = node;
+
+        resolveGenericsHeader(node.getGenericsTypes());
+
+        ModuleNode module = node.getModule();
+        if (!module.hasImportsResolved()) {
+            List l = module.getImports();
+            for (Iterator iter = l.iterator(); iter.hasNext();) {
+                ImportNode element = (ImportNode) iter.next();
+                ClassNode type = element.getType();
+                if (resolve(type, false, false, true)) continue;
+                addError("unable to resolve class " + type.getName(), type);
+            }
+            Map importPackages = module.getStaticImportClasses();
+            for (Iterator iter = importPackages.values().iterator(); iter.hasNext();) {
+                ClassNode type = (ClassNode) iter.next();
+                if (resolve(type, false, false, true)) continue;
+                addError("unable to resolve class " + type.getName(), type);
+            }
+            for (Iterator iter = module.getStaticImportAliases().values().iterator(); iter.hasNext();) {
+                ClassNode type = (ClassNode) iter.next();
+                if (resolve(type, true, true, true)) continue;
+                addError("unable to resolve class " + type.getName(), type);
+            }
+            for (Iterator iter = module.getStaticImportClasses().values().iterator(); iter.hasNext();) {
+                ClassNode type = (ClassNode) iter.next();
+                if (resolve(type, true, true, true)) continue;
+                addError("unable to resolve class " + type.getName(), type);
+            }
+            module.setImportsResolved(true);
+        }
+
+        ClassNode sn = node.getUnresolvedSuperClass();
+        if (sn != null) resolveOrFail(sn, node, true);
+
+        ClassNode[] interfaces = node.getInterfaces();
+        for (int i = 0; i < interfaces.length; i++) {
+            resolveOrFail(interfaces[i], node, true);
+
+        }
+
+        super.visitClass(node);
+
+        currentClass = oldNode;
+    }
+
+    public void visitCatchStatement(CatchStatement cs) {
+        resolveOrFail(cs.getExceptionType(), cs);
+        if (cs.getExceptionType() == ClassHelper.DYNAMIC_TYPE) {
+            cs.getVariable().setType(ClassHelper.make(Exception.class));
+        }
+        super.visitCatchStatement(cs);
+    }
+
+    public void visitForLoop(ForStatement forLoop) {
+        resolveOrFail(forLoop.getVariableType(), forLoop);
+        super.visitForLoop(forLoop);
+    }
+
+    public void visitBlockStatement(BlockStatement block) {
+        VariableScope oldScope = currentScope;
+        currentScope = block.getVariableScope();
+        super.visitBlockStatement(block);
+        currentScope = oldScope;
+    }
+
+    protected SourceUnit getSourceUnit() {
+        return source;
+    }
+
+    private void resolveGenericsTypes(GenericsType[] types) {
+        if (types == null) return;
+        currentClass.setUsingGenerics(true);
+        for (int i = 0; i < types.length; i++) {
+            resolveGenericsType(types[i]);
+        }
+    }
+
+    private void resolveGenericsHeader(GenericsType[] types) {
+        if (types == null) return;
+        currentClass.setUsingGenerics(true);
+        for (int i = 0; i < types.length; i++) {
+            ClassNode type = types[i].getType();
+            String name = type.getName();
+            ClassNode[] bounds = types[i].getUpperBounds();
+            if (bounds != null) {
+                boolean nameAdded = false;
+                for (int j = 0; j < bounds.length; j++) {
+                    ClassNode upperBound = bounds[j];
+                    if (!nameAdded && upperBound != null || !resolve(type)) {
+                        genericParameterNames.put(name, types[i]);
+                        types[i].setPlaceholder(true);
+                        type.setRedirect(upperBound);
+                        nameAdded = true;
+                    }
+                    resolveOrFail(upperBound, type);
+                }
+            } else {
+                genericParameterNames.put(name, types[i]);
+                type.setRedirect(ClassHelper.OBJECT_TYPE);
+                types[i].setPlaceholder(true);
+            }
+        }
+    }
+
+    private void resolveGenericsType(GenericsType genericsType) {
+        if (genericsType.isResolved()) return;
+        currentClass.setUsingGenerics(true);
+        ClassNode type = genericsType.getType();
+        // save name before redirect
+        String name = type.getName();
+        ClassNode[] bounds = genericsType.getUpperBounds();
+        if (!genericParameterNames.containsKey(name)) {
+            if (bounds != null) {
+                for (int j = 0; j < bounds.length; j++) {
+                    ClassNode upperBound = bounds[j];
+                    resolveOrFail(upperBound, genericsType);
+                    type.setRedirect(upperBound);
+                    resolveGenericsTypes(upperBound.getGenericsTypes());
+                }
+            } else if (genericsType.isWildcard()) {
+                type.setRedirect(ClassHelper.OBJECT_TYPE);
+            } else {
+                resolveOrFail(type, genericsType);
+            }
+        } else {
+            GenericsType gt = (GenericsType) genericParameterNames.get(name);
+            type.setRedirect(gt.getType());
+            genericsType.setPlaceholder(true);
+        }
+
+
+        if (genericsType.getLowerBound() != null) {
+            resolveOrFail(genericsType.getLowerBound(), genericsType);
+        }
+        resolveGenericsTypes(type.getGenericsTypes());
+        genericsType.setResolved(true);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/SourceUnit.java b/groovy/src/main/org/codehaus/groovy/control/SourceUnit.java
new file mode 100644
index 0000000..0e0e9d7
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/SourceUnit.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control;
+
+import groovy.lang.GroovyClassLoader;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+import org.codehaus.groovy.GroovyBugError;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.control.io.FileReaderSource;
+import org.codehaus.groovy.control.io.ReaderSource;
+import org.codehaus.groovy.control.io.StringReaderSource;
+import org.codehaus.groovy.control.io.URLReaderSource;
+import org.codehaus.groovy.control.messages.Message;
+import org.codehaus.groovy.control.messages.SimpleMessage;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessage;
+import org.codehaus.groovy.syntax.*;
+import org.codehaus.groovy.tools.Utilities;
+
+import antlr.CharScanner;
+import antlr.MismatchedTokenException;
+import antlr.NoViableAltException;
+import antlr.NoViableAltForCharException;
+
+import com.thoughtworks.xstream.XStream;
+
+
+/**
+ * Provides an anchor for a single source unit (usually a script file)
+ * as it passes through the compiler system.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
+ * @version $Id$
+ */
+
+public class SourceUnit extends ProcessingUnit {
+
+    /**
+     * The pluggable parser used to generate the AST - we allow pluggability currently as we need to have Classic and JSR support
+     */
+    private ParserPlugin parserPlugin;
+
+    /**
+     * Where we can get Readers for our source unit
+     */
+    protected ReaderSource source;
+    /**
+     * A descriptive name of the source unit. This name shouldn't
+     * be used for controling the SourceUnit, it is only for error
+     * messages
+     */
+    protected String name;
+    /**
+     * A Concrete Syntax Tree of the source
+     */
+    protected Reduction cst;
+
+    /**
+     * The root of the Abstract Syntax Tree for the source
+     */
+    protected ModuleNode ast;
+
+
+    /**
+     * Initializes the SourceUnit from existing machinery.
+     */
+    public SourceUnit(String name, ReaderSource source, CompilerConfiguration flags, GroovyClassLoader loader, ErrorCollector er) {
+        super(flags, loader, er);
+
+        this.name = name;
+        this.source = source;
+    }
+
+
+    /**
+     * Initializes the SourceUnit from the specified file.
+     */
+    public SourceUnit(File source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
+        this(source.getPath(), new FileReaderSource(source, configuration), configuration, loader, er);
+    }
+
+
+    /**
+     * Initializes the SourceUnit from the specified URL.
+     */
+    public SourceUnit(URL source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
+        this(source.getPath(), new URLReaderSource(source, configuration), configuration, loader, er);
+    }
+
+
+    /**
+     * Initializes the SourceUnit for a string of source.
+     */
+    public SourceUnit(String name, String source, CompilerConfiguration configuration, GroovyClassLoader loader, ErrorCollector er) {
+        this(name, new StringReaderSource(source, configuration), configuration, loader, er);
+    }
+
+
+    /**
+     * Returns the name for the SourceUnit. This name shouldn't
+     * be used for controling the SourceUnit, it is only for error
+     * messages
+     */
+    public String getName() {
+        return name;
+    }
+
+
+    /**
+     * Returns the Concrete Syntax Tree produced during parse()ing.
+     */
+    public Reduction getCST() {
+        return this.cst;
+    }
+
+    /**
+     * Returns the Abstract Syntax Tree produced during parse()ing
+     * and expanded during later phases.
+     */
+    public ModuleNode getAST() {
+        return this.ast;
+    }
+
+
+    /**
+     * Convenience routine, primarily for use by the InteractiveShell,
+     * that returns true if parse() failed with an unexpected EOF.
+     */
+    public boolean failedWithUnexpectedEOF() {
+    	// Implementation note - there are several ways for the Groovy compiler
+    	// to report an unexpected EOF. Perhaps this implementation misses some.
+    	// If you find another way, please add it.
+        if (getErrorCollector().hasErrors()) {
+            Message last = (Message) getErrorCollector().getLastError();
+            Throwable cause = null;
+            if (last instanceof SyntaxErrorMessage) {
+                cause = ((SyntaxErrorMessage) last).getCause().getCause();
+            }
+            if (cause != null) {
+            	if (cause instanceof NoViableAltException) {
+                    return isEofToken(((NoViableAltException) cause).token);
+            	} else if (cause instanceof NoViableAltForCharException) {
+            		char badChar = ((NoViableAltForCharException) cause).foundChar;
+            		return badChar == CharScanner.EOF_CHAR;
+                } else if (cause instanceof MismatchedTokenException) {
+                    return isEofToken(((MismatchedTokenException) cause).token);
+                }
+            }
+        }
+        return false;    
+    }
+
+    protected boolean isEofToken(antlr.Token token) {
+        return token.getType() == antlr.Token.EOF_TYPE;
+    }
+
+
+
+    //---------------------------------------------------------------------------
+    // FACTORIES
+
+
+    /**
+     * A convenience routine to create a standalone SourceUnit on a String
+     * with defaults for almost everything that is configurable.
+     */
+    public static SourceUnit create(String name, String source) {
+        CompilerConfiguration configuration = new CompilerConfiguration();
+        configuration.setTolerance(1);
+
+        return new SourceUnit(name, source, configuration, null, new ErrorCollector(configuration));
+    }
+
+
+    /**
+     * A convenience routine to create a standalone SourceUnit on a String
+     * with defaults for almost everything that is configurable.
+     */
+    public static SourceUnit create(String name, String source, int tolerance) {
+        CompilerConfiguration configuration = new CompilerConfiguration();
+        configuration.setTolerance(tolerance);
+
+        return new SourceUnit(name, source, configuration, null, new ErrorCollector(configuration));
+    }
+
+
+
+
+
+    //---------------------------------------------------------------------------
+    // PROCESSING
+
+
+    /**
+     * Parses the source to a CST.  You can retrieve it with getCST().
+     */
+    public void parse() throws CompilationFailedException {
+        if (this.phase > Phases.PARSING) {
+            throw new GroovyBugError("parsing is already complete");
+        }
+
+        if (this.phase == Phases.INITIALIZATION) {
+            nextPhase();
+        }
+
+
+        //
+        // Create a reader on the source and run the parser.
+
+        Reader reader = null;
+        try {
+            reader = source.getReader();
+
+            // lets recreate the parser each time as it tends to keep around state
+            parserPlugin = getConfiguration().getPluginFactory().createParserPlugin();
+
+            cst = parserPlugin.parseCST(this, reader);
+
+            reader.close();
+            
+        }
+        catch (IOException e) {
+            getErrorCollector().addFatalError(new SimpleMessage(e.getMessage(),this));
+        }
+        finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                }
+                catch (IOException e) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Generates an AST from the CST.  You can retrieve it with getAST().
+     */
+    public void convert() throws CompilationFailedException {
+        if (this.phase == Phases.PARSING && this.phaseComplete) {
+            gotoPhase(Phases.CONVERSION);
+        }
+
+        if (this.phase != Phases.CONVERSION) {
+            throw new GroovyBugError("SourceUnit not ready for convert()");
+        }
+
+        
+        //
+        // Build the AST
+        
+        try {
+            this.ast = parserPlugin.buildAST(this, this.classLoader, this.cst);
+
+            this.ast.setDescription(this.name);
+        }
+        catch (SyntaxException e) {
+            getErrorCollector().addError(new SyntaxErrorMessage(e,this));
+        }
+
+        String property = (String) AccessController.doPrivileged(new PrivilegedAction() {
+        	public Object run() {
+        		return System.getProperty("groovy.ast");
+        	}
+        });
+        
+        if ("xml".equals(property)) {
+            saveAsXML(name,ast);
+        }
+    }
+
+    private void saveAsXML(String name, ModuleNode ast) {
+        XStream xstream = new XStream();
+        try {
+            xstream.toXML(ast,new FileWriter(name + ".xml"));
+            System.out.println("Written AST to " + name + ".xml");
+        } catch (Exception e) {
+            System.out.println("Couldn't write to " + name + ".xml");
+            e.printStackTrace();
+        }
+    }
+    //---------------------------------------------------------------------------    // SOURCE SAMPLING
+
+    /**
+     * Returns a sampling of the source at the specified line and column,
+     * of null if it is unavailable.
+     */
+    public String getSample(int line, int column, Janitor janitor) {
+        String sample = null;
+        String text = source.getLine(line, janitor);
+
+        if (text != null) {
+            if (column > 0) {
+                String marker = Utilities.repeatString(" ", column - 1) + "^";
+
+                if (column > 40) {
+                    int start = column - 30 - 1;
+                    int end = (column + 10 > text.length() ? text.length() : column + 10 - 1);
+                    sample = "   " + text.substring(start, end) + Utilities.eol() + "   " + marker.substring(start, marker.length());
+                }
+                else {
+                    sample = "   " + text + Utilities.eol() + "   " + marker;
+                }
+            }
+            else {
+                sample = text;
+            }
+        }
+
+        return sample;
+    }
+    
+    public void addException(Exception e) throws CompilationFailedException {
+        getErrorCollector().addException(e,this);
+    }
+    
+    public void addError(SyntaxException se) throws CompilationFailedException {
+        getErrorCollector().addError(se,this);
+    }
+}
+
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/StaticImportVisitor.java b/groovy/src/main/org/codehaus/groovy/control/StaticImportVisitor.java
new file mode 100644
index 0000000..4326920
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/StaticImportVisitor.java
@@ -0,0 +1,260 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.control;

+

+import org.codehaus.groovy.ast.*;

+import org.codehaus.groovy.ast.expr.*;

+import org.codehaus.groovy.ast.stmt.Statement;

+

+import java.util.*;

+

+/**

+ * Visitor to resolve constants and method calls from static Imports

+ *

+ * @author Jochen Theodorou

+ * @author Paul King

+ */

+public class StaticImportVisitor extends ClassCodeExpressionTransformer {

+    private ClassNode currentClass;

+    private SourceUnit source;

+    private CompilationUnit compilationUnit;

+    private boolean stillResolving;

+    private boolean inSpecialConstructorCall;

+    private boolean inClosure;

+    private boolean inPropertyExpression;

+    private Expression foundConstant;

+    private Expression foundArgs;

+

+    public StaticImportVisitor(CompilationUnit cu) {

+        compilationUnit = cu;

+    }

+

+    public void visitClass(ClassNode node, SourceUnit source) {

+        this.currentClass = node;

+        this.source = source;

+        super.visitClass(node);

+    }

+

+    public Expression transform(Expression exp) {

+        if (exp == null) return null;

+        if (exp.getClass() == VariableExpression.class) {

+            return transformVariableExpression((VariableExpression) exp);

+        }

+        if (exp.getClass() == PropertyExpression.class) {

+            return transformPropertyExpression((PropertyExpression) exp);

+        }

+        if (exp.getClass() == MethodCallExpression.class) {

+            return transformMethodCallExpression((MethodCallExpression) exp);

+        }

+        if (exp.getClass() == ClosureExpression.class) {

+            return transformClosureExpression((ClosureExpression) exp);

+        }

+        if (exp.getClass() == ConstructorCallExpression.class) {

+            return transformConstructorCallExpression((ConstructorCallExpression) exp);

+        }

+        if (exp.getClass() == ArgumentListExpression.class) {

+            Expression result = exp.transformExpression(this);

+            if (inPropertyExpression) {

+                foundArgs = result;

+            }

+            return result;

+        }

+        if (exp.getClass() == ConstantExpression.class) {

+            Expression result = exp.transformExpression(this);

+            if (inPropertyExpression) {

+                foundConstant = result;

+            }

+            return result;

+        }

+        return exp.transformExpression(this);

+    }

+

+    protected Expression transformVariableExpression(VariableExpression ve) {

+        Variable v = ve.getAccessedVariable();

+        if (v != null && v instanceof DynamicVariable) {

+            Expression result = findStaticFieldImportFromModule(v.getName());

+            if (result != null) return result;

+            if (!inPropertyExpression || inSpecialConstructorCall) addStaticVariableError(ve);

+        }

+        return ve;

+    }

+

+    protected Expression transformMethodCallExpression(MethodCallExpression mce) {

+        Expression args = transform(mce.getArguments());

+        Expression method = transform(mce.getMethod());

+        if (mce.isImplicitThis()) {

+            Expression ret = findStaticMethodImportFromModule(method, args);

+            if (ret != null) {

+                return ret;

+            }

+            if (inSpecialConstructorCall && method instanceof ConstantExpression) {

+                ConstantExpression ce = (ConstantExpression) method;

+                Object value = ce.getValue();

+                if (value instanceof String) {

+                    return new StaticMethodCallExpression(currentClass, (String) value, args);

+                }

+            }

+        }

+        Expression object = transform(mce.getObjectExpression());

+

+        MethodCallExpression result = new MethodCallExpression(object, method, args);

+        result.setSafe(mce.isSafe());

+        result.setImplicitThis(mce.isImplicitThis());

+        result.setSpreadSafe(mce.isSpreadSafe());

+        result.setSourcePosition(mce);

+        return result;

+    }

+

+    protected Expression transformConstructorCallExpression(ConstructorCallExpression cce) {

+        inSpecialConstructorCall = cce.isSpecialCall();

+        Expression ret = cce.transformExpression(this);

+        inSpecialConstructorCall = false;

+        return ret;

+    }

+

+    protected Expression transformClosureExpression(ClosureExpression ce) {

+        boolean oldInClosure = inClosure;

+        inClosure = true;

+        Statement code = ce.getCode();

+        if (code != null) code.visit(this);

+        inClosure = oldInClosure;

+        return ce;

+    }

+

+    // TODO: find a nicer way to do this - we are unravelling what ResolveVisitor ravelled

+    protected Expression transformPropertyExpression(PropertyExpression pe) {

+        boolean oldInPropertyExpression = inPropertyExpression;

+        Expression oldFoundArgs = foundArgs;

+        Expression oldFoundMethod = foundConstant;

+        inPropertyExpression = true;

+        foundArgs = null;

+        foundConstant = null;

+        Expression objectExpression = transform(pe.getObjectExpression());

+        if (foundArgs != null && foundConstant != null) {

+            Expression result = findStaticMethodImportFromModule(foundConstant, foundArgs);

+            if (result != null) {

+                objectExpression = result;

+            }

+        }

+        inPropertyExpression = oldInPropertyExpression;

+        foundArgs = oldFoundArgs;

+        foundConstant = oldFoundMethod;

+        pe.setObjectExpression(objectExpression);

+        if (!inSpecialConstructorCall) checkStaticScope(pe);

+        return pe;

+    }

+

+    private void checkStaticScope(PropertyExpression pe) {

+        if (inClosure) return;

+        for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {

+            if (it instanceof PropertyExpression) continue;

+            if (it instanceof VariableExpression) {

+                addStaticVariableError((VariableExpression) it);

+            }

+            return;

+        }

+    }

+

+    private void addStaticVariableError(VariableExpression ve) {

+        // closures are always dynamic

+        // propertiesExpressions will handle the error a bit different

+        if (!inSpecialConstructorCall && (inClosure || !ve.isInStaticContext())) return;

+        if (stillResolving) return;

+        if (ve == VariableExpression.THIS_EXPRESSION || ve == VariableExpression.SUPER_EXPRESSION) return;

+        Variable v = ve.getAccessedVariable();

+        if (v != null && !(v instanceof DynamicVariable) && v.isInStaticContext()) return;

+        addError("the name " + ve.getName() + " doesn't refer to a declared variable or class. The static" +

+                " scope requires that you declare variables before using them. If the variable should have" +

+                " been a class check the spelling.", ve);

+    }

+

+    private Expression findStaticFieldImportFromModule(String name) {

+        ModuleNode module = currentClass.getModule();

+        if (module == null) return null;

+        Map aliases = module.getStaticImportAliases();

+        stillResolving = false;

+        if (aliases.containsKey(name)) {

+            ClassNode node = (ClassNode) aliases.get(name);

+            Map fields = module.getStaticImportFields();

+            String fieldName = (String) fields.get(name);

+            Expression expression = findStaticField(node, fieldName);

+            if (expression != null) return expression;

+        }

+        Map importedClasses = module.getStaticImportClasses();

+        Iterator it = importedClasses.keySet().iterator();

+        while (it.hasNext()) {

+            String className = (String) it.next();

+            ClassNode node = (ClassNode) importedClasses.get(className);

+            Expression expression = findStaticField(node, name);

+            if (expression != null) return expression;

+        }

+        return null;

+    }

+

+    private Expression findStaticField(ClassNode staticImportType, String fieldName) {

+        if (!staticImportType.isResolved() && !staticImportType.isPrimaryClassNode()) {

+            stillResolving = true;

+        }

+        if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {

+            staticImportType.getFields(); // force init

+            FieldNode field = staticImportType.getField(fieldName);

+            if (field != null && field.isStatic()) {

+                return new PropertyExpression(new ClassExpression(staticImportType), fieldName);

+            }

+        }

+        return null;

+    }

+

+    private Expression findStaticMethodImportFromModule(Expression method, Expression args) {

+        ModuleNode module = currentClass.getModule();

+        if (module == null || !(method instanceof ConstantExpression)) return null;

+        Map aliases = module.getStaticImportAliases();

+        ConstantExpression ce = (ConstantExpression) method;

+        Object value = ce.getValue();

+        // skip non-Strings, e.g. Integer

+        if (!(value instanceof String)) return null;

+        final String name = (String) value;

+        if (aliases.containsKey(name)) {

+            ClassNode node = (ClassNode) aliases.get(name);

+            Map fields = module.getStaticImportFields();

+            String fieldName = (String) fields.get(name);

+            Expression expression = findStaticMethod(node, fieldName, args);

+            if (expression != null) return expression;

+        }

+        Map importPackages = module.getStaticImportClasses();

+        Iterator it = importPackages.keySet().iterator();

+        while (it.hasNext()) {

+            String className = (String) it.next();

+            ClassNode starImportType = (ClassNode) importPackages.get(className);

+            Expression expression = findStaticMethod(starImportType, name, args);

+            if (expression != null) return expression;

+        }

+        return null;

+    }

+

+    private Expression findStaticMethod(ClassNode staticImportType, String methodName, Expression args) {

+        if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {

+            if (staticImportType.hasPossibleStaticMethod(methodName, args)) {

+                return new StaticMethodCallExpression(staticImportType, methodName, args);

+            }

+        }

+        return null;

+    }

+

+    protected SourceUnit getSourceUnit() {

+        return source;

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/control/io/AbstractReaderSource.java b/groovy/src/main/org/codehaus/groovy/control/io/AbstractReaderSource.java
new file mode 100644
index 0000000..b7f4e34
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/io/AbstractReaderSource.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control.io;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.Janitor;
+
+/**
+ * For ReaderSources that can choose a parent class, a base that
+ * provides common functionality.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @version $Id$
+ */
+
+public abstract class AbstractReaderSource implements ReaderSource {
+    protected CompilerConfiguration configuration;   // Configuration data
+
+    public AbstractReaderSource(CompilerConfiguration configuration) {
+        if (configuration == null) {
+            throw new IllegalArgumentException("Compiler configuration must not be null!");
+            // ... or more relaxed?
+            // configuration = CompilerConfiguration.DEFAULT;
+        }
+        this.configuration = configuration;
+    }
+
+    /**
+     * Returns true if the source can be restarted (ie. if getReader()
+     * will return non-null on subsequent calls.
+     */
+    public boolean canReopenSource() {
+        return true;
+    }
+
+    private BufferedReader lineSource = null;    // If set, a reader on the current source file
+    private String line = null;    // The last line read from the current source file
+    private int number = 0;       // The last line number read
+
+    /**
+     * Returns a line from the source, or null, if unavailable.  If
+     * you supply a Janitor, resources will be cached.
+     */
+    public String getLine(int lineNumber, Janitor janitor) {
+        // If the source is already open and is passed the line we
+        // want, close it.
+        if (lineSource != null && number > lineNumber) {
+            cleanup();
+        }
+
+        // If the line source is closed, try to open it.
+        if (lineSource == null) {
+            try {
+                lineSource = new BufferedReader(getReader());
+            } catch (Exception e) {
+                // Ignore
+            }
+            number = 0;
+        }
+
+        // Read until the appropriate line number.
+        if (lineSource != null) {
+            while (number < lineNumber) {
+                try {
+                    line = lineSource.readLine();
+                    number++;
+                }
+                catch (IOException e) {
+                    cleanup();
+                }
+            }
+
+            if (janitor == null) {
+                cleanup();
+            } else {
+                janitor.register(this);
+            }
+        }
+
+        return line;
+    }
+
+    /**
+     * Cleans up any cached resources used by getLine().
+     */
+    public void cleanup() {
+        if (lineSource != null) {
+            try {
+                lineSource.close();
+            } catch (Exception e) {
+                // Ignore
+            }
+        }
+
+        lineSource = null;
+        line = null;
+        number = 0;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/io/FileReaderSource.java b/groovy/src/main/org/codehaus/groovy/control/io/FileReaderSource.java
new file mode 100644
index 0000000..f32acb2
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/io/FileReaderSource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control.io;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import org.codehaus.groovy.control.CompilerConfiguration;
+
+/**
+ *  A ReaderSource for source files.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *  @version $Id$
+ */
+public class FileReaderSource extends AbstractReaderSource {
+    private File file;  // The File from which we produce Readers.
+
+   /**
+    *  Creates the ReaderSource from a File descriptor.
+    * @param file script source file
+    * @param configuration configuration for compiling source
+    */
+    public FileReaderSource( File file, CompilerConfiguration configuration ) {
+       super( configuration );
+        this.file = file;
+    }
+
+   /**
+    *  Returns a new Reader on the underlying source object.  
+    */
+    public Reader getReader() throws IOException {
+       return new InputStreamReader( new FileInputStream(file), configuration.getSourceEncoding() );
+    }
+    
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/io/InputStreamReaderSource.java b/groovy/src/main/org/codehaus/groovy/control/io/InputStreamReaderSource.java
new file mode 100644
index 0000000..e186509
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/io/InputStreamReaderSource.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+import org.codehaus.groovy.control.CompilerConfiguration;
+
+/**
+ * A ReaderSource for source strings.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @version $Id$
+ */
+public class InputStreamReaderSource extends AbstractReaderSource {
+    private InputStream stream;  // The InputStream from which we produce a Reader.
+
+    /**
+     * Creates the ReaderSource from a File descriptor.
+     *
+     * @param stream        stream containing source
+     * @param configuration configuration for compiling source
+     */
+    public InputStreamReaderSource(InputStream stream, CompilerConfiguration configuration) {
+        super(configuration);
+        this.stream = stream;
+    }
+
+    /**
+     * Returns a new Reader on the underlying source object.
+     */
+    public Reader getReader() throws IOException {
+        if (stream != null) {
+            Reader reader = new InputStreamReader(stream, configuration.getSourceEncoding());
+            stream = null;
+            return reader;
+        }
+        return null;
+    }
+
+    /**
+     * Returns true if the source can be restarted (ie. if getReader()
+     * will return non-null on subsequent calls.
+     */
+    public boolean canReopenSource() {
+        return false;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/io/NullWriter.java b/groovy/src/main/org/codehaus/groovy/control/io/NullWriter.java
new file mode 100644
index 0000000..6acaca7
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/io/NullWriter.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control.io;
+
+import java.io.Writer;
+
+/**
+ *  An Writer than eats its input.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+public class NullWriter extends Writer {
+    public static final NullWriter DEFAULT = new NullWriter();
+    
+    public void close() {}
+    
+    public void flush() {}
+    
+    public void write( char[] cbuf, int off, int len ) {}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/io/ReaderSource.java b/groovy/src/main/org/codehaus/groovy/control/io/ReaderSource.java
new file mode 100644
index 0000000..7aac996
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/io/ReaderSource.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control.io;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.codehaus.groovy.control.HasCleanup;
+import org.codehaus.groovy.control.Janitor;
+
+/**
+ *  An interface for things that can supply (and potentially resupply) a Reader
+ *  on a source stream.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+public interface ReaderSource extends HasCleanup {
+   /**
+    *  Returns a new Reader on the underlying source object.  Returns
+    *  null if the source can't be reopened.
+    * @throws java.io.IOException if there was an error opening for stream
+    * @return the reader to the resource
+    */
+    Reader getReader() throws IOException;
+    
+   /**
+    *  Returns true if the source can be restarted (ie. if getReader()
+    *  will return non-null on subsequent calls.
+    * @return true if the resource can be reopened for reading
+    */
+    boolean canReopenSource();
+    
+   /**
+    *  Returns a line from the source, or null, if unavailable.  If
+    *  you supply a Janitor, resources will be cached.
+    * @param lineNumber the number of the line of interest
+    * @param janitor helper to clean up afterwards
+    * @return the line of interest
+    */
+    String getLine( int lineNumber, Janitor janitor );
+    
+   /**
+    *  Cleans up any cached resources used by getLine().
+    */
+    void cleanup();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/io/StringReaderSource.java b/groovy/src/main/org/codehaus/groovy/control/io/StringReaderSource.java
new file mode 100644
index 0000000..0d4bf25
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/io/StringReaderSource.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control.io;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
+import org.codehaus.groovy.control.CompilerConfiguration;
+
+/**
+ *  A ReaderSource for source strings.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class StringReaderSource extends AbstractReaderSource {
+    private final String string;  // The String from which we produce Readers.
+
+   /**
+    * Creates the ReaderSource from a File descriptor.
+    *
+    * @param string string containing script source
+    * @param configuration configuration for compiling source
+    */
+    public StringReaderSource( String string, CompilerConfiguration configuration ) {
+       super( configuration );
+        this.string= string;
+    }
+    
+   /**
+    *  Returns a new Reader on the underlying source object.  
+    */
+    public Reader getReader() throws IOException {
+       return new StringReader( string );
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/io/URLReaderSource.java b/groovy/src/main/org/codehaus/groovy/control/io/URLReaderSource.java
new file mode 100644
index 0000000..7aa64e5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/io/URLReaderSource.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control.io;
+
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.URL;
+
+import org.codehaus.groovy.control.CompilerConfiguration;
+
+/**
+ *  A ReaderSource for source files hosted at a URL.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+public class URLReaderSource extends AbstractReaderSource {
+    private URL url;  // The URL from which we produce Readers.
+    
+   /**
+    *  Creates the ReaderSource from a File descriptor.
+    * @param url url pointing to script source
+    * @param configuration configuration for compiling source
+    */
+    public URLReaderSource( URL url, CompilerConfiguration configuration ) {
+       super( configuration );
+        this.url = url;
+    }
+
+   /**
+    *  Returns a new Reader on the underlying source object.  
+    */
+    public Reader getReader() throws IOException {
+       return new InputStreamReader( url.openStream(), configuration.getSourceEncoding() );
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/io/package.html b/groovy/src/main/org/codehaus/groovy/control/io/package.html
new file mode 100644
index 0000000..919683f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/io/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.control.io.*</title>
+  </head>
+  <body>
+    <p>Internal classes for Groovier Input/Output.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/control/messages/ExceptionMessage.java b/groovy/src/main/org/codehaus/groovy/control/messages/ExceptionMessage.java
new file mode 100644
index 0000000..0057701
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/messages/ExceptionMessage.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control.messages;
+
+import java.io.PrintWriter;
+
+import org.codehaus.groovy.control.Janitor;
+import org.codehaus.groovy.control.ProcessingUnit;
+
+
+
+/**
+ *  A class for error messages produced by the parser system.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class ExceptionMessage extends Message
+{
+    protected boolean verbose = true;
+
+    private Exception cause = null;   // The exception source of the message, if any
+    ProcessingUnit owner = null;
+
+    public ExceptionMessage( Exception cause, boolean v, ProcessingUnit owner )
+    {
+        this.verbose = v;
+        this.cause = cause;
+        this.owner = owner;
+    }
+    
+    
+   
+   /**
+    *  Returns the underlying Exception.
+    */
+
+    public Exception getCause()
+    {
+        return this.cause;
+    }
+    
+
+
+   /**
+    *  Writes out a nicely formatted summary of the exception. 
+    */
+    
+    public void write( PrintWriter output, Janitor janitor )
+    {
+        String description = "General error during " + owner.getPhaseDescription() + ": "; 
+        
+        String message = cause.getMessage();
+        if( message != null )
+        {
+            output.println( description + message );
+        }
+        else
+        {
+            output.println( description + cause );
+        }
+        output.println();
+
+        //if (verbose) {
+            cause.printStackTrace(output);
+        //}
+    }
+    
+}
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/messages/LocatedMessage.java b/groovy/src/main/org/codehaus/groovy/control/messages/LocatedMessage.java
new file mode 100644
index 0000000..2879c30
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/messages/LocatedMessage.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control.messages;
+
+import java.io.PrintWriter;
+
+import org.codehaus.groovy.control.Janitor;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.CSTNode;
+
+
+
+/**
+ *  A base class for compilation messages.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class LocatedMessage extends SimpleMessage
+{
+    protected CSTNode context;  // The CSTNode that indicates the location to which the message applies
+    
+    public LocatedMessage( String message, CSTNode context, SourceUnit source ) 
+    {
+        super( message, source );
+        this.context = context;
+    }
+    
+    
+    public LocatedMessage( String message, Object data, CSTNode context, SourceUnit source ) 
+    {
+        super( message, data, source );
+        this.context = context;
+    }
+    
+    
+    public void write( PrintWriter writer, Janitor janitor )
+    {
+        SourceUnit source = (SourceUnit) owner;
+        
+        String name   = source.getName();
+        int    line   = context.getStartLine();
+        int    column = context.getStartColumn();
+        String sample = source.getSample( line, column, janitor );
+        
+        if( sample != null )
+        {
+            writer.println( source.getSample(line, column, janitor) );
+        }
+        
+        writer.println( name + ": " + line + ": " + this.message );
+        writer.println("");
+    }
+    
+}
+
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/messages/Message.java b/groovy/src/main/org/codehaus/groovy/control/messages/Message.java
new file mode 100644
index 0000000..5c2471b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/messages/Message.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control.messages;
+
+import java.io.PrintWriter;
+
+import org.codehaus.groovy.control.Janitor;
+import org.codehaus.groovy.control.ProcessingUnit;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+
+
+/**
+ *  A base class for compilation messages.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public abstract class Message
+{
+    
+    
+   /**
+    *  Writes the message to the specified PrintWriter.  The supplied
+    *  ProcessingUnit is the unit that holds this Message.
+    */
+    
+    public abstract void write( PrintWriter writer, Janitor janitor );
+    
+    
+   /**
+    *  A synonyn for write( writer, owner, null ).
+    */
+    
+    public final void write( PrintWriter writer)
+    {
+        write( writer,  null );
+    }
+    
+    
+    
+  //---------------------------------------------------------------------------
+  // FACTORY METHODS
+    
+    
+   /**
+    *  Creates a new Message from the specified text.
+    */
+    
+    public static Message create( String text, ProcessingUnit owner )
+    {
+        return new SimpleMessage( text, owner );
+    }
+    
+    
+          
+   /**
+    *  Creates a new Message from the specified text.
+    */
+     
+    public static Message create( String text, Object data, ProcessingUnit owner  )
+    {
+        return new SimpleMessage( text, data, owner);
+    }
+     
+     
+           
+   /**
+    *  Creates a new Message from the specified SyntaxException.
+    */
+      
+    public static Message create( SyntaxException error, SourceUnit owner )
+    {
+        return new SyntaxErrorMessage( error, owner );
+    }
+      
+    
+      
+    
+}
+
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/messages/SimpleMessage.java b/groovy/src/main/org/codehaus/groovy/control/messages/SimpleMessage.java
new file mode 100644
index 0000000..898524f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/messages/SimpleMessage.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control.messages;
+
+import java.io.PrintWriter;
+
+import org.codehaus.groovy.control.Janitor;
+import org.codehaus.groovy.control.ProcessingUnit;
+import org.codehaus.groovy.control.SourceUnit;
+
+
+
+/**
+ *  A base class for compilation messages.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class SimpleMessage extends Message
+{
+    protected String message;  // Message text
+    protected Object data;     // Data, when the message text is an I18N identifier
+    protected ProcessingUnit owner;
+    
+    public SimpleMessage( String message, ProcessingUnit source ) 
+    {
+        this( message, null, source );
+    }
+    
+    public SimpleMessage( String message, Object data, ProcessingUnit source )
+    {
+        this.message = message;
+        this.data    = null;
+        this.owner = source;
+    }
+    
+    
+    public void write( PrintWriter writer, Janitor janitor )
+    {
+        if( owner instanceof SourceUnit )
+        {
+            String name = ((SourceUnit)owner).getName();
+            writer.println( "" + name + ": " + message );
+        }
+        else
+        {
+            writer.println( message );
+        }
+    }
+    
+    
+    public String getMessage()
+    {
+        return message;
+    }
+    
+}
+
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/messages/SyntaxErrorMessage.java b/groovy/src/main/org/codehaus/groovy/control/messages/SyntaxErrorMessage.java
new file mode 100644
index 0000000..6945a91
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/messages/SyntaxErrorMessage.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.control.messages;
+
+import java.io.PrintWriter;
+
+import org.codehaus.groovy.control.Janitor;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+/**
+ * A class for error messages produced by the parser system.
+ *
+ * @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ * @version $Id$
+ */
+
+public class SyntaxErrorMessage extends Message {
+    protected SyntaxException cause;
+    protected SourceUnit source;
+    
+    public SyntaxErrorMessage(SyntaxException cause, SourceUnit source) {
+        this.cause = cause;
+        this.source = source;
+        cause.setSourceLocator(source.getName());
+    }
+
+    /**
+     * Returns the underlying SyntaxException.
+     */
+
+    public SyntaxException getCause() {
+        return this.cause;
+    }
+
+    /**
+     * Writes out a nicely formatted summary of the syntax error.
+     */
+
+    public void write(PrintWriter output, Janitor janitor) {
+        String name = source.getName();
+        int line = getCause().getStartLine();
+        int column = getCause().getStartColumn();
+        String sample = source.getSample(line, column, janitor);
+
+        output.print(name + ": " + line + ": " + getCause().getMessage());
+        if (sample != null) {
+            output.println();
+            output.print(sample);
+            output.println();
+        }
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/control/messages/WarningMessage.java b/groovy/src/main/org/codehaus/groovy/control/messages/WarningMessage.java
new file mode 100644
index 0000000..56cfd21
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/messages/WarningMessage.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.control.messages;
+
+import java.io.PrintWriter;
+
+import org.codehaus.groovy.control.Janitor;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.CSTNode;
+
+
+
+/**
+ *  A class for warning messages.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class WarningMessage extends LocatedMessage
+{
+  //---------------------------------------------------------------------------
+  // WARNING LEVELS
+
+    public static final int NONE            = 0;  // For querying, ignore all errors
+    public static final int LIKELY_ERRORS   = 1;  // Warning indicates likely error
+    public static final int POSSIBLE_ERRORS = 2;  // Warning indicates possible error
+    public static final int PARANOIA        = 3;  // Warning indicates paranoia on the part of the compiler
+    
+    
+   /**
+    *  Returns true if a warning would be relevant to the specified level.
+    */
+    
+    public static boolean isRelevant( int actual, int limit )
+    {
+        return actual <= limit;
+    }
+    
+    
+    
+   /**
+    *  Returns true if this message is as or more important than the 
+    *  specified importance level.
+    */
+    
+    public boolean isRelevant( int importance )
+    {
+        return isRelevant( this.importance, importance );
+    }
+    
+    
+    
+  //---------------------------------------------------------------------------
+  // CONSTRUCTION AND DATA ACCESS
+
+    private int importance;  // The warning level, for filtering
+    
+    
+   /**
+    *  Creates a new warning message.
+    * 
+    *  @param importance the warning level 
+    *  @param message    the message text
+    *  @param context    context information for locating the offending source text
+    */
+     
+    public WarningMessage( int importance, String message, CSTNode context, SourceUnit owner )
+    {
+        super( message, context, owner );
+        this.importance = importance;
+    }
+
+    
+    
+   /**
+    *  Creates a new warning message.
+    *
+    *  @param importance the warning level 
+    *  @param message    the message text
+    *  @param data       additional data needed when generating the message
+    *  @param context    context information for locating the offending source text
+    */
+     
+    public WarningMessage( int importance, String message, Object data, CSTNode context, SourceUnit owner )
+    {
+        super( message, data, context, owner );
+        this.importance = importance;
+    }
+    
+    
+    public void write( PrintWriter writer, Janitor janitor )
+    {
+        writer.print( "warning: " );
+        super.write( writer, janitor );
+    }
+
+     
+     
+}
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/control/messages/package.html b/groovy/src/main/org/codehaus/groovy/control/messages/package.html
new file mode 100644
index 0000000..062d4eb
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/messages/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.control.messages.*</title>
+  </head>
+  <body>
+    <p>Error message classes.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/control/package.html b/groovy/src/main/org/codehaus/groovy/control/package.html
new file mode 100644
index 0000000..c81574a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/control/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.control.*</title>
+  </head>
+  <body>
+    <p>Compiler control classes.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyClassDoc.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyClassDoc.java
new file mode 100644
index 0000000..d60e2cb
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyClassDoc.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyClassDoc extends GroovyType, GroovyProgramElementDoc{
+    GroovyConstructorDoc[] constructors();
+    GroovyConstructorDoc[] constructors(boolean filter);
+    boolean definesSerializableFields();
+    GroovyFieldDoc[] enumConstants();
+    GroovyFieldDoc[] fields();
+    GroovyFieldDoc[] fields(boolean filter);
+    GroovyClassDoc findClass(String className);
+    GroovyClassDoc[] importedClasses();
+    GroovyPackageDoc[] importedPackages();
+    GroovyClassDoc[] innerClasses(); // not supported in groovy
+    GroovyClassDoc[] innerClasses(boolean filter); // not supported in groovy
+    GroovyClassDoc[] interfaces();
+    GroovyType[] interfaceTypes();
+    boolean isAbstract();
+    boolean isExternalizable();
+    boolean isSerializable();
+    GroovyMethodDoc[] methods();
+    GroovyMethodDoc[] methods(boolean filter);
+    GroovyFieldDoc[] serializableFields();
+    GroovyMethodDoc[] serializationMethods();
+    boolean subclassOf(GroovyClassDoc gcd);
+    GroovyClassDoc superclass();
+    GroovyType superclassType();
+//    GroovyTypeVariable[] typeParameters(); // not supported in groovy
+//    GroovyParamTag[] typeParamTags(); // not supported in groovy
+
+
+    String getFullPathName(); // not in Java Doclet API
+    String getRelativeRootPath(); // not in Java Doclet API	
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyConstructorDoc.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyConstructorDoc.java
new file mode 100644
index 0000000..13a3e86
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyConstructorDoc.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyConstructorDoc extends GroovyExecutableMemberDoc {
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyDoc.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyDoc.java
new file mode 100644
index 0000000..5efcc47
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyDoc.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyDoc extends Comparable {
+
+    String commentText();
+//    GroovyTag[] firstSentenceTags();
+    String getRawCommentText();
+//    GroovyTag[] inlineTags();
+    boolean isAnnotationType();
+    boolean isAnnotationTypeElement();
+    boolean isClass();
+    boolean isConstructor();
+    boolean isEnum();
+    boolean isEnumConstant();
+    boolean isError();
+    boolean isException();
+    boolean isField();
+    boolean isIncluded();
+    boolean isInterface();
+    boolean isMethod();
+    boolean isOrdinaryClass();
+    String name();
+//    GroovySourcePosition position();
+//    GroovySeeTag[] seeTags();
+    void setRawCommentText(String arg0);
+//    GroovyTag[] tags();
+//    GroovyTag[] tags(String arg0);
+	
+    String firstSentenceCommentText();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyDocErrorReporter.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyDocErrorReporter.java
new file mode 100644
index 0000000..e6224b5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyDocErrorReporter.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyDocErrorReporter{
+    void printError(String arg0);
+//    void printError(GroovySourcePosition arg0, String arg1);
+    void printNotice(String arg0);
+//    void printNotice(GroovySourcePosition arg0, String arg1);
+    void printWarning(String arg0);
+//    void printWarning(GroovySourcePosition arg0, String arg1);
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyExecutableMemberDoc.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyExecutableMemberDoc.java
new file mode 100644
index 0000000..fc751df
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyExecutableMemberDoc.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyExecutableMemberDoc extends GroovyMemberDoc {
+    String flatSignature();
+    boolean isNative();
+    boolean isSynchronized();
+    boolean isVarArgs();
+//    GroovyParameter[] parameters();
+//    GroovyParamTag[] paramTags();
+    String signature();
+    GroovyClassDoc[] thrownExceptions();
+    GroovyType[] thrownExceptionTypes();
+//    GroovyThrowsTag[] throwsTags();
+//    GroovyTypeVariable[] typeParameters();
+//    GroovyParamTag[] typeParamTags();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyFieldDoc.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyFieldDoc.java
new file mode 100644
index 0000000..f68018a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyFieldDoc.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.groovydoc;
+
+
+public interface GroovyFieldDoc extends GroovyMemberDoc {
+    Object constantValue();
+    String constantValueExpression();
+    boolean isTransient();
+    boolean isVolatile();
+//    GroovySerialFieldTag[] serialFieldTags();
+    GroovyType type();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyMemberDoc.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyMemberDoc.java
new file mode 100644
index 0000000..a31107b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyMemberDoc.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyMemberDoc extends GroovyProgramElementDoc{
+    boolean isSynthetic();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyMethodDoc.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyMethodDoc.java
new file mode 100644
index 0000000..646dfa1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyMethodDoc.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyMethodDoc extends GroovyExecutableMemberDoc {
+    boolean isAbstract();
+    GroovyClassDoc overriddenClass();
+    GroovyMethodDoc overriddenMethod();
+    GroovyType overriddenType();
+    boolean overrides(GroovyMethodDoc arg0);
+    GroovyType returnType();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyPackageDoc.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyPackageDoc.java
new file mode 100644
index 0000000..c31ba0c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyPackageDoc.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyPackageDoc extends GroovyDoc {
+    GroovyClassDoc[] allClasses();
+    GroovyClassDoc[] allClasses(boolean arg0);
+//    GroovyAnnotationTypeDoc[] annotationTypes();
+//    GroovyAnnotationDesc[] annotations();
+    GroovyClassDoc[] enums();
+    GroovyClassDoc[] errors();
+    GroovyClassDoc[] exceptions();
+    GroovyClassDoc findClass(String arg0);
+    GroovyClassDoc[] interfaces();
+    GroovyClassDoc[] ordinaryClasses();
+	
+    String nameWithDots(); // not in JavaDoc API
+    String getRelativeRootPath(); // not in JavaDoc API
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyParameter.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyParameter.java
new file mode 100644
index 0000000..e01609d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyParameter.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyParameter{
+//	GroovyAnnotationDesc[] annotations();
+	String name();
+	GroovyType type();
+	String typeName();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyProgramElementDoc.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyProgramElementDoc.java
new file mode 100644
index 0000000..d1c9eb8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyProgramElementDoc.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyProgramElementDoc extends GroovyDoc {
+
+//    GroovyAnnotationDesc[] annotations();
+
+    GroovyClassDoc containingClass();
+
+    GroovyPackageDoc containingPackage();
+
+    boolean isFinal();
+
+    boolean isPackagePrivate();
+
+    boolean isPrivate();
+
+    boolean isProtected();
+
+    boolean isPublic();
+
+    boolean isStatic();
+
+    String modifiers();
+
+    int modifierSpecifier();
+
+    String qualifiedName();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyRootDoc.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyRootDoc.java
new file mode 100644
index 0000000..9872bd8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyRootDoc.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyRootDoc extends GroovyDoc, GroovyDocErrorReporter {
+	GroovyClassDoc classNamed(String arg0);
+	GroovyClassDoc[] classes();
+	String[][] options();
+	GroovyPackageDoc packageNamed(String arg0);
+	GroovyClassDoc[] specifiedClasses();
+	GroovyPackageDoc[] specifiedPackages();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyType.java b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyType.java
new file mode 100644
index 0000000..1707eb2
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/GroovyType.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.groovydoc;
+
+public interface GroovyType {
+//	GroovyAnnotationTypeDoc asAnnotationTypeDoc();
+	GroovyClassDoc asClassDoc();
+//	GroovyParameterizedType asParameterizedType();
+//	GroovyTypeVariable asTypeVariable();
+//	GroovyWildcardType asWildcardType();
+	String dimension();
+	boolean isPrimitive();
+	String qualifiedTypeName();
+	String simpleTypeName();
+	String typeName();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/groovydoc/package.html b/groovy/src/main/org/codehaus/groovy/groovydoc/package.html
new file mode 100644
index 0000000..64a504b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/groovydoc/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.groovydoc.*</title>
+  </head>
+  <body>
+    <p>GroovyDoc internal classes.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/package.html b/groovy/src/main/org/codehaus/groovy/package.html
new file mode 100644
index 0000000..0bbd7fc
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.*</title>
+  </head>
+  <body>
+    <p>Groovy Language for the JVM</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/reflection/CachedClass.java b/groovy/src/main/org/codehaus/groovy/reflection/CachedClass.java
new file mode 100644
index 0000000..1485400
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/CachedClass.java
@@ -0,0 +1,417 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.reflection;

+

+import groovy.lang.GString;

+import groovy.lang.MetaMethod;

+import org.codehaus.groovy.classgen.BytecodeHelper;

+import org.codehaus.groovy.runtime.Reflector;

+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;

+

+import java.lang.reflect.Constructor;

+import java.lang.reflect.Field;

+import java.lang.reflect.Method;

+import java.math.BigDecimal;

+import java.math.BigInteger;

+import java.util.*;

+import java.security.PrivilegedAction;

+import java.security.AccessController;

+

+/**

+ * @author Alex.Tkachman

+ */

+public class CachedClass {

+    private CachedClass cachedSuperClass;

+

+    private static final MetaMethod[] EMPTY = new MetaMethod[0];

+

+    int hashCode;

+

+    private Reflector reflector;

+

+    private CachedField[] fields;

+    private CachedConstructor[] constructors;

+    private CachedMethod[] methods;

+    private final Class cachedClass;

+    private MetaMethod[] newMetaMethods = EMPTY;

+    public  CachedMethod [] mopMethods;

+    public static final CachedClass[] EMPTY_ARRAY = new CachedClass[0];

+

+    public Set getInterfaces() {

+        if (interfaces == null)  {

+            interfaces = new HashSet (0);

+

+            if (getCachedClass().isInterface())

+              interfaces.add(this);

+

+            Class[] classes = getCachedClass().getInterfaces();

+            for (int i = 0; i < classes.length; i++) {

+                final CachedClass aClass = ReflectionCache.getCachedClass(classes[i]);

+                if (!interfaces.contains(aClass))

+                  interfaces.addAll(aClass.getInterfaces());

+            }

+

+            final CachedClass superClass = getCachedSuperClass();

+            if (superClass != null)

+              interfaces.addAll(superClass.getInterfaces());

+        }

+        return interfaces;

+    }

+

+    private Set ownInterfaces;

+

+    public Set getOwnInterfaces() {

+        if (ownInterfaces == null)  {

+            ownInterfaces = new HashSet (0);

+

+            if (getCachedClass().isInterface())

+              ownInterfaces.add(this);

+

+            Class[] classes = getCachedClass().getInterfaces();

+            for (int i = 0; i < classes.length; i++) {

+                final CachedClass aClass = ReflectionCache.getCachedClass(classes[i]);

+                if (!ownInterfaces.contains(aClass))

+                  ownInterfaces.addAll(aClass.getInterfaces());

+            }

+

+            final CachedClass superClass = getCachedSuperClass();

+            if (superClass != null)

+              ownInterfaces.addAll(superClass.getInterfaces());

+        }

+        return ownInterfaces;

+    }

+

+    private Set interfaces;

+

+    public final boolean isArray;

+    public final boolean isPrimitive;

+    public final int modifiers;

+    int distance = -1;

+    public final boolean isInterface;

+    public final boolean isNumber;

+

+    CachedClass(Class klazz) {

+        cachedClass = klazz;

+        isArray = klazz.isArray();

+        isPrimitive = klazz.isPrimitive();

+        modifiers = klazz.getModifiers();

+        isInterface = klazz.isInterface();

+        isNumber = Number.class.isAssignableFrom(klazz);

+    }

+

+    public synchronized CachedClass getCachedSuperClass() {

+        if (cachedSuperClass == null) {

+            if (!isArray)

+              cachedSuperClass = ReflectionCache.getCachedClass(getCachedClass().getSuperclass());

+            else

+              if (cachedClass.getComponentType().isPrimitive() || cachedClass.getComponentType() == Object.class)

+                cachedSuperClass = ReflectionCache.OBJECT_CLASS;

+              else

+                cachedSuperClass = ReflectionCache.OBJECT_ARRAY_CLASS;

+        }

+

+        return cachedSuperClass;

+    }

+

+    public synchronized CachedMethod[] getMethods() {

+        if (methods == null) {

+            final Method[] declaredMethods = (Method[])

+               AccessController.doPrivileged(new PrivilegedAction/*<Method[]>*/() {

+                   public /*Method[]*/ Object run() {

+                       return getCachedClass().getDeclaredMethods();

+                   }

+               });

+            ArrayList methods = new ArrayList(declaredMethods.length);

+            ArrayList mopMethods = new ArrayList(declaredMethods.length);

+            for (int i = 0; i != declaredMethods.length; ++i) {

+                final CachedMethod cachedMethod = new CachedMethod(this, declaredMethods[i]);

+                final String name = cachedMethod.getName();

+

+                if (name.indexOf('+') >= 0) {

+                    // Skip Synthetic methods inserted by JDK 1.5 compilers and later

+                    continue;

+                } /*else if (Modifier.isAbstract(reflectionMethod.getModifiers())) {

+                   continue;

+                }*/

+

+                if (name.startsWith("this$") || name.startsWith("super$"))

+                  mopMethods.add(cachedMethod);

+                else

+                  methods.add(cachedMethod);

+            }

+            this.methods = (CachedMethod[]) methods.toArray(new CachedMethod[methods.size()]);

+            Arrays.sort(this.methods);

+

+            final CachedClass superClass = getCachedSuperClass();

+            if (superClass != null) {

+                superClass.getMethods();

+                final CachedMethod[] superMopMethods = superClass.mopMethods;

+                for (int i = 0; i != superMopMethods.length; ++i)

+                  mopMethods.add(superMopMethods[i]);

+            }

+            this.mopMethods = (CachedMethod[]) mopMethods.toArray(new CachedMethod[mopMethods.size()]);

+            Arrays.sort(this.mopMethods, CachedMethodComparatorByName.INSTANCE);

+        }

+        return methods;

+    }

+

+    public synchronized CachedField[] getFields() {

+        if (fields == null) {

+

+            final Field[] declaredFields = (Field[])

+               AccessController.doPrivileged(new PrivilegedAction/*<Field[]>*/() {

+                   public /*Field[]*/ Object run() {

+                       return getCachedClass().getDeclaredFields();

+                   }

+               });

+            fields = new CachedField[declaredFields.length];

+            for (int i = 0; i != fields.length; ++i)

+                fields[i] = new CachedField(this, declaredFields[i]);

+        }

+        return fields;

+    }

+

+    public synchronized CachedConstructor[] getConstructors() {

+        if (constructors == null) {

+            final Constructor[] declaredConstructors = (Constructor[])

+               AccessController.doPrivileged(new PrivilegedAction/*<Constructor[]>*/() {

+                   public /*Constructor[]*/ Object run() {

+                       return getCachedClass().getDeclaredConstructors();

+                   }

+               });

+            constructors = new CachedConstructor[declaredConstructors.length];

+            for (int i = 0; i != constructors.length; ++i)

+                constructors[i] = new CachedConstructor(this, declaredConstructors[i]);

+        }

+        return constructors;

+    }

+

+    public CachedMethod searchMethods(String name, CachedClass[] parameterTypes) {

+        CachedMethod[] methods = getMethods();

+

+        CachedMethod res = null;

+        for (int i = 0; i < methods.length; i++) {

+            CachedMethod m = methods[i];

+            if (m.getName().equals(name)

+                    && ReflectionCache.arrayContentsEq(parameterTypes, m.getParameterTypes())

+                    && (res == null || res.getReturnType().isAssignableFrom(m.cachedMethod.getReturnType())))

+                res = m;

+        }

+

+        return res;

+    }

+

+    public int getModifiers() {

+        return modifiers;

+    }

+

+    /**

+     * Coerces a GString instance into String if needed

+     *

+     * @return the coerced argument

+     */

+    protected Object coerceGString(Object argument) {

+        if (getCachedClass() != String.class) return argument;

+        if (!(argument instanceof GString)) return argument;

+        return argument.toString();

+    }

+

+    // PRECONDITION:

+    //   !ReflectionCache.isAssignableFrom(parameterType, argument.getClass())

+    protected Object coerceNumber(Object argument) {

+        if (argument instanceof Number && (isNumber || isPrimitive)) { // Number types

+            Object oldArgument = argument;

+            boolean wasDouble = false;

+            boolean wasFloat = false;

+            Class param = getCachedClass();

+            if (param == Byte.class || param == Byte.TYPE) {

+                argument = new Byte(((Number) argument).byteValue());

+            } else if (param == Double.class || param == Double.TYPE) {

+                wasDouble = true;

+                argument = new Double(((Number) argument).doubleValue());

+            } else if (param == Float.class || param == Float.TYPE) {

+                wasFloat = true;

+                argument = new Float(((Number) argument).floatValue());

+            } else if (param == Integer.class || param == Integer.TYPE) {

+                argument = new Integer(((Number) argument).intValue());

+            } else if (param == Long.class || param == Long.TYPE) {

+                argument = new Long(((Number) argument).longValue());

+            } else if (param == Short.class || param == Short.TYPE) {

+                argument = new Short(((Number) argument).shortValue());

+            } else if (param == BigDecimal.class) {

+                argument = new BigDecimal(String.valueOf((Number) argument));

+            } else if (param == BigInteger.class) {

+                argument = new BigInteger(String.valueOf((Number) argument));

+            }

+

+            if (oldArgument instanceof BigDecimal) {

+                BigDecimal oldbd = (BigDecimal) oldArgument;

+                boolean throwException = false;

+                if (wasDouble) {

+                    Double d = (Double) argument;

+                    if (d.isInfinite()) throwException = true;

+                } else if (wasFloat) {

+                    Float f = (Float) argument;

+                    if (f.isInfinite()) throwException = true;

+                } else {

+                    BigDecimal newbd = new BigDecimal(String.valueOf((Number) argument));

+                    throwException = !oldArgument.equals(newbd);

+                }

+

+                if (throwException)

+                    throw new IllegalArgumentException(param + " out of range while converting from BigDecimal");

+            }

+

+        }

+        return argument;

+    }

+

+    protected Object coerceArray(Object argument) {

+        if (!isArray) return argument;

+        Class argumentClass = argument.getClass();

+        if (!argumentClass.isArray()) return argument;

+        Class argumentComponent = argumentClass.getComponentType();

+

+        Class paramComponent = getCachedClass().getComponentType();

+        if (paramComponent.isPrimitive()) {

+            if (paramComponent == boolean.class && argumentClass == Boolean[].class) {

+                argument = DefaultTypeTransformation.convertToBooleanArray(argument);

+            } else if (paramComponent == byte.class && argumentClass == Byte[].class) {

+                argument = DefaultTypeTransformation.convertToByteArray(argument);

+            } else if (paramComponent == char.class && argumentClass == Character[].class) {

+                argument = DefaultTypeTransformation.convertToCharArray(argument);

+            } else if (paramComponent == short.class && argumentClass == Short[].class) {

+                argument = DefaultTypeTransformation.convertToShortArray(argument);

+            } else if (paramComponent == int.class && argumentClass == Integer[].class) {

+                argument = DefaultTypeTransformation.convertToIntArray(argument);

+            } else if (paramComponent == long.class &&

+                    (argumentClass == Long[].class || argumentClass == Integer[].class)) {

+                argument = DefaultTypeTransformation.convertToLongArray(argument);

+            } else if (paramComponent == float.class &&

+                    (argumentClass == Float[].class || argumentClass == Integer[].class)) {

+                argument = DefaultTypeTransformation.convertToFloatArray(argument);

+            } else if (paramComponent == double.class &&

+                    (argumentClass == Double[].class || argumentClass == Float[].class

+                            || BigDecimal[].class.isAssignableFrom(argumentClass))) {

+                argument = DefaultTypeTransformation.convertToDoubleArray(argument);

+            }

+        } else if (paramComponent == String.class && argument instanceof GString[]) {

+            GString[] strings = (GString[]) argument;

+            String[] ret = new String[strings.length];

+            for (int i = 0; i < strings.length; i++) {

+                ret[i] = strings[i].toString();

+            }

+            argument = ret;

+        } else if (paramComponent==Object.class && argumentComponent.isPrimitive()){

+            argument = DefaultTypeTransformation.primitiveArrayBox(argument);

+        }

+        return argument;

+    }

+    

+    public int getSuperClassDistance() {

+        synchronized (getCachedClass()) {

+            if (distance == -1) {

+                int distance = 0;

+                for (Class klazz= getCachedClass(); klazz != null; klazz = klazz.getSuperclass()) {

+                    distance++;

+                }

+                this.distance = distance;

+            }

+            return distance;

+        }

+    }

+

+    public int hashCode() {

+        if (hashCode == 0) {

+          hashCode = super.hashCode();

+          if (hashCode == 0)

+            hashCode = 0xcafebebe;

+        }

+        return hashCode;

+    }

+

+    public boolean isPrimitive() {

+        return isPrimitive;

+    }

+

+    public boolean isVoid() {

+        return getCachedClass() == void.class;

+    }

+

+    public void box(BytecodeHelper helper) {

+        helper.box(getCachedClass());

+    }

+

+    public void unbox(BytecodeHelper helper) {

+        helper.unbox(getCachedClass());

+    }

+

+    public boolean isInterface() {

+        return isInterface;

+    }

+

+    public void doCast(BytecodeHelper helper) {

+        helper.doCast(getCachedClass());

+    }

+

+    public String getName() {

+        return getCachedClass().getName();

+    }

+

+    public String getTypeDescription() {

+        return BytecodeHelper.getTypeDescription(getCachedClass());

+    }

+

+    public synchronized Reflector getReflector() {

+        /*if (reflector == null) {

+            final MetaClassRegistry metaClassRegistry = MetaClassRegistryImpl.getInstance(MetaClassRegistryImpl.LOAD_DEFAULT);

+            reflector = ((MetaClassRegistryImpl)metaClassRegistry).loadReflector(getCachedClass(), Arrays.asList(getMethods()));

+        }*/

+        return reflector;

+    }

+

+    public Class getCachedClass() {

+        return cachedClass;

+    }

+

+    public MetaMethod[] getNewMetaMethods() {

+        return newMetaMethods;

+    }

+

+    public void setNewMopMethods(ArrayList arr) {

+        newMetaMethods = (MetaMethod[]) arr.toArray(new MetaMethod[arr.size()]);

+    }

+

+    public static class CachedMethodComparatorByName implements Comparator {

+        public static final Comparator INSTANCE = new CachedMethodComparatorByName();

+

+        public int compare(Object o1, Object o2) {

+            return ((CachedMethod)o1).getName().compareTo(((CachedMethod)o2).getName());

+        }

+    }

+

+    public static class CachedMethodComparatorWithString implements Comparator {

+        public static final Comparator INSTANCE = new CachedMethodComparatorWithString();

+

+        public int compare(Object o1, Object o2) {

+            return ((CachedMethod)o1).getName().compareTo((String)o2);

+        }

+    }

+

+    public String toString() {

+        return cachedClass.toString();

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/reflection/CachedConstructor.java b/groovy/src/main/org/codehaus/groovy/reflection/CachedConstructor.java
new file mode 100644
index 0000000..ab6ce9b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/CachedConstructor.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.reflection;
+
+import java.lang.reflect.Constructor;
+
+/**
+ * @author Alex.Tkachman
+ */
+public class CachedConstructor extends ParameterTypes {
+    CachedClass clazz;
+
+    public final Constructor cachedConstructor;
+
+    public CachedConstructor(CachedClass clazz, Constructor c) {
+        this.cachedConstructor = c;
+        this.clazz = clazz;
+        try {
+            c.setAccessible(true);
+        }
+        catch (SecurityException e) {
+            // IGNORE
+        }
+    }
+
+    public CachedConstructor(Constructor c) {
+        this(ReflectionCache.getCachedClass(c.getDeclaringClass()), c);
+    }
+
+    Class[] getPT() {
+        return cachedConstructor.getParameterTypes();
+    }
+
+    public static CachedConstructor find(Constructor constructor) {
+        CachedConstructor[] constructors = ReflectionCache.getCachedClass(constructor.getDeclaringClass()).getConstructors();
+        for (int i = 0; i < constructors.length; i++) {
+            CachedConstructor cachedConstructor = constructors[i];
+            if (cachedConstructor.cachedConstructor.equals(constructor))
+                return cachedConstructor;
+        }
+        throw new RuntimeException("Couldn't find method: " + constructor);
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/reflection/CachedField.java b/groovy/src/main/org/codehaus/groovy/reflection/CachedField.java
new file mode 100644
index 0000000..4381edf
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/CachedField.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.reflection;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+public class CachedField {
+    public final Field field;
+
+    CachedClass cachedClass;
+
+    public CachedField(CachedClass clazz, Field field) {
+        this.field = field;
+        cachedClass = clazz;
+    }
+
+    public String getName() {
+        return field.getName();
+    }
+
+    public boolean isStatic() {
+        return Modifier.isStatic(getModifiers());
+    }
+
+    public Class getType() {
+        return field.getType();
+    }
+
+    public int getModifiers() {
+        return field.getModifiers();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/reflection/CachedMethod.java b/groovy/src/main/org/codehaus/groovy/reflection/CachedMethod.java
new file mode 100644
index 0000000..51db424
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/CachedMethod.java
@@ -0,0 +1,230 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.reflection;

+

+import groovy.lang.MetaMethod;

+import org.codehaus.groovy.classgen.BytecodeHelper;

+import org.codehaus.groovy.runtime.metaclass.MethodHelper;

+import org.codehaus.groovy.runtime.metaclass.ReflectionMetaMethod;

+

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.security.AccessController;

+import java.security.PrivilegedAction;

+import java.util.Arrays;

+

+/**

+ * @author Alex.Tkachman

+ */

+public class CachedMethod extends ParameterTypes implements Comparable{

+    public final CachedClass cachedClass;

+

+    public final Method cachedMethod;

+    private boolean alreadySetAccessible;

+    private int methodIndex;

+    private int hashCode;

+    private MetaMethod reflectionMetaMethod;

+

+    public CachedMethod(CachedClass clazz, Method method) {

+        this.cachedMethod = method;

+        this.cachedClass = clazz;

+        alreadySetAccessible = Modifier.isPublic(method.getModifiers()) && Modifier.isPublic(clazz.getModifiers());

+    }

+

+    public CachedMethod(Method method) {

+        this(ReflectionCache.getCachedClass(method.getDeclaringClass()),method);

+    }

+

+    public static CachedMethod find(Method method) {

+        CachedMethod[] methods = ReflectionCache.getCachedClass(method.getDeclaringClass()).getMethods();

+//        for (int i = 0; i < methods.length; i++) {

+//            CachedMethod cachedMethod = methods[i];

+//            if (cachedMethod.cachedMethod.equals(method))

+//                return cachedMethod;

+//        }

+//        return null;

+        int i = Arrays.binarySearch(methods, method);

+        if (i < 0)

+          return null;

+

+        return methods[i];

+    }

+

+    Class[] getPT() {

+        return cachedMethod.getParameterTypes();

+    }

+

+    public String getName() {

+        return cachedMethod.getName();

+    }

+

+    public String getDescriptor() {

+        return BytecodeHelper.getMethodDescriptor(getReturnType(), getNativeParameterTypes());

+    }

+

+    public Class getDeclaringClass() {

+        return cachedClass.getCachedClass();

+    }

+

+    public Class getReturnType() {

+        return cachedMethod.getReturnType();

+    }

+

+    public int getParamsCount() {

+        return getParameterTypes().length;

+    }

+

+    public int getModifiers() {

+        return cachedMethod.getModifiers();

+    }

+

+

+    public String getSignature() {

+        return getName() + getDescriptor();

+    }

+

+    public Method setAccessible() {

+        if ( !alreadySetAccessible ) {

+            AccessController.doPrivileged(new PrivilegedAction() {

+                public Object run() {

+                    cachedMethod.setAccessible(true);

+                    return null;

+                }

+            });

+        }

+        alreadySetAccessible = true;

+        return cachedMethod;

+    }

+

+    public boolean isStatic() {

+        return MethodHelper.isStatic(cachedMethod);

+    }

+

+    public void setMethodIndex(int i) {

+        methodIndex = i;

+    }

+

+    public int getMethodIndex() {

+        return methodIndex;

+    }

+

+    public boolean canBeCalledByReflector () {

+            if (!Modifier.isPublic(cachedClass.getModifiers()))

+                return false;

+

+            if (!Modifier.isPublic(getModifiers()))

+              return false;

+

+            getParameterTypes();

+            for (int i = 0; i != parameterTypes.length; ++i) {

+                if (!parameterTypes[i].isPrimitive && !Modifier.isPublic(parameterTypes[i].getModifiers()))

+                  return false;

+            }

+        return true;

+    }

+

+    public int compareTo(Object o) {

+      if (o instanceof CachedMethod)

+        return compareToCachedMethod((CachedMethod)o);

+      else

+        return compareToMethod((Method)o);

+    }

+

+    private int compareToCachedMethod(CachedMethod m) {

+        if (m == null)

+         return -1;

+

+        final int strComp = getName().compareTo(m.getName());

+        if (strComp != 0)

+          return strComp;

+

+        final int retComp = getReturnType().getName().compareTo(m.getReturnType().getName());

+        if (retComp != 0)

+          return retComp;

+

+        CachedClass[]  params =   getParameterTypes();

+        CachedClass [] mparams = m.getParameterTypes();

+

+        final int pd = params.length - mparams.length;

+        if (pd != 0)

+          return pd;

+

+        for (int i = 0; i != params.length; ++i)

+        {

+            final int nameComp = params[i].getName().compareTo(mparams[i].getName());

+            if (nameComp != 0)

+              return nameComp;

+        }

+

+        throw new RuntimeException("Should never happen");

+    }

+

+    private int compareToMethod(Method m) {

+        if (m == null)

+         return -1;

+

+        final int strComp = getName().compareTo(m.getName());

+        if (strComp != 0)

+          return strComp;

+

+        final int retComp = getReturnType().getName().compareTo(m.getReturnType().getName());

+        if (retComp != 0)

+          return retComp;

+

+        CachedClass[]  params =   getParameterTypes();

+        Class [] mparams = m.getParameterTypes();

+

+        final int pd = params.length - mparams.length;

+        if (pd != 0)

+          return pd;

+

+        for (int i = 0; i != params.length; ++i)

+        {

+            final int nameComp = params[i].getName().compareTo(mparams[i].getName());

+            if (nameComp != 0)

+              return nameComp;

+        }

+

+        return 0;

+    }

+

+    public boolean equals(Object o) {

+        return (o instanceof CachedMethod && cachedMethod.equals(((CachedMethod)o).cachedMethod))

+                || (o instanceof Method && cachedMethod.equals(o));

+    }

+

+    public int hashCode() {

+        if (hashCode == 0) {

+           hashCode = cachedMethod.hashCode();

+           if (hashCode == 0)

+             hashCode = 0xcafebebe;

+        }

+        return hashCode;

+    }

+

+    public String toString() {

+        return cachedMethod.toString();

+    }

+

+    // Should have lock for declaringClass

+    public MetaMethod getReflectionMetaMethod() {

+        if (reflectionMetaMethod == null) {

+          reflectionMetaMethod = new ReflectionMetaMethod(this);

+        }

+        return reflectionMetaMethod;

+    }

+}

+

diff --git a/groovy/src/main/org/codehaus/groovy/reflection/ComplexKeyHashMap.java b/groovy/src/main/org/codehaus/groovy/reflection/ComplexKeyHashMap.java
new file mode 100644
index 0000000..3226858
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/ComplexKeyHashMap.java
@@ -0,0 +1,174 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.reflection;

+

+import java.util.NoSuchElementException;

+

+public class ComplexKeyHashMap

+{

+  public static class Entry {

+    public int hash;

+    public Entry next;

+    public Object value;

+

+    public Object getValue() {

+      return value;

+    }

+

+    public void setValue(Object value) {

+      this.value = value;

+    }

+  }

+

+  protected Entry table [];

+

+  protected static final int DEFAULT_CAPACITY = 32;

+  protected static final int MINIMUM_CAPACITY = 4;

+  protected static final int MAXIMUM_CAPACITY = 1 << 28;

+

+  protected int size;

+  protected transient int threshold;

+

+  public ComplexKeyHashMap() {

+      init(DEFAULT_CAPACITY);

+  }

+

+    public ComplexKeyHashMap(boolean b) {

+    }

+

+  public ComplexKeyHashMap(int expectedMaxSize) {

+    init (capacity(expectedMaxSize));

+  }

+

+  public static int hash(int h) {

+    h += ~(h << 9);

+    h ^=  (h >>> 14);

+    h +=  (h << 4);

+    h ^=  (h >>> 10);

+    return h;

+  }

+

+  public int size() {

+      return size;

+  }

+

+  public boolean isEmpty() {

+      return size == 0;

+  }

+

+  public void clear() {

+      Object[] tab = table;

+      for (int i = 0; i < tab.length; i++)

+          tab[i] = null;

+      size = 0;

+  }

+

+  public void init(int initCapacity) {

+      threshold = (initCapacity * 6)/8;

+      table = new Entry[initCapacity];

+  }

+

+  public void resize(int newLength) {

+      Entry[] oldTable = table;

+      int oldLength = table.length;

+

+      Entry[] newTable = new Entry[newLength];

+

+      for (int j = 0; j < oldLength; j++) {

+

+        for (Entry e = oldTable [j]; e != null;) {

+          Entry next = e.next;

+          int index = e.hash & (newLength-1);

+

+          e.next = newTable[index];

+          newTable [index] = e;

+

+          e = next;

+        }

+      }

+

+      table = newTable;

+      threshold = (6 * newLength) / 8;

+  }

+

+  private int capacity(int expectedMaxSize) {

+      // Compute min capacity for expectedMaxSize given a load factor of 3/4

+      int minCapacity = (8 * expectedMaxSize)/6;

+

+      // Compute the appropriate capacity

+      int result;

+      if (minCapacity > MAXIMUM_CAPACITY || minCapacity < 0) {

+          result = MAXIMUM_CAPACITY;

+      } else {

+          result = MINIMUM_CAPACITY;

+          while (result < minCapacity)

+              result <<= 1;

+      }

+      return result;

+  }

+

+  public interface EntryIterator {

+      boolean hasNext ();

+      Entry   next ();

+  }

+

+

+    public ComplexKeyHashMap.Entry[] getTable() {

+        return table;

+    }

+

+  public EntryIterator  getEntrySetIterator() {

+        return new EntryIterator() {

+            Entry next;	// next entry to return

+            int index;		// current slot

+            Entry current;	// current entry

+

+            {

+                Entry[] t = table;

+                int i = t.length;

+                Entry n = null;

+                if (size != 0) { // advance to first entry

+                    while (i > 0 && (n = t[--i]) == null) {}

+                }

+                next = n;

+                index = i;

+            }

+

+            public boolean hasNext() {

+                return next != null;

+            }

+

+            public Entry next() {

+                return nextEntry();

+            }

+

+            Entry nextEntry() {

+                Entry e = next;

+                if (e == null)

+                    throw new NoSuchElementException();

+

+                Entry n = e.next;

+                Entry[] t = table;

+                int i = index;

+                while (n == null && i > 0)

+                    n = t[--i];

+                index = i;

+                next = n;

+                return current = e;

+            }

+        };

+  }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/reflection/DoubleKeyHashMap.java b/groovy/src/main/org/codehaus/groovy/reflection/DoubleKeyHashMap.java
new file mode 100644
index 0000000..04a72ca
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/DoubleKeyHashMap.java
@@ -0,0 +1,90 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.reflection;

+

+public class DoubleKeyHashMap extends ComplexKeyHashMap

+{

+  public static class Entry extends ComplexKeyHashMap.Entry{

+    public Object key1, key2;

+  }

+

+  public final Object get(Object key1, Object key2) {

+    int h = hash (31*key1.hashCode()+key2.hashCode());

+    ComplexKeyHashMap.Entry e = table [h & (table.length-1)];

+    for (; e != null; e = e.next)

+      if (e.hash == h && checkEquals((Entry) e, key1, key2))

+        return e;

+

+    return null;

+  }

+

+    public boolean checkEquals(ComplexKeyHashMap.Entry e, Object key1, Object key2) {

+        Entry ee = (Entry) e;

+        return ee.key1 == key1 && ee.key2 == key2;

+    }

+

+  public Entry getOrPut(Object key1, Object key2)

+  {

+    int h = hash (31*key1.hashCode()+key2.hashCode());

+    final int index = h & (table.length - 1);

+    ComplexKeyHashMap.Entry e = table [index];

+    for (; e != null; e = e.next)

+      if (e.hash == h && checkEquals( e, key1, key2))

+        return (Entry) e;

+

+    ComplexKeyHashMap.Entry entry = createEntry(key1, key2, h, index);

+    table [index] = entry;

+

+    if ( ++size == threshold )

+      resize(2*table.length);

+

+    return (Entry) entry;

+  }

+

+  private ComplexKeyHashMap.Entry createEntry(Object key1, Object key2, int h, int index)

+  {

+    Entry entry = createEntry ();

+    entry.next = table [index];

+    entry.hash = h;

+    entry.key1 = key1;

+    entry.key2 = key2;

+    return entry;

+  }

+

+  public Entry createEntry() {

+      return new Entry ();

+  }

+

+  public final ComplexKeyHashMap.Entry remove(Object key1, Object key2) {

+    int h = hash (31*key1.hashCode()+key2.hashCode());

+    int index = h & (table.length -1);

+    for (ComplexKeyHashMap.Entry e = table [index], prev = null; e != null; prev = e, e = e.next ) {

+      if (e.hash == h && checkEquals((Entry) e, key1, key2)) {

+        if (prev == null)

+          table [index] = e.next;

+        else

+          prev.next = e.next;

+        size--;

+

+        e.next = null;

+        return e;

+      }

+    }

+

+    return null;

+  }

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/reflection/FastArray.java b/groovy/src/main/org/codehaus/groovy/reflection/FastArray.java
new file mode 100644
index 0000000..870820d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/FastArray.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.reflection;
+
+import java.util.AbstractList;
+import java.util.Collection;
+import java.util.List;
+
+public class FastArray implements Cloneable {
+    private Object[] data;
+    public int size;
+    public static final FastArray EMPTY_LIST = new FastArray(0);
+
+    public FastArray(int initialCapacity) {
+        data = new Object[initialCapacity];
+    }
+
+    public FastArray() {
+       this (8);
+    }
+
+    public FastArray(Collection c) {
+        this (c.toArray());
+    }
+
+    public FastArray(Object[] objects) {
+        data = objects;
+        size = objects.length;
+    }
+
+    public Object get(int index) {
+        return data [index];
+    }
+
+    public void add(Object o) {
+        if (size == data.length) {
+            Object [] newData = new Object[size == 0 ? 8 : size*2];
+            System.arraycopy(data, 0, newData, 0, size);
+            data = newData;
+        }
+        data [size++] = o;
+    }
+
+    public void set(int index, Object o) {
+        data [index] = o;
+    }
+
+    public int size() {
+        return size;
+    }
+
+    public void clear() {
+        data = new Object[data.length];
+        size = 0;
+    }
+
+    public void addAll(FastArray newData) {
+        addAll(newData.data, newData.size);
+    }
+
+    public void addAll(Object [] newData, int size) {
+        if (size == 0)
+          return;
+        final int newSize = this.size + size;
+        if (newSize > data.length) {
+            Object nd [] = new Object [newSize];
+            System.arraycopy(data, 0, nd, 0, this.size);
+            data = nd;
+        }
+        System.arraycopy(newData, 0, data, this.size, size);
+        this.size = newSize;
+    }
+
+    public FastArray copy() {
+        final Object[] newData = new Object[size];
+        System.arraycopy(data, 0, newData, 0, size);
+        return new FastArray(newData);
+    }
+
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    public void addAll(List coll) {
+        final Object[] newData = coll.toArray();
+        addAll(newData, newData.length);
+    }
+
+    public void remove(int index) {
+        int numMoved = size - index - 1;
+        if (numMoved > 0)
+            System.arraycopy(data, index+1, data, index, numMoved);
+        data[--size] = null;
+    }
+
+    public List toList () {
+        return new AbstractList() {
+
+            public Object get(int index) {
+                return FastArray.this.get(index);
+            }
+
+            public int size() {
+                return size;
+            }
+        };
+    }
+
+    public Object[] getArray() {
+        return data;
+    }
+
+    public String toString() {
+        if (size() == 0) return "[]";
+        return toList().toString();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/reflection/ParameterTypes.java b/groovy/src/main/org/codehaus/groovy/reflection/ParameterTypes.java
new file mode 100644
index 0000000..ae90f7d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/ParameterTypes.java
@@ -0,0 +1,180 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.reflection;

+

+import org.codehaus.groovy.GroovyBugError;

+import org.codehaus.groovy.runtime.MetaClassHelper;

+

+import java.lang.reflect.Array;

+

+public class ParameterTypes

+{

+  protected Class [] nativeParamTypes;

+  protected CachedClass [] parameterTypes;

+

+    public ParameterTypes () {

+    }

+

+    public ParameterTypes(Class pt []) {

+        nativeParamTypes = pt;

+    }

+

+    public ParameterTypes(CachedClass[] parameterTypes) {

+        this.parameterTypes = parameterTypes;

+    }

+

+    public CachedClass[] getParameterTypes() {

+      if (parameterTypes == null) {

+        synchronized (this) {

+          if (parameterTypes != null)

+            return parameterTypes;

+

+          Class [] npt = nativeParamTypes == null ? getPT() : nativeParamTypes;

+

+          CachedClass [] pt = new CachedClass [npt.length];

+          for (int i = 0; i != npt.length; ++i)

+            pt[i] = ReflectionCache.getCachedClass(npt[i]);

+

+          nativeParamTypes = npt;

+          parameterTypes = pt;

+        }

+      }

+

+      return parameterTypes;

+  }

+

+    public Class[] getNativeParameterTypes() {

+        if (nativeParamTypes == null) {

+          synchronized (this) {

+            if (nativeParamTypes != null)

+              return nativeParamTypes;

+

+            Class [] npt;

+            if (parameterTypes != null) {

+                npt = new Class [parameterTypes.length];

+                for (int i = 0; i != parameterTypes.length; ++i) {

+                    npt[i] = parameterTypes[i].getCachedClass();

+                }

+            }

+            else

+              npt = getPT ();

+            nativeParamTypes = npt;

+          }

+        }

+        return nativeParamTypes;

+    }

+

+    Class[] getPT() { return null; }

+

+    public boolean isVargsMethod(Object[] arguments) {

+        getParameterTypes();

+        if (parameterTypes.length == 0) return false;

+        final int lenMinus1 = parameterTypes.length - 1;

+        if (!parameterTypes[lenMinus1].isArray) return false;

+        // -1 because the varg part is optional

+        if (lenMinus1 == arguments.length) return true;

+        if (lenMinus1 > arguments.length) return false;

+        if (arguments.length > parameterTypes.length) return true;

+

+        // only case left is arguments.length == parameterTypes.length

+        Object last = arguments[arguments.length - 1];

+        if (last == null) return true;

+        Class clazz = last.getClass();

+        return !clazz.equals(parameterTypes[lenMinus1].getCachedClass());

+

+    }

+

+    public Object[] coerceArgumentsToClasses(Object[] argumentArray) {

+        getParameterTypes();

+        // correct argumentArray's length

+        if (argumentArray == null) {

+            argumentArray = MetaClassHelper.EMPTY_ARRAY;

+        } else if (parameterTypes.length == 1 && argumentArray.length == 0) {

+            if (isVargsMethod(argumentArray)) {

+                argumentArray = new Object[]{Array.newInstance(parameterTypes[0].getCachedClass().getComponentType(), 0)};

+            }

+            else

+                argumentArray = MetaClassHelper.ARRAY_WITH_NULL;

+        } else if (isVargsMethod(argumentArray)) {

+            argumentArray = fitToVargs(argumentArray, parameterTypes);

+        }

+

+        //correct Type

+        for (int i = 0; i < argumentArray.length; i++) {

+            Object argument = argumentArray[i];

+            if (argument == null) continue;

+            CachedClass parameterType = parameterTypes[i];

+            if (ReflectionCache.isAssignableFrom(parameterType.getCachedClass(), argument.getClass())) continue;

+

+            argument = parameterType.coerceGString(argument);

+            argument = parameterType.coerceNumber(argument);

+            argument = parameterType.coerceArray(argument);

+            argumentArray[i] = argument;

+        }

+        return argumentArray;

+    }

+

+    /**

+     * this method is called when the number of arguments to a method is greater than 1

+     * and if the method is a vargs method. This method will then transform the given

+     * arguments to make the method callable

+     *

+     * @param argumentArray the arguments used to call the method

+     * @param paramTypes    the types of the paramters the method takes

+     */

+    private static Object[] fitToVargs(Object[] argumentArray, CachedClass[] paramTypes) {

+        Class vargsClass = ReflectionCache.autoboxType(paramTypes[paramTypes.length - 1].getCachedClass().getComponentType());

+

+        if (argumentArray.length == paramTypes.length - 1) {

+            // the vargs argument is missing, so fill it with an empty array

+            Object[] newArgs = new Object[paramTypes.length];

+            System.arraycopy(argumentArray, 0, newArgs, 0, argumentArray.length);

+            Object vargs = MetaClassHelper.makeArray(null, vargsClass, 0);

+            newArgs[newArgs.length - 1] = vargs;

+            return newArgs;

+        } else if (argumentArray.length == paramTypes.length) {

+            // the number of arguments is correct, but if the last argument

+            // is no array we have to wrap it in a array. If the last argument

+            // is null, then we don't have to do anything

+            Object lastArgument = argumentArray[argumentArray.length - 1];

+            if (lastArgument != null && !lastArgument.getClass().isArray()) {

+                // no array so wrap it

+                Object wrapped = MetaClassHelper.makeArray(lastArgument, vargsClass, 1);

+                System.arraycopy(argumentArray, argumentArray.length - 1, wrapped, 0, 1);

+                Object[] newArgs = new Object[paramTypes.length];

+                System.arraycopy(argumentArray, 0, newArgs, 0, paramTypes.length - 1);

+                newArgs[newArgs.length - 1] = wrapped;

+                return newArgs;

+            } else {

+                // we may have to box the arguemnt!

+                return argumentArray;

+            }

+        } else if (argumentArray.length > paramTypes.length) {

+            // the number of arguments is too big, wrap all exceeding elements

+            // in an array, but keep the old elements that are no vargs

+            Object[] newArgs = new Object[paramTypes.length];

+            // copy arguments that are not a varg

+            System.arraycopy(argumentArray, 0, newArgs, 0, paramTypes.length - 1);

+            // create a new array for the vargs and copy them

+            int numberOfVargs = argumentArray.length - paramTypes.length;

+            Object vargs = MetaClassHelper.makeCommonArray(argumentArray, paramTypes.length - 1, vargsClass);

+            newArgs[newArgs.length - 1] = vargs;

+            return newArgs;

+        } else {

+            throw new GroovyBugError("trying to call a vargs method without enough arguments");

+        }

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/reflection/ReflectionCache.java b/groovy/src/main/org/codehaus/groovy/reflection/ReflectionCache.java
new file mode 100644
index 0000000..6fb0833
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/ReflectionCache.java
@@ -0,0 +1,151 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.reflection;

+

+import java.lang.ref.SoftReference;

+import java.util.HashMap;

+import java.util.Map;

+import java.util.WeakHashMap;

+

+public class ReflectionCache {

+    private static Map primitiveTypesMap = new HashMap();

+

+    static {

+        primitiveTypesMap.put(byte.class, Byte.class);

+        primitiveTypesMap.put(boolean.class, Boolean.class);

+        primitiveTypesMap.put(char.class, Character.class);

+        primitiveTypesMap.put(double.class, Double.class);

+        primitiveTypesMap.put(float.class, Float.class);

+        primitiveTypesMap.put(int.class, Integer.class);

+        primitiveTypesMap.put(long.class, Long.class);

+        primitiveTypesMap.put(short.class, Short.class);

+    }

+

+    public static Class autoboxType(Class type) {

+        final Class res = (Class) primitiveTypesMap.get(type);

+        return res == null ? type : res;

+/*

+    final String name = type.getName();

+    switch (name.charAt(0)) {

+       case 'b':

+           if ("boolean".equals(name))

+             return Boolean.class;

+           else

+             if ("byte".equals(name))

+               return Byte.class;

+             else

+               return null;

+

+       case 'c':

+         return "char".equals(name) ? Character.class : null;

+

+       case 'd':

+          return "double".equals(name) ? Double.class : null;

+

+      case 'f':

+        return "float".equals(name) ? Float.class : null;

+

+      case 'i':

+        return "int".equals(name) ? Integer.class : null;

+

+      case 'l':

+        return "long".equals(name) ? Long.class : null;

+

+      case 's':

+        return "short".equals(name) ? Short.class : null;

+

+       default:

+         return null;

+    }

+*/

+    }

+

+    static TripleKeyHashMap mopNames = new TripleKeyHashMap();

+

+    public static String getMOPMethodName(CachedClass declaringClass, String name, boolean useThis) {

+        TripleKeyHashMap.Entry mopNameEntry = mopNames.getOrPut(declaringClass, name, Boolean.valueOf(useThis));

+        if (mopNameEntry.value == null) {

+            mopNameEntry.value = new StringBuffer().append(useThis ? "this$" : "super$").append(declaringClass.getSuperClassDistance()).append("$").append(name).toString();

+        }

+        return (String) mopNameEntry.value;

+    }

+

+    static final Map /*<Class,SoftReference<CachedClass>>*/ CACHED_CLASS_MAP = new WeakHashMap();

+

+    public static boolean isArray(Class klazz) {

+        CachedClass cachedClass = getCachedClass(klazz);

+        return cachedClass.isArray;

+    }

+

+    static WeakDoubleKeyHashMap assignableMap = new WeakDoubleKeyHashMap();

+

+    public static boolean isAssignableFrom(Class klazz, Class aClass) {

+        WeakDoubleKeyHashMap.Entry val = assignableMap.getOrPut(klazz, aClass);

+        if (val.value == null) {

+            val.value = Boolean.valueOf(klazz.isAssignableFrom(aClass));

+        }

+        return ((Boolean)val.value).booleanValue();

+    }

+

+    static boolean arrayContentsEq(Object[] a1, Object[] a2) {

+        if (a1 == null) {

+            return a2 == null || a2.length == 0;

+        }

+

+        if (a2 == null) {

+            return a1.length == 0;

+        }

+

+        if (a1.length != a2.length) {

+            return false;

+        }

+

+        for (int i = 0; i < a1.length; i++) {

+            if (a1[i] != a2[i]) {

+                return false;

+            }

+        }

+

+        return true;

+    }

+

+    public static final CachedClass OBJECT_CLASS = new CachedClass(Object.class) {

+        public synchronized CachedClass getCachedSuperClass() {

+            return null;

+        }

+    };

+

+    public static final CachedClass OBJECT_ARRAY_CLASS = getCachedClass(Object[].class);

+

+    public static CachedClass getCachedClass(Class klazz) {

+        if (klazz == null)

+            return null;

+

+        if (klazz == Object.class)

+            return OBJECT_CLASS;

+

+        CachedClass cachedClass;

+        synchronized (CACHED_CLASS_MAP) {

+            SoftReference ref = (SoftReference) CACHED_CLASS_MAP.get(klazz);

+            if (ref == null || (cachedClass = (CachedClass) ref.get()) == null) {

+                cachedClass = new CachedClass(klazz);

+                CACHED_CLASS_MAP.put(klazz, new SoftReference(cachedClass));

+            }

+        }

+        return cachedClass;

+    }

+

+            }

diff --git a/groovy/src/main/org/codehaus/groovy/reflection/SingleKeyHashMap.java b/groovy/src/main/org/codehaus/groovy/reflection/SingleKeyHashMap.java
new file mode 100644
index 0000000..bb961c1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/SingleKeyHashMap.java
@@ -0,0 +1,158 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.reflection;

+

+public class SingleKeyHashMap extends ComplexKeyHashMap

+{

+    public static class Entry extends ComplexKeyHashMap.Entry{

+        public Object key;

+

+        public Object getKey() {

+              return key;

+        }

+    }

+

+    public SingleKeyHashMap () {

+        super ();

+    }

+

+    public SingleKeyHashMap (boolean b) {

+        super (false);

+    }

+

+    public boolean containsKey(String name) {

+        return get(name) != null;

+    }

+

+    public void put(Object key, Object value) {

+        getOrPut(key).value = value;

+    }

+

+  public final Object get(Object key) {

+    int h = hash (key.hashCode());

+    ComplexKeyHashMap.Entry e = table [h & (table.length-1)];

+    for (; e != null; e = e.next)

+        if (e.hash == h && ((Entry) e).key.equals(key))

+          return ((Entry)e).value;

+

+    return null;

+  }

+

+  public Entry getOrPut(Object key)

+  {

+    int h = hash (key.hashCode());

+      final ComplexKeyHashMap.Entry[] t = table;

+      final int index = h & (t.length - 1);

+    ComplexKeyHashMap.Entry e = t[index];

+    for (; e != null; e = e.next)

+        if (e.hash == h && ((Entry) e).key.equals(key))

+          return (Entry) e;

+

+      Entry entry = new Entry();

+      entry.next = t [index];

+      entry.hash = h;

+      entry.key = key;

+      t[index] = entry;

+

+    if ( ++size == threshold )

+      resize(2* t.length);

+

+    return entry;

+  }                                                    

+

+    public Entry getOrPutEntry(Entry element) {

+        Object key = element.key;

+        int h = element.hash;

+          final ComplexKeyHashMap.Entry[] t = table;

+          final int index = h & (t.length - 1);

+        ComplexKeyHashMap.Entry e = t[index];

+        for (; e != null; e = e.next)

+            if (e.hash == h && ((Entry) e).key.equals(key))

+              return (Entry) e;

+

+          Entry entry = new Entry();

+          entry.next = t [index];

+          entry.hash = h;

+          entry.key = key;

+          t[index] = entry;

+

+        if ( ++size == threshold )

+          resize(2* t.length);

+

+        return entry;

+    }

+

+    public Entry putCopyOfUnexisting(Entry ee)

+    {

+      int h = ee.hash;

+      final ComplexKeyHashMap.Entry[] t = table;

+      final int index = h & (t.length - 1);

+

+        Entry entry = new Entry();

+        entry.next = t [index];

+        entry.hash = h;

+        entry.key = ee.key;

+        entry.value = ee.value;

+        t[index] = entry;

+

+      if ( ++size == threshold )

+        resize(2* t.length);

+

+      return entry;

+    }

+

+    public final ComplexKeyHashMap.Entry remove(Object key) {

+    int h = hash (key.hashCode());

+    int index = h & (table.length -1);

+    for (ComplexKeyHashMap.Entry e = table [index], prev = null; e != null; prev = e, e = e.next ) {

+        if (e.hash == h && ((Entry) e).key.equals(key)) {

+        if (prev == null)

+          table [index] = e.next;

+        else

+          prev.next = e.next;

+        size--;

+

+        e.next = null;

+        return e;

+      }

+    }

+

+    return null;

+  }

+

+  public static SingleKeyHashMap copy (SingleKeyHashMap dst, SingleKeyHashMap src, Copier copier) {

+      dst.threshold = src.threshold;

+      dst.size = src.size;

+      final int len = src.table.length;

+      final ComplexKeyHashMap.Entry[] t = new ComplexKeyHashMap.Entry[len], tt = src.table;

+      for (int i = 0; i != len; ++i) {

+          for (Entry e = (Entry) tt[i]; e != null; e = (Entry) e.next) {

+              Entry ee = new Entry();

+              ee.hash = e.hash;

+              ee.key = e.key;

+              ee.value = copier.copy(e.value);

+              ee.next = t [i];

+              t [i] = ee;

+          }

+      }

+      dst.table = t;

+      return dst;

+  }

+

+  public static interface Copier {

+      Object copy (Object value);

+  }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/reflection/TripleKeyHashMap.java b/groovy/src/main/org/codehaus/groovy/reflection/TripleKeyHashMap.java
new file mode 100644
index 0000000..fab52f3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/TripleKeyHashMap.java
@@ -0,0 +1,83 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.reflection;

+

+public class TripleKeyHashMap extends ComplexKeyHashMap

+{

+  public static class Entry extends ComplexKeyHashMap.Entry{

+    public Object key1, key2, key3;

+  }

+

+  public final Object get(Object key1, Object key2, Object key3) {

+    int h = hash (31*(31*key1.hashCode()+key2.hashCode())+key3.hashCode());

+    ComplexKeyHashMap.Entry e = table [h & (table.length-1)];

+    for (; e != null; e = e.next)

+      if (e.hash == h && checkEquals((Entry) e, key1, key2, key3))

+        return e;

+

+    return null;

+  }

+

+  public boolean checkEquals(Entry e, Object key1, Object key2, Object key3) {

+      return e.key1.equals(key1) && e.key2.equals(key2) && e.key3.equals(key3);

+  }

+

+  public Entry getOrPut(Object key1, Object key2, Object key3)

+  {

+    int h = hash (31*(31*key1.hashCode()+key2.hashCode())+key3.hashCode());

+    final int index = h & (table.length - 1);

+    ComplexKeyHashMap.Entry e = table [index];

+    for (; e != null; e = e.next)

+      if (e.hash == h && checkEquals((Entry) e, key1, key2, key3))

+        return (Entry) e;

+

+    Entry entry = createEntry ();

+    entry.next = table [index];

+    entry.hash = h;

+    entry.key1 = key1;

+    entry.key2 = key2;

+    entry.key3 = key3;

+    table [index] = entry;

+

+    if ( ++size == threshold )

+      resize(2*table.length);

+

+    return entry;

+  }

+

+  public Entry createEntry() {

+      return new Entry ();

+  }

+

+  public final ComplexKeyHashMap.Entry remove(Object key1, Object key2, Object key3) {

+    int h = hash (31*(31*key1.hashCode()+key2.hashCode())+key3.hashCode());

+    int index = h & (table.length -1);

+    for (ComplexKeyHashMap.Entry e = table [index], prev = null; e != null; prev = e, e = e.next ) {

+      if (e.hash == h && checkEquals((Entry) e, key1, key2, key3)) {

+        if (prev == null)

+          table [index] = e.next;

+        else

+          prev.next = e.next;

+        size--;

+

+        e.next = null;

+        return e;

+      }

+    }

+

+    return null;

+  }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/reflection/WeakDoubleKeyHashMap.java b/groovy/src/main/org/codehaus/groovy/reflection/WeakDoubleKeyHashMap.java
new file mode 100644
index 0000000..1afffff
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/WeakDoubleKeyHashMap.java
@@ -0,0 +1,145 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.reflection;

+

+import java.lang.ref.ReferenceQueue;

+import java.lang.ref.WeakReference;

+

+public class WeakDoubleKeyHashMap extends ComplexKeyHashMap

+{

+  private static class Ref extends WeakReference {

+      public Ref(Object referent, ReferenceQueue q, Entry entry) {

+          super(referent, q);

+          this.entry = entry;

+      }

+

+      Entry entry;

+  }

+

+  public static class Entry extends ComplexKeyHashMap.Entry{

+    public Ref key1, key2;

+  }

+

+  private final ReferenceQueue queue = new ReferenceQueue();

+

+    private void expungeStaleEntries() {

+	    Ref r;

+        while ( (r = (Ref) queue.poll()) != null) {

+            Entry e = r.entry;

+            if (e == null)

+              continue;

+

+            r.entry = null;

+            e.key1 = e.key2 = null;

+

+            int h = e.hash;

+            int i = h & (table.length-1);

+

+            Entry prev = (Entry) table[i];

+            Entry p = prev;

+            while (p != null) {

+                Entry next = (Entry) p.next;

+                if (p == e) {

+                    if (prev == e)

+                        table[i] = next;

+                    else

+                        prev.next = next;

+                    e.next = null;  // Help GC

+                    e.value = null; //  "   "

+                    size--;

+                    break;

+                }

+                prev = p;

+                p = next;

+            }

+        }

+    }

+

+  public final Object get(Object key1, Object key2) {

+    expungeStaleEntries();

+    int h = hash (31*key1.hashCode()+key2.hashCode());

+    ComplexKeyHashMap.Entry e = table [h & (table.length-1)];

+    for (; e != null; e = e.next)

+      if (e.hash == h && checkEquals((Entry) e, key1, key2))

+        return e;

+

+    return null;

+  }

+

+    public boolean checkEquals(ComplexKeyHashMap.Entry e, Object key1, Object key2) {

+        Entry ee = (Entry) e;

+        return ee.key1.get() == key1 && ee.key2.get() == key2;

+    }

+

+  public Entry getOrPut(Object key1, Object key2)

+  {

+    expungeStaleEntries();

+    int h = hash (31*key1.hashCode()+key2.hashCode());

+    final int index = h & (table.length - 1);

+    ComplexKeyHashMap.Entry e = table [index];

+    for (; e != null; e = e.next)

+      if (e.hash == h && checkEquals( e, key1, key2))

+        return (Entry) e;

+

+    ComplexKeyHashMap.Entry entry = createEntry(key1, key2, h, index);

+    table [index] = entry;

+

+    if ( ++size == threshold )

+      resize(2*table.length);

+

+    return (Entry) entry;

+  }

+

+  private ComplexKeyHashMap.Entry createEntry(Object key1, Object key2, int h, int index)

+  {

+    Entry entry = createEntry ();

+    entry.next = table [index];

+    entry.hash = h;

+    entry.key1 = new Ref(key1, queue, entry);

+    entry.key2 = new Ref(key2, queue, entry);

+    return entry;

+  }

+

+  public Entry createEntry() {

+      return new Entry ();

+  }

+

+    public int size() {

+        expungeStaleEntries();

+        return super.size();

+    }

+

+    public final ComplexKeyHashMap.Entry remove(Object key1, Object key2) {

+    expungeStaleEntries();

+    int h = hash (31*key1.hashCode()+key2.hashCode());

+    int index = h & (table.length -1);

+    for (ComplexKeyHashMap.Entry e = table [index], prev = null; e != null; prev = e, e = e.next ) {

+      if (e.hash == h && checkEquals((Entry) e, key1, key2)) {

+        if (prev == null)

+          table [index] = e.next;

+        else

+          prev.next = e.next;

+        size--;

+

+        e.next = null;

+        return e;

+      }

+    }

+

+    return null;

+  }

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/reflection/package.html b/groovy/src/main/org/codehaus/groovy/reflection/package.html
new file mode 100644
index 0000000..ac25b59
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/reflection/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.reflection.*</title>
+  </head>
+  <body>
+    <p>Internal classes for assisting with reflection.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/ClassExtender.java b/groovy/src/main/org/codehaus/groovy/runtime/ClassExtender.java
new file mode 100644
index 0000000..d98f828
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/ClassExtender.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * A helper class used by the runtime to allow Groovy classes to be extended at runtime
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ClassExtender {
+    private Map variables;
+    private Map methods;
+
+    public synchronized Object get(String name) {
+        if (variables != null) {
+            return variables.get(name);
+        }
+        return null;
+    }
+
+    public synchronized void set(String name, Object value) {
+        if (variables == null) {
+            variables = createMap();
+        }
+        variables.put(name, value);
+    }
+
+    public synchronized void remove(String name) {
+        if (variables != null) {
+            variables.remove(name);
+        }
+    }
+
+    public void call(String name, Object params) {
+        Closure closure = null;
+        synchronized (this) {
+            if (methods != null) {
+                closure = (Closure) methods.get(name);
+            }
+        }
+        if (closure != null) {
+            closure.call(params);
+        }
+        /*
+        else {
+            throw DoesNotUnderstandException();
+        }
+        */
+    }
+
+    public synchronized void addMethod(String name, Closure closure) {
+        if (methods == null) {
+            methods = createMap();
+        }
+        methods.put(name, methods);
+    }
+
+    public synchronized void removeMethod(String name) {
+        if (methods != null) {
+            methods.remove(name);
+        }
+    }
+
+    protected Map createMap() {
+        return new HashMap();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/ConversionHandler.java b/groovy/src/main/org/codehaus/groovy/runtime/ConversionHandler.java
new file mode 100644
index 0000000..00c86e3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/ConversionHandler.java
@@ -0,0 +1,136 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.runtime;

+

+import java.lang.reflect.InvocationHandler;

+import java.lang.reflect.InvocationTargetException;

+import java.lang.reflect.Method;

+import java.lang.reflect.Proxy;

+

+/**

+ * This class is a general adapter to map a call to an Java interface 

+ * to a given delegate.

+ * <p>

+ * @author Ben Yu

+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>

+ */

+public abstract class ConversionHandler implements InvocationHandler {

+    private Object delegate;

+    

+    /**

+     * Creates a ConversionHandler with an deleagte.

+     * @param delegate the delegate

+     * @throws IllegalArgumentException if the given delegate is null

+     */

+    public ConversionHandler(Object delegate) {

+        if (delegate==null) throw new IllegalArgumentException("delegate must not be null");

+        this.delegate = delegate;

+    }

+    

+    /**

+     * gets the delegate.

+     * @return the delegate

+     */

+    public Object getDelegate(){

+        return delegate;

+    }

+    

+    /**

+     * This method is a default implementation for the invoke method

+     * given in Invocationhandler. Any call to an method with an

+     * declaring class that is not Object is redirected to invokeCustom. 

+     * Methods like tostring, equals and hashcode are called on the class

+     * itself instead of the delegate. It is better to overwrite the 

+     * invokeCustom method where the Object related methods are filtered out.

+     * 

+     * @see #invokeCustom(Object, Method, Object[])

+     * @see InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])

+     * 

+     * @param proxy the proxy

+     * @param method the method

+     * @param args the arguments

+     * @return the result of the invocation by method or delegate

+     * @throws Throwable any exception caused by the delegate or the method

+     */

+    public Object invoke(Object proxy, Method method, Object[] args)

+    throws Throwable {

+        if(!isObjectMethod(method)){

+            return invokeCustom(proxy,method,args);

+        }

+        try {

+            return method.invoke(this, args);

+        } catch (InvocationTargetException ite) {

+            throw ite.getTargetException();

+        }  

+    }

+    

+    /**

+     * This method is called for all Methods not defined on Object. 

+     * The delegate should be called here.

+     * 

+     * @param proxy the proxy

+     * @param method the method

+     * @param args the arguments

+     * @return the result of the invocation of the delegate

+     * @throws Throwable any exception causes by the delegate

+     * @see #invoke(Object, Method, Object[])

+     * @see InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])

+     * 

+     */

+    public abstract Object invokeCustom(Object proxy, Method method, Object[] args) throws Throwable;

+    

+    /**

+     * Indicates whether some other object is "equal to" this one.

+     * The delegate is used if the class of the parameter and the

+     * current class are equal. In other cases the method will return 

+     * false. The exact class is here used, if inheritance is needed,

+     * this method must be overwritten. 

+     *        

+     * @see java.lang.Object#equals(java.lang.Object)

+     */

+    public boolean equals(Object obj) {

+        if (obj instanceof Proxy){

+            obj = Proxy.getInvocationHandler(obj);

+        }

+        

+        if (obj instanceof ConversionHandler){

+            return (((ConversionHandler)obj).getDelegate()).equals(delegate);

+        } else {

+            return false;

+        }

+    }

+

+    /**

+     * Returns a hash code value for the delegate. 

+     * @see java.lang.Object#hashCode()

+     */

+    public int hashCode() {

+        return delegate.hashCode();

+    }

+    

+    /**

+     * Returns a String version of the delegate.

+     * @see java.lang.Object#toString()

+     */

+    public String toString() {

+        return delegate.toString();

+    }

+    

+    private static boolean isObjectMethod(Method mtd){

+        return mtd.getDeclaringClass().equals(Object.class);

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/runtime/ConvertedClosure.java b/groovy/src/main/org/codehaus/groovy/runtime/ConvertedClosure.java
new file mode 100644
index 0000000..52cd569
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/ConvertedClosure.java
@@ -0,0 +1,51 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.runtime;

+

+import java.lang.reflect.Method;

+import groovy.lang.Closure;

+

+/**

+ * This class is a general adapter to adapt a closure to any Java interface.

+ * <p>

+ * @author Ben Yu

+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>

+ * Jul 27, 2006 3:50:51 PM

+ */

+public class ConvertedClosure extends ConversionHandler {

+    private String methodName;

+    

+    /**

+     * to create a ConvertedClosure object.

+     * @param closure the closure object.

+     */

+    public ConvertedClosure(Closure closure, String method) {

+        super(closure);

+        this.methodName = method;

+    }

+    

+    public ConvertedClosure(Closure closure) {

+        this(closure,null);

+    }

+    

+    public Object invokeCustom(Object proxy, Method method, Object[] args)

+    throws Throwable {

+        if (methodName!=null && !methodName.equals(method.getName())) return null;

+        return ((Closure) getDelegate()).call(args);

+    }

+}

+

diff --git a/groovy/src/main/org/codehaus/groovy/runtime/ConvertedMap.java b/groovy/src/main/org/codehaus/groovy/runtime/ConvertedMap.java
new file mode 100644
index 0000000..602b368
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/ConvertedMap.java
@@ -0,0 +1,50 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.runtime;

+

+import java.lang.reflect.Method;

+import java.util.Map;

+

+import groovy.lang.Closure;

+

+/**

+ * This class is a general adapter to adapt a map of closures to

+ * any Java interface.

+ * <p>

+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>

+ */

+public class ConvertedMap extends ConversionHandler {

+        

+    /**

+     * to create a ConvertedMap object.

+     * @param closures the map of closres

+     */

+    protected ConvertedMap(Map closures) {

+        super(closures);

+    }

+    

+    public Object invokeCustom(Object proxy, Method method, Object[] args)

+    throws Throwable {

+        Map m = (Map) getDelegate();

+        Closure cl = (Closure) m.get(method.getName());

+        return cl.call(args);

+    }

+    

+    public String toString() {

+        return DefaultGroovyMethods.toString((Map) getDelegate());

+    }

+}

+

diff --git a/groovy/src/main/org/codehaus/groovy/runtime/CurriedClosure.java b/groovy/src/main/org/codehaus/groovy/runtime/CurriedClosure.java
new file mode 100644
index 0000000..2793305
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/CurriedClosure.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime;
+
+
+import groovy.lang.Closure;
+ 
+/**
+ * Represents wrapper around a Closure to support currying
+ * 
+ * @author Jochen Theodorou
+ */
+public final class CurriedClosure extends Closure {
+
+    private Object[] curriedParams;
+    
+    public CurriedClosure(Closure uncurriedClosure, Object[] arguments) {
+        super(uncurriedClosure);
+        curriedParams = arguments;
+        maximumNumberOfParameters = uncurriedClosure.getMaximumNumberOfParameters()-arguments.length;
+    }
+    
+    public CurriedClosure(Closure uncurriedClosure, int i) {
+        this(uncurriedClosure, new Object[]{new Integer(i)});
+    }
+
+    public Object[] getUncurriedArguments(Object[] arguments) {
+        final Object newCurriedParams[] = new Object[curriedParams.length + arguments.length];
+        System.arraycopy(curriedParams, 0, newCurriedParams, 0, curriedParams.length);
+        System.arraycopy(arguments, 0, newCurriedParams, curriedParams.length, arguments.length);
+        return newCurriedParams;        
+    }
+    
+    public void setDelegate(Object delegate) {
+        ((Closure)getOwner()).setDelegate(delegate);
+    }
+    
+    public Object clone() {
+        Closure uncurriedClosure = (Closure) ((Closure) getOwner()).clone();
+        return new CurriedClosure(uncurriedClosure,curriedParams);
+    }
+    
+    public Class[] getParameterTypes() {
+        Class[] oldParams = ((Closure)getOwner()).getParameterTypes();
+        Class[] newParams = new Class[oldParams.length-curriedParams.length];
+        System.arraycopy(oldParams, curriedParams.length, newParams, 0, newParams.length);
+        return newParams;  
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/DefaultCachedMethodKey.java b/groovy/src/main/org/codehaus/groovy/runtime/DefaultCachedMethodKey.java
new file mode 100644
index 0000000..709dc22
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/DefaultCachedMethodKey.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import org.codehaus.groovy.reflection.CachedClass;
+
+
+/**
+ * A default implementation of MethodKey
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision: 7922 $
+ */
+public class DefaultCachedMethodKey extends MethodKey{
+
+    private final CachedClass[] parameterTypes;
+
+    public DefaultCachedMethodKey(Class sender, String name, CachedClass[] parameterTypes, boolean isCallToSuper) {
+        super(sender, name,isCallToSuper);
+        this.parameterTypes = parameterTypes;
+    }
+
+    public int getParameterCount() {
+        return parameterTypes.length;
+    }
+
+    public Class getParameterType(int index) {
+        CachedClass c = parameterTypes[index];
+        if (c==null) return Object.class;
+        return c.getCachedClass();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/groovy/src/main/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
new file mode 100644
index 0000000..83d6b59
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -0,0 +1,9079 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.*;
+import groovy.util.*;
+import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.runtime.typehandling.GroovyCastException;
+import org.codehaus.groovy.runtime.typehandling.NumberMath;
+import org.codehaus.groovy.tools.RootLoader;
+import org.w3c.dom.NodeList;
+
+import java.io.*;
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * This class defines all the new groovy methods which appear on normal JDK
+ * classes inside the Groovy environment. Static methods are used with the
+ * first parameter the destination class.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Jeremy Rayner
+ * @author Sam Pullara
+ * @author Rod Cope
+ * @author Guillaume Laforge
+ * @author John Wilson
+ * @author Hein Meling
+ * @author Dierk Koenig
+ * @author Pilho Kim
+ * @author Marc Guillemot
+ * @author Russel Winder
+ * @author bing ran
+ * @author Jochen Theodorou
+ * @author Paul King
+ * @author Michael Baehr
+ * @author Joachim Baumann
+ * @version $Revision$
+ */
+public class DefaultGroovyMethods {
+
+    private static final Logger LOG = Logger.getLogger(DefaultGroovyMethods.class.getName());
+    private static final Integer ONE = new Integer(1);
+
+    /**
+     * Identity check. Since == is overridden in Groovy with the meaning of equality
+     * we need some fallback to check for object identity.  Invoke using the
+     * 'is' operator, like so: <code>def same = (this is that)</code>
+     *
+     * @param self  an object
+     * @param other an object to compare identity with
+     * @return true if self and other are both references to the same
+     *         instance, false otherwise
+     */
+    public static boolean is(Object self, Object other) {
+        return self == other;
+    }
+
+    /**
+     * Allows the closure to be called for the object reference self
+     * synonym for 'with()'.
+     *
+     * @param self    the object to have a closure act upon
+     * @param closure the closure to call on the object
+     * @return result of calling the closure
+     */
+    public static Object identity(Object self, Closure closure) {
+        return DefaultGroovyMethods.with(self, closure);
+    }
+
+    /**
+     * Allows the closure to be called for the object reference self
+     *
+     * @param self    the object to have a closure act upon
+     * @param closure the closure to call on the object
+     * @return result of calling the closure
+     */
+    public static Object with(Object self, Closure closure) {
+        final Closure clonedClosure = (Closure) closure.clone();
+        clonedClosure.setDelegate(self);
+        return clonedClosure.call(self);
+    }
+
+    /**
+     * Allows the subscript operator to be used to lookup dynamic property values.
+     * <code>bean[somePropertyNameExpression]</code>. The normal property notation
+     * of groovy is neater and more concise but only works with compile-time known
+     * property names.
+     *
+     * @param self     the object to act upon
+     * @param property the property name of interest
+     * @return the property value
+     */
+    public static Object getAt(Object self, String property) {
+        return InvokerHelper.getProperty(self, property);
+    }
+
+    /**
+     * Allows the subscript operator to be used to set dynamically named property values.
+     * <code>bean[somePropertyNameExpression] = foo</code>. The normal property notation
+     * of groovy is neater and more concise but only works with property names which
+     * are known at compile time.
+     *
+     * @param self     the object to act upon
+     * @param property the name of the property to set
+     * @param newValue the value to set
+     */
+    public static void putAt(Object self, String property, Object newValue) {
+        InvokerHelper.setProperty(self, property, newValue);
+    }
+
+    /**
+     * Generates a detailed dump string of an object showing its class,
+     * hashCode and fields.
+     *
+     * @param self an object
+     * @return the dump representation
+     */
+    public static String dump(Object self) {
+        if (self == null) {
+            return "null";
+        }
+        StringBuffer buffer = new StringBuffer("<");
+        Class klass = self.getClass();
+        buffer.append(klass.getName());
+        buffer.append("@");
+        buffer.append(Integer.toHexString(self.hashCode()));
+        boolean groovyObject = self instanceof GroovyObject;
+
+        /*jes this may be rewritten to use the new getProperties() stuff
+         * but the original pulls out private variables, whereas getProperties()
+         * does not. What's the real use of dump() here?
+         */
+        while (klass != null) {
+            Field[] fields = klass.getDeclaredFields();
+            for (int i = 0; i < fields.length; i++) {
+                final Field field = fields[i];
+                if ((field.getModifiers() & Modifier.STATIC) == 0) {
+                    if (groovyObject && field.getName().equals("metaClass")) {
+                        continue;
+                    }
+                    AccessController.doPrivileged(new PrivilegedAction() {
+                        public Object run() {
+                            field.setAccessible(true);
+                            return null;
+                        }
+                    });
+                    buffer.append(" ");
+                    buffer.append(field.getName());
+                    buffer.append("=");
+                    try {
+                        buffer.append(InvokerHelper.toString(field.get(self)));
+                    } catch (Exception e) {
+                        buffer.append(e);
+                    }
+                }
+            }
+
+            klass = klass.getSuperclass();
+        }
+
+        /* here is a different implementation that uses getProperties(). I have left
+         * it commented out because it returns a slightly different list of properties;
+         * ie it does not return privates. I don't know what dump() really should be doing,
+         * although IMO showing private fields is a no-no
+         */
+        /*
+        List props = getProperties(self);
+            for(Iterator itr = props.keySet().iterator(); itr.hasNext(); ) {
+            String propName = itr.next().toString();
+
+            // the original skipped this, so I will too
+            if(pv.getName().equals("metaClass")) continue;
+            if(pv.getName().equals("class")) continue;
+
+            buffer.append(" ");
+            buffer.append(propName);
+            buffer.append("=");
+            try {
+                buffer.append(InvokerHelper.toString(props.get(propName)));
+            }
+            catch (Exception e) {
+                buffer.append(e);
+            }
+        }
+        */
+
+        buffer.append(">");
+        return buffer.toString();
+    }
+
+    /**
+     * Retrieves the list of {@link MetaProperty} objects for 'self' and wraps it
+     * in a list of {@link PropertyValue} objects that additionally provide
+     * the value for each property of 'self'.
+     *
+     * @param self the receiver object
+     * @return list of {@link PropertyValue} objects
+     * @see groovy.util.Expando#getMetaPropertyValues()
+     */
+    public static List getMetaPropertyValues(Object self) {
+        MetaClass metaClass = InvokerHelper.getMetaClass(self);
+        List mps = metaClass.getProperties();
+        List props = new ArrayList(mps.size());
+        for (Iterator itr = mps.iterator(); itr.hasNext();) {
+            MetaProperty mp = (MetaProperty) itr.next();
+            PropertyValue pv = new PropertyValue(self, mp);
+            props.add(pv);
+        }
+        return props;
+    }
+
+    /**
+     * Convenience method that calls {@link #getMetaPropertyValues(Object)}(self)
+     * and provides the data in form of simple key/value pairs, i.e. without
+     * type() information.
+     *
+     * @param self the receiver object
+     * @return meta properties as Map of key/value pairs
+     */
+    public static Map getProperties(Object self) {
+        List metaProps = getMetaPropertyValues(self);
+        Map props = new HashMap(metaProps.size());
+
+        for (Iterator itr = metaProps.iterator(); itr.hasNext();) {
+            PropertyValue pv = (PropertyValue) itr.next();
+            try {
+                props.put(pv.getName(), pv.getValue());
+            } catch (Exception e) {
+                LOG.throwing(self.getClass().getName(), "getProperty(" + pv.getName() + ")", e);
+            }
+        }
+        return props;
+    }
+
+    /**
+     * Scoped use method
+     *
+     * @param self          any Object
+     * @param categoryClass a category class to use
+     * @param closure       the closure to invoke with the category in place
+     * @return the value returned from the closure
+     */
+    public static Object use(Object self, Class categoryClass, Closure closure) {
+        return GroovyCategorySupport.use(categoryClass, closure);
+    }
+
+    /**
+     * Scoped use method with list of categories.
+     *
+     * @param self              any Object
+     * @param categoryClassList a list of category classes
+     * @param closure           the closure to invoke with the categories in place
+     * @return the value returned from the closure
+     */
+    public static Object use(Object self, List categoryClassList, Closure closure) {
+        return GroovyCategorySupport.use(categoryClassList, closure);
+    }
+
+    /**
+     * Allows the usage of addShutdownHook without getting the runtime first.
+     *
+     * @param self    the object the method is called on (ignored)
+     * @param closure the shutdown hook action
+     */
+    public static void addShutdownHook(Object self, Closure closure) {
+        Runtime.getRuntime().addShutdownHook(new Thread(closure));
+    }
+
+    /**
+     * use() a list of categories, specifying the list as varargs:<br>
+     * use(CategoryClass1, CategoryClass2) { ... }<br>
+     * This method prevents the error of forgetting to wrap the the category
+     * classes in a list.
+     *
+     * @param self  any Object
+     * @param array a list of category classes and a Closure
+     * @return the value returned from the closure
+     */
+    public static Object use(Object self, Object[] array) {
+        if (array.length < 2)
+            throw new IllegalArgumentException(
+                    "Expecting at least 2 arguments, a category class and a Closure");
+        Closure closure;
+        try {
+            closure = (Closure) array[array.length - 1];
+        } catch (ClassCastException e) {
+            throw new IllegalArgumentException("Expecting a Closure to be the last argument");
+        }
+        List list = new ArrayList(array.length - 1);
+        for (int i = 0; i < array.length - 1; ++i)
+            list.add(array[i]);
+        return GroovyCategorySupport.use(list, closure);
+    }
+
+    /**
+     * Print a value to the standard output stream.
+     *
+     * @param self  any Object
+     * @param value the value to print
+     */
+    public static void print(Object self, Object value) {
+        System.out.print(InvokerHelper.toString(value));
+    }
+
+    /**
+     * Print a linebreak to the standard output stream.
+     *
+     * @param self any Object
+     */
+    public static void println(Object self) {
+        System.out.println();
+    }
+
+    /**
+     * Print a value (followed by a newline) to the standard output stream.
+     *
+     * @param self  any Object
+     * @param value the value to print
+     */
+    public static void println(Object self, Object value) {
+        System.out.println(InvokerHelper.toString(value));
+    }
+
+    /**
+     * Printf to a console.  Only works with JDK1.5 or later.
+     *
+     * @param self   any Object
+     * @param format a format string
+     * @param values values referenced by the format specifiers in the format string.
+     */
+    public static void printf(Object self, String format, Object[] values) {
+        if (self instanceof PrintStream)
+            printf((PrintStream) self, format, values);
+        else
+            printf(System.out, format, values);
+    }
+
+    /**
+     * Sprintf to a string.  Only works with JDK1.5 or later.
+     *
+     * @param self   any Object
+     * @param format a format string
+     * @param values values referenced by the format specifiers in the format string.
+     * @return the resulting formatted string
+     */
+    public static String sprintf(Object self, String format, Object[] values) {
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        PrintStream out = new PrintStream(outputStream);
+        printf(out, format, values);
+        return outputStream.toString();
+    }
+
+    /**
+     * Printf to a PrintStream.  Only works with JDK1.5 or later.
+     *
+     * @param out    a PrintStream object
+     * @param format a format string
+     * @param values values referenced by the format specifiers in the format string.
+     */
+    private static void printf(PrintStream out, String format, Object[] values) {
+        char version = System.getProperty("java.version").charAt(2);
+        if (version >= '5') {
+            //
+            //  Cannot just do:
+            //
+            //        System.out.printf(format, values) ;
+            //
+            //  because this fails to compile on JDK1.4.x and earlier.  So until the entire world is using
+            //  JDK1.5 or later then we have to do things by reflection so as to hide the use of printf
+            //  from the compiler.  In JDK1.5 you might try:
+            //
+            //        System.out.getClass().getMethod("printf", String.class, Object[].class).invoke(System.out, format, values) ;
+            //
+            //  but of course this doesn't work on JDK1.4 as it relies on varargs.  argh.  So we are
+            //  forced into:
+            //
+            try {
+                out.getClass().getMethod("printf", new Class[]{String.class, Object[].class}).invoke(out, new Object[]{format, values});
+            } catch (NoSuchMethodException nsme) {
+                throw new RuntimeException("getMethod threw a NoSuchMethodException.  This is impossible.");
+            } catch (IllegalAccessException iae) {
+                throw new RuntimeException("invoke threw an IllegalAccessException.  This is impossible.");
+            } catch (java.lang.reflect.InvocationTargetException ite) {
+                throw new InvokerInvocationException(ite);
+            }
+        } else {
+            throw new RuntimeException("printf requires JDK1.5 or later.");
+        }
+    }
+
+    /**
+     * Prints a formatted string using the specified format string and
+     * arguments.
+     * <p/>
+     * <p/>
+     * For examples, <pre>
+     *     printf ( "Hello, %s!\n" , [ "world" ] as String[] )
+     *     printf ( "Hello, %s!\n" , [ "Groovy" ])
+     *     printf ( "%d + %d = %d\n" , [ 1 , 2 , 1+2 ] as Integer[] )
+     *     printf ( "%d + %d = %d\n" , [ 3 , 3 , 3+3 ])
+     * <p/>
+     *     ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as Integer[] ) }
+     *     ( 1..5 ).each { printf ( "-- %d\n" , [ it ] as int[] ) }
+     *     ( 0x41..0x45 ).each { printf ( "-- %c\n" , [ it ] as char[] ) }
+     *     ( 07..011 ).each { printf ( "-- %d\n" , [ it ] as byte[] ) }
+     *     ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as short[] ) }
+     *     ( 7..11 ).each { printf ( "-- %d\n" , [ it ] as long[] ) }
+     *     ( 7..11 ).each { printf ( "-- %5.2f\n" , [ it ] as float[] ) }
+     *     ( 7..11 ).each { printf ( "-- %5.2g\n" , [ it ] as double[] ) }
+     * </pre>
+     * <p/>
+     *
+     * @param self   any Object
+     * @param format A format string
+     * @param arg    Argument which is referenced by the format specifiers in the format
+     *               string.  The type of <code>arg</code> should be one of Object[], List,
+     *               int[], short[], byte[], char[], boolean[], long[], float[], or double[].
+     */
+    public static void printf(Object self, String format, Object arg) {
+        if (self instanceof PrintStream)
+            printf((PrintStream) self, format, arg);
+        else
+            printf(System.out, format, arg);
+    }
+
+    private static void printf(PrintStream self, String format, Object arg) {
+        if (arg instanceof Object[]) {
+            printf(self, format, (Object[]) arg);
+            return;
+        }
+        if (arg instanceof List) {
+            printf(self, format, ((List) arg).toArray());
+            return;
+        }
+        if (!arg.getClass().isArray()) {
+            Object[] o = (Object[]) java.lang.reflect.Array.newInstance(arg.getClass(), 1);
+            o[0] = arg;
+            printf(self, format, o);
+            return;
+        }
+
+        Object[] ans;
+        String elemType = arg.getClass().getName();
+        if (elemType.equals("[I")) {
+            int[] ia = (int[]) arg;
+            ans = new Integer[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Integer(ia[i]);
+            }
+        } else if (elemType.equals("[C")) {
+            char[] ia = (char[]) arg;
+            ans = new Character[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Character(ia[i]);
+            }
+        } else if (elemType.equals("[Z")) {
+            boolean[] ia = (boolean[]) arg;
+            ans = new Boolean[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Boolean(ia[i]);
+            }
+        } else if (elemType.equals("[B")) {
+            byte[] ia = (byte[]) arg;
+            ans = new Byte[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Byte(ia[i]);
+            }
+        } else if (elemType.equals("[S")) {
+            short[] ia = (short[]) arg;
+            ans = new Short[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Short(ia[i]);
+            }
+        } else if (elemType.equals("[F")) {
+            float[] ia = (float[]) arg;
+            ans = new Float[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Float(ia[i]);
+            }
+        } else if (elemType.equals("[J")) {
+            long[] ia = (long[]) arg;
+            ans = new Long[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Long(ia[i]);
+            }
+        } else if (elemType.equals("[D")) {
+            double[] ia = (double[]) arg;
+            ans = new Double[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Double(ia[i]);
+            }
+        } else {
+            throw new RuntimeException("printf(String," + arg + ")");
+        }
+        printf(self, format, ans);
+    }
+
+    /**
+     * Returns a formatted string using the specified format string and
+     * arguments.
+     * <p/>
+     * TODO: remove duplication with printf
+     *
+     * @param self   any Object
+     * @param format A format string
+     * @param arg    Argument which is referenced by the format specifiers in the format
+     *               string.  The type of <code>arg</code> should be one of Object[], List,
+     *               int[], short[], byte[], char[], boolean[], long[], float[], or double[].
+     * @return the resulting printf'd string
+     */
+    public static String sprintf(Object self, String format, Object arg) {
+        if (arg instanceof Object[]) {
+            return sprintf(self, format, (Object[]) arg);
+        }
+        if (arg instanceof List) {
+            return sprintf(self, format, ((List) arg).toArray());
+        }
+        if (!arg.getClass().isArray()) {
+            Object[] o = (Object[]) java.lang.reflect.Array.newInstance(arg.getClass(), 1);
+            o[0] = arg;
+            return sprintf(self, format, o);
+        }
+
+        Object[] ans;
+        String elemType = arg.getClass().getName();
+        if (elemType.equals("[I")) {
+            int[] ia = (int[]) arg;
+            ans = new Integer[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Integer(ia[i]);
+            }
+        } else if (elemType.equals("[C")) {
+            char[] ia = (char[]) arg;
+            ans = new Character[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Character(ia[i]);
+            }
+        } else if (elemType.equals("[Z")) {
+            boolean[] ia = (boolean[]) arg;
+            ans = new Boolean[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Boolean(ia[i]);
+            }
+        } else if (elemType.equals("[B")) {
+            byte[] ia = (byte[]) arg;
+            ans = new Byte[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Byte(ia[i]);
+            }
+        } else if (elemType.equals("[S")) {
+            short[] ia = (short[]) arg;
+            ans = new Short[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Short(ia[i]);
+            }
+        } else if (elemType.equals("[F")) {
+            float[] ia = (float[]) arg;
+            ans = new Float[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Float(ia[i]);
+            }
+        } else if (elemType.equals("[J")) {
+            long[] ia = (long[]) arg;
+            ans = new Long[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Long(ia[i]);
+            }
+        } else if (elemType.equals("[D")) {
+            double[] ia = (double[]) arg;
+            ans = new Double[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = new Double(ia[i]);
+            }
+        } else {
+            throw new RuntimeException("sprintf(String," + arg + ")");
+        }
+        return sprintf(self, format, (Object[]) ans);
+    }
+
+
+    /**
+     * @param self any Object
+     * @return a String that matches what would be typed into a terminal to
+     *         create this object. e.g. [1, 'hello'].inspect() -> [1, "hello"]
+     */
+    public static String inspect(Object self) {
+        return InvokerHelper.inspect(self);
+    }
+
+    /**
+     * Print to a console in interactive format.
+     *
+     * @param self any Object
+     * @param out  the PrintWriter used for printing
+     */
+    public static void print(Object self, PrintWriter out) {
+        if (out == null) {
+            out = new PrintWriter(System.out);
+        }
+        out.print(InvokerHelper.toString(self));
+    }
+
+    /**
+     * Print to a console in interactive format.
+     *
+     * @param self any Object
+     * @param out  the PrintWriter used for printing
+     */
+    public static void println(Object self, PrintWriter out) {
+        if (out == null) {
+            out = new PrintWriter(System.out);
+        }
+        InvokerHelper.invokeMethod(self, "print", out);
+        out.println();
+    }
+
+    /**
+     * Provide a dynamic method invocation method which can be overloaded in
+     * classes to implement dynamic proxies easily.
+     *
+     * @param object    any Object
+     * @param method    the name of the method to call
+     * @param arguments the arguments to use
+     * @return the result of the method call
+     */
+    public static Object invokeMethod(Object object, String method, Object arguments) {
+        return InvokerHelper.invokeMethod(object, method, arguments);
+    }
+
+    // isCase methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Method for overloading the behavior of the 'case' method in switch statements.
+     * The default implementation simply delegates to Object#equals, but this
+     * may be overridden for other types.  In this example:
+     * <pre> switch( a ) {
+     *   case b: //some code
+     * }</pre>
+     * "some code" is called when <code>b.isCase( a )</code> returns
+     * <code>true</code>.
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return true if the switchValue is deemed to be equal to the caseValue
+     */
+    public static boolean isCase(Object caseValue, Object switchValue) {
+        return caseValue.equals(switchValue);
+    }
+
+    /**
+     * 'Case' implementation for a String, which uses String#equals(Object)
+     * in order to allow Strings to be used in switch statements.
+     * For example:
+     * <pre>switch( str ) {
+     *   case 'one' :
+     *   // etc...
+     * }</pre>
+     * Note that this returns <code>true</code> for the case where both the
+     * 'switch' and 'case' operand is <code>null</code>.
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return true if the switchValue's toString() equals the caseValue
+     */
+    public static boolean isCase(String caseValue, Object switchValue) {
+        if (switchValue == null) {
+            return caseValue == null;
+        }
+        return caseValue.equals(switchValue.toString());
+    }
+
+    /**
+     * Special 'Case' implementation for Class, which allows testing
+     * for a certain class in a switch statement.
+     * For example:
+     * <pre>switch( obj ) {
+     *   case List :
+     *     // obj is a list
+     *     break;
+     *   case Set :
+     *     // etc
+     * }<pre>
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return true if the switchValue is deemed to be assignable from the given class
+     */
+    public static boolean isCase(Class caseValue, Object switchValue) {
+        if (switchValue instanceof Class) {
+            Class val = (Class) switchValue;
+            return caseValue.isAssignableFrom(val);
+        }
+        return caseValue.isInstance(switchValue);
+    }
+
+    /**
+     * 'Case' implementation for collections which tests if the 'switch'
+     * operand is contained in any of the 'case' values.
+     * For example:
+     * <pre>switch( item ) {
+     *   case firstList :
+     *     // item is contained in this list
+     *     // etc
+     * }</pre>
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return true if the caseValue is deemed to contain the switchValue
+     * @see java.util.Collection#contains(Object)
+     */
+    public static boolean isCase(Collection caseValue, Object switchValue) {
+        return caseValue.contains(switchValue);
+    }
+
+    /**
+     * 'Case' implementation for the {@link Pattern} class, which allows
+     * testing a String against a number of regular expressions.
+     * For example:
+     * <pre>switch( str ) {
+     *   case ~/one/ :
+     *     // the regex 'one' matches the value of str
+     * }
+     * </pre>
+     * Note that this returns true for the case where both the pattern and
+     * the 'switch' values are <code>null</code>.
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return true if the switchValue is deemed to match the caseValue
+     */
+    public static boolean isCase(Pattern caseValue, Object switchValue) {
+        if (switchValue == null) {
+            return caseValue == null;
+        }
+        final Matcher matcher = caseValue.matcher(switchValue.toString());
+        if (matcher.matches()) {
+            RegexSupport.setLastMatcher(matcher);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Special 'case' implementation for all numbers, which delegates to the
+     * <code>compareTo()</code> method for comparing numbers of different
+     * types.
+     *
+     * @param caseValue   the case value
+     * @param switchValue the switch value
+     * @return true if the numbers are deemed equal
+     */
+    public static boolean isCase(Number caseValue, Number switchValue) {
+        return NumberMath.compareTo(caseValue, switchValue) == 0;
+    }
+
+    // Collection based methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Modifies this collection to remove all duplicated items, using the
+     * default comparator.
+     *
+     * @param self a collection
+     * @return this collection
+     */
+    public static Collection unique(Collection self) {
+        if (self instanceof Set)
+            return self;
+        List answer = new ArrayList();
+        NumberAwareComparator numberAwareComparator = new NumberAwareComparator();
+        for (Iterator it = self.iterator(); it.hasNext();) {
+            Object o = it.next();
+            boolean duplicated = false;
+            for (Iterator it2 = answer.iterator(); it2.hasNext();) {
+                Object o2 = it2.next();
+                if (numberAwareComparator.compare(o, o2) == 0) {
+                    duplicated = true;
+                    break;
+                }
+            }
+            if (!duplicated)
+                answer.add(o);
+        }
+        self.clear();
+        self.addAll(answer);
+        return self;
+    }
+
+    /**
+     * A convenience method for making a collection unique using a closure
+     * as a comparator.  If the closure takes a single parameter, the
+     * argument passed will be each element, and the closure
+     * should return a value used for comparison (either using
+     * {@link Comparable#compareTo(Object)} or Object#equals() ).  If the
+     * closure takes two parameters, two items from the collection
+     * will be passed as arguments, and the closure should return an
+     * int value (with 0 indicating the items are not unique).
+     *
+     * @param self    a Collection
+     * @param closure a Closure used as a comparator
+     * @return self   without any duplicates
+     */
+    public static Collection unique(Collection self, Closure closure) {
+        if (self instanceof Set)
+            return self;
+        // use a comparator of one item or two
+        int params = closure.getMaximumNumberOfParameters();
+        if (params == 1) {
+            unique(self, new OrderBy(closure));
+        } else {
+            unique(self, new ClosureComparator(closure));
+        }
+        return self;
+    }
+
+    /**
+     * Remove all duplicates from a given Collection.
+     * Works on the receiver object and returns it.
+     * The order of members in the Collection are compared by the given Comparator.
+     * For each duplicate, the first member which is returned
+     * by the given Collection's iterator is retained, but all other ones are removed.
+     * The given Collection's original order is preserved.
+     * <p/>
+     * <code><pre>
+     *     class Person {
+     *         def fname, lname
+     *         public String toString() {
+     *             return fname + " " + lname
+     *         }
+     *     }
+     * <p/>
+     *     class PersonComparator implements Comparator {
+     *         public int compare(Object o1, Object o2) {
+     *             Person p1 = (Person) o1
+     *             Person p2 = (Person) o2
+     *             if (p1.lname != p2.lname)
+     *                 return p1.lname.compareTo(p2.lname)
+     *             else
+     *                 return p1.fname.compareTo(p2.fname)
+     *         }
+     * <p/>
+     *         public boolean equals(Object obj) {
+     *             return this.equals(obj)
+     *         }
+     *     }
+     * <p/>
+     *     Person a = new Person(fname:"John", lname:"Taylor")
+     *     Person b = new Person(fname:"Clark", lname:"Taylor")
+     *     Person c = new Person(fname:"Tom", lname:"Cruz")
+     *     Person d = new Person(fname:"Clark", lname:"Taylor")
+     * <p/>
+     *     def list = [a, b, c, d]
+     *     List list2 = list.unique(new PersonComparator())
+     *     assert( list2 == list && list == [a, b, c] )
+     * <p/>
+     * </pre></code>
+     *
+     * @param self       a Collection
+     * @param comparator a Comparator.
+     * @return self       without duplicates
+     */
+    public static Collection unique(Collection self, Comparator comparator) {
+        if (self instanceof Set)
+            return self;
+        List answer = new ArrayList();
+        for (Iterator it = self.iterator(); it.hasNext();) {
+            Object o = it.next();
+            boolean duplicated = false;
+            for (Iterator it2 = answer.iterator(); it2.hasNext();) {
+                Object o2 = it2.next();
+                if (comparator.compare(o, o2) == 0) {
+                    duplicated = true;
+                    break;
+                }
+            }
+            if (!duplicated)
+                answer.add(o);
+        }
+        self.clear();
+        self.addAll(answer);
+        return self;
+    }
+
+    /**
+     * Iterates through an aggregate type or data structure,
+     * passing each item to the given closure.  Custom types may utilize this
+     * method by simply providing an "iterator()" method.  The items returned
+     * from the resulting iterator will be passed to the closure.
+     *
+     * @param self    the object over which we iterate
+     * @param closure the closure applied on each element found
+     * @return the self Object
+     */
+    public static Object each(Object self, Closure closure) {
+        each(InvokerHelper.asIterator(self), closure);
+        return self;
+    }
+
+    /**
+     * Iterates through an aggregate type or data structure,
+     * passing each item and the item's index (a counter starting at
+     * zero) to the given closure.
+     *
+     * @param self    an Object
+     * @param closure a Closure to operate on each item
+     * @return the self Object
+     */
+    public static Object eachWithIndex(Object self, Closure closure) {
+        int counter = 0;
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+            closure.call(new Object[]{iter.next(), new Integer(counter++)});
+        }
+        return self;
+    }
+
+    private static Iterator each(Iterator iter, Closure closure) {
+        while (iter.hasNext()) {
+            closure.call(iter.next());
+        }
+        return iter;
+    }
+
+    /**
+     * Allows a Map to be iterated through using a closure. If the
+     * closure takes one parameter then it will be passed the Map.Entry
+     * otherwise if the closure takes two parameters then it will be
+     * passed the key and the value.
+     *
+     * @param self    the map over which we iterate
+     * @param closure the closure applied on each entry of the map
+     * @return returns the self parameter
+     */
+    public static Map each(Map self, Closure closure) {
+        for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            callClosureForMapEntry(closure, entry);
+        }
+        return self;
+    }
+
+    /**
+     * Iterate over each element of the list in the reverse order.
+     *
+     * @param self    a List
+     * @param closure a closure to which each item is passed.
+     * @return the original list
+     */
+    public static List reverseEach(List self, Closure closure) {
+        each(reverse(self).iterator(), closure);
+        return self;
+    }
+
+    /**
+     * Used to determine if the given predicate closure is valid (i.e. returns
+     * <code>true</code>) for all items in this data structure.
+     * A simple example for a list:
+     * <pre>def list = [3,4,5]
+     * def greaterThanTwo = list.every { it > 2 }
+     * </pre>
+     *
+     * @param self    the object over which we iterate
+     * @param closure the closure predicate used for matching
+     * @return true if every iteration of the object matches the closure predicate
+     */
+    public static boolean every(Object self, Closure closure) {
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+            if (!DefaultTypeTransformation.castToBoolean(closure.call(iter.next()))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Iterates over the entries of a map, and checks whether a predicate is
+     * valid for all entries.
+     *
+     * @param self    the map over which we iterate
+     * @param closure the closure predicate used for matching
+     * @return true if every entry of the map matches the closure predicate
+     */
+    public static boolean every(Map self, Closure closure) {
+        for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            if (!DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Iterates over every element of a collection, and checks whether all
+     * elements are <code>true</code> according to the Groovy Truth.
+     * Equivalent to <code>self.every({element -> element})</code>
+     *
+     * @param self the object over which we iterate
+     * @return true if every item in the collection matches the closure
+     *         predicate
+     */
+    public static boolean every(Object self) {
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+            if (!DefaultTypeTransformation.castToBoolean(iter.next())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Iterates over the contents of an object or collection, and checks whether a
+     * predicate is valid for at least one element.
+     *
+     * @param self    the object over which we iterate
+     * @param closure the closure predicate used for matching
+     * @return true   if any iteration for the object matches the closure predicate
+     */
+    public static boolean any(Object self, Closure closure) {
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+            if (DefaultTypeTransformation.castToBoolean(closure.call(iter.next()))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Iterates over the entries of a map, and checks whether a predicate is
+     * valid for at least one entry
+     *
+     * @param self    the map over which we iterate
+     * @param closure the closure predicate used for matching
+     * @return true if any entry in the map matches the closure predicate
+     */
+    public static boolean any(Map self, Closure closure) {
+        for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Iterates over the elements of a collection, and checks whether at least
+     * one element is true according to the Groovy Truth.
+     * Equivalent to self.any({element -> element})
+     *
+     * @param self the object over which we iterate
+     * @return true if any item in the collection matches the closure predicate
+     */
+    public static boolean any(Object self) {
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+            if (DefaultTypeTransformation.castToBoolean(iter.next())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Iterates over every element of the collection and returns each item that matches
+     * the given filter - calling the <code>{@link #isCase(Object,Object)}</code>
+     * method used by switch statements.  This method can be used with different
+     * kinds of filters like regular expressions, classes, ranges etc.
+     * Example:
+     * <pre>def list = ['a', 'b', 'aa', 'bc' ]
+     * def filtered = list.grep( ~/a+/ ) //contains 'a' and 'aa'
+     * </pre>
+     *
+     * @param self   the object over which we iterate
+     * @param filter the filter to perform on the collection (using the isCase(object) method)
+     * @return a list of objects which match the filter
+     */
+    public static List grep(Object self, Object filter) {
+        List answer = new ArrayList();
+        MetaClass metaClass = InvokerHelper.getMetaClass(filter);
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+            Object object = iter.next();
+            if (DefaultTypeTransformation.castToBoolean(metaClass.invokeMethod(filter, "isCase", object))) {
+                answer.add(object);
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Counts the number of occurrences of the given value inside this collection.
+     * Comparison is done using Groovy's == operator (using
+     * <code>compareTo(value) == 0</code> or <code>equals(value)</code> ).
+     *
+     * @param self  the collection within which we count the number of occurrences
+     * @param value the value being searched for
+     * @return the number of occurrences
+     */
+    public static int count(Collection self, Object value) {
+        int answer = 0;
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            if (DefaultTypeTransformation.compareEqual(iter.next(), value)) {
+                ++answer;
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Convert a collection to a List.
+     *
+     * @param self a collection
+     * @return a List
+     */
+    public static List toList(Collection self) {
+        List answer = new ArrayList(self.size());
+        answer.addAll(self);
+        return answer;
+    }
+
+    /**
+     * Convert an iterator to a List.
+     *
+     * @param self an iterator
+     * @return a List
+     */
+    public static List toList(Iterator self) {
+        List answer = new ArrayList();
+        while (self.hasNext()) {
+            answer.add(self.next());
+        }
+        return answer;
+    }
+
+    /**
+     * Convert an enumeration to a List.
+     *
+     * @param self an enumeration
+     * @return a List
+     */
+    public static List toList(Enumeration self) {
+        List answer = new ArrayList();
+        while (self.hasMoreElements()) {
+            answer.add(self.nextElement());
+        }
+        return answer;
+    }
+
+    /**
+     * Iterates through this object transforming each item into a new value using the closure
+     * as a transformer, returning a list of transformed values.
+     * Example:
+     * <pre>def list = [1, 'a', 1.23, true ]
+     * def types = list.collect { it.class }
+     * </pre>
+     *
+     * @param self    the values of the object to map
+     * @param closure the closure used to transform each element of the collection
+     * @return a List of the mapped values
+     */
+    public static List collect(Object self, Closure closure) {
+        return (List) collect(self, new ArrayList(), closure);
+    }
+
+    /**
+     * Iterates through this object transforming each object into a new value using the closure
+     * as a transformer and adding it to the collection, returning the resulting collection.
+     *
+     * @param self       the values of the object to map
+     * @param collection the Collection to which the transformed values are added
+     * @param closure    the closure used to map each element of the collection
+     * @return the given collection after the items are added
+     */
+    public static Collection collect(Object self, Collection collection, Closure closure) {
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+            collection.add(closure.call(iter.next()));
+        }
+        return collection;
+    }
+
+    /**
+     * Iterates through this collection transforming each entry into a new value using the closure
+     * as a transformer, returning a list of transformed values.
+     *
+     * @param self    a collection
+     * @param closure the closure used for mapping
+     * @return a List of the mapped values
+     */
+    public static List collect(Collection self, Closure closure) {
+        return (List) collect(self, new ArrayList(self.size()), closure);
+    }
+
+    /**
+     * Iterates through this collection transforming each entry into a new value using the closure
+     * as a transformer, returning a list of transformed values.
+     *
+     * @param self       a collection
+     * @param collection the Collection to which the mapped values are added
+     * @param closure    the closure used to map each element of the collection
+     * @return the resultant collection
+     */
+    public static Collection collect(Collection self, Collection collection, Closure closure) {
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            collection.add(closure.call(iter.next()));
+            if (closure.getDirective() == Closure.DONE) {
+                break;
+            }
+        }
+        return collection;
+    }
+
+    /**
+     * Iterates through this Map transforming each entry into a new value using the closure
+     * as a transformer, returning a list of transformed values.
+     *
+     * @param self       a Map
+     * @param collection the Collection to which the mapped values are added
+     * @param closure    the closure used for mapping, which can be with one(Map.Entry) or two(key, value) parameters
+     * @return a List of the mapped values
+     */
+    public static Collection collect(Map self, Collection collection, Closure closure) {
+        boolean isTwoParams = (closure.getParameterTypes().length == 2);
+        for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
+            if (isTwoParams) {
+                Map.Entry entry = (Map.Entry) iter.next();
+                collection.add(closure.call(new Object[]{entry.getKey(), entry.getValue()}));
+            } else {
+                collection.add(closure.call(iter.next()));
+            }
+        }
+        return collection;
+    }
+
+    /**
+     * Iterates through this Map transforming each entry into a new value using the closure
+     * as a transformer, returning a list of transformed values.
+     *
+     * @param self    a Map
+     * @param closure the closure used to map each element of the collection
+     * @return the resultant collection
+     */
+    public static List collect(Map self, Closure closure) {
+        return (List) collect(self, new ArrayList(self.size()), closure);
+    }
+
+    /**
+     * Finds the first value matching the closure condition
+     *
+     * @param self    an Object with an iterator returning its values
+     * @param closure a closure condition
+     * @return the first Object found
+     */
+    public static Object find(Object self, Closure closure) {
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+            Object value = iter.next();
+            if (DefaultTypeTransformation.castToBoolean(closure.call(value))) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Finds the first value matching the closure condition.  Example:
+     * <pre>def list = [1,2,3]
+     * list.find { it > 1 } // returns 2
+     * </pre>
+     *
+     * @param self    a Collection
+     * @param closure a closure condition
+     * @return the first Object found
+     */
+    public static Object find(Collection self, Closure closure) {
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (DefaultTypeTransformation.castToBoolean(closure.call(value))) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Finds the first entry matching the closure condition.  If the closure takes
+     * two parameters, the entry key and value are passed.  If the closure takes
+     * one parameter, the Map.Entry object is passed.
+     *
+     * @param self    a Map
+     * @param closure a closure condition
+     * @return the first Object found
+     */
+    public static Object find(Map self, Closure closure) {
+        for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) {
+                return entry;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Finds all items matching the closure condition.
+     *
+     * @param self    an Object with an Iterator returning its values
+     * @param closure a closure condition
+     * @return a List of the values found
+     */
+    public static List findAll(Object self, Closure closure) {
+        List answer = new ArrayList();
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext();) {
+            Object value = iter.next();
+            if (DefaultTypeTransformation.castToBoolean(closure.call(value))) {
+                answer.add(value);
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Finds all values matching the closure condition.
+     *
+     * @param self    a Collection
+     * @param closure a closure condition
+     * @return a List of the values found
+     */
+    public static List findAll(Collection self, Closure closure) {
+        List answer = new ArrayList(self.size());
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (DefaultTypeTransformation.castToBoolean(closure.call(value))) {
+                answer.add(value);
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Adds combinations() as a method on collections.
+     *
+     * @param self a Collection of lists
+     * @return a List of the combinations found
+     * @see GroovyCollections#combinations(Collection)
+     */
+    public static List combinations(Collection self) {
+        return GroovyCollections.combinations(self);
+    }
+
+    /**
+     * Adds transpose() as a method on collections.
+     *
+     * @param self a Collection of lists
+     * @return a List of the transposed lists
+     * @see GroovyCollections#transpose(Collection)
+     */
+    public static List transpose(Collection self) {
+        return GroovyCollections.transpose(self);
+    }
+
+    /**
+     * Finds all entries matching the closure condition. If the
+     * closure takes one parameter then it will be passed the Map.Entry.
+     * Otherwise if the closure should take two parameters, which will be
+     * the key and the value.
+     *
+     * @param self    a Map
+     * @param closure a closure condition applying on the entries
+     * @return a new subMap
+     */
+    public static Map findAll(Map self, Closure closure) {
+        Map answer = new HashMap(self.size());
+        for (Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            if (DefaultTypeTransformation.castToBoolean(callClosureForMapEntry(closure, entry))) {
+                answer.put(entry.getKey(), entry.getValue());
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Sorts all collection members into groups determined by the
+     * supplied mapping closure.  The closure should return the key that this
+     * item should be grouped by.  The returned Map will have an entry for each
+     * distinct key returned from the closure, with each value being a list of
+     * items for that group.
+     *
+     * @param self    a collection to group (no map)
+     * @param closure a closure mapping entries on keys
+     * @return a new Map grouped by keys
+     */
+    public static Map groupBy(Collection self, Closure closure) {
+        Map answer = new HashMap();
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            Object element = iter.next();
+            Object value = closure.call(element);
+            groupAnswer(answer, element, value);
+        }
+        return answer;
+    }
+
+    /**
+     * Groups all map members into groups determined by the
+     * supplied mapping closure. The closure will be passed a Map.Entry or
+     * key and value (depending on the number of parameters the closure accepts)
+     * and should return the key that each item should be grouped under.  The
+     * resulting map will have an entry for each 'group' key returned by the
+     * closure, with values being the a list of map entries that belong to each
+     * group.
+     *
+     * @param self    a map to group
+     * @param closure a closure mapping entries on keys
+     * @return a new Map grouped by keys
+     */
+    public static Map groupBy(Map self, Closure closure) {
+        final Map answer = new HashMap();
+        for (final Iterator iter = self.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            Object value = callClosureForMapEntry(closure, entry);//closure.call(element);
+            groupAnswer(answer, entry, value);
+        }
+        return answer;
+    }
+
+    /**
+     * Groups the current element according to the value
+     *
+     * @param answer  the map containing the results
+     * @param element the element to be placed
+     * @param value   the value according to which the element will be placed
+     */
+    protected static void groupAnswer(final Map answer, Object element, Object value) {
+        if (answer.containsKey(value)) {
+            ((List) answer.get(value)).add(element);
+        } else {
+            List groupedElements = new ArrayList();
+            groupedElements.add(element);
+            answer.put(value, groupedElements);
+        }
+    }
+
+    // internal helper method
+    protected static Object callClosureForMapEntry(Closure closure, Map.Entry entry) {
+        if (closure.getMaximumNumberOfParameters() == 2) {
+            return closure.call(new Object[]{entry.getKey(), entry.getValue()});
+        }
+        return closure.call(entry);
+    }
+
+
+    /**
+     * Iterates through the given collection, passing in the initial value to
+     * the closure along with the current iterated item then passing into the
+     * next iteration the value of the previous closure.
+     *
+     * @param self    a Collection
+     * @param value   a value
+     * @param closure a closure
+     * @return the last value of the last iteration
+     */
+    public static Object inject(Collection self, Object value, Closure closure) {
+        return inject(self.iterator(), value, closure);
+    }
+
+    /**
+     * Iterates through the given iterator, passing in the initial value to
+     * the closure along with the current iterated item then passing into the
+     * next iteration the value of the previous closure.
+     *
+     * @param self    a Collection
+     * @param value   a value
+     * @param closure a closure
+     * @return the last value of the last iteration
+     */
+    public static Object inject(Iterator self, Object value, Closure closure) {
+        Object[] params = new Object[2];
+        while (self.hasNext()) {
+            Object item = self.next();
+            params[0] = value;
+            params[1] = item;
+            value = closure.call(params);
+        }
+        return value;
+    }
+
+    /**
+     * Iterates through the given object, passing in the initial value to
+     * the closure along with the current iterated item then passing into the
+     * next iteration the value of the previous closure.
+     *
+     * @param self    a Collection
+     * @param value   a value
+     * @param closure a closure
+     * @return the last value of the last iteration
+     */
+    public static Object inject(Object self, Object value, Closure closure) {
+        Iterator iter = InvokerHelper.asIterator(self);
+        return inject(iter, value, closure);
+    }
+
+    /**
+     * Iterates through the given array of objects, passing in the initial value to
+     * the closure along with the current iterated item then passing into the
+     * next iteration the value of the previous closure.
+     *
+     * @param self         an Object[]
+     * @param initialValue an initialValue
+     * @param closure      a closure
+     * @return the last value of the last iteration
+     */
+    public static Object inject(Object[] self, Object initialValue, Closure closure) {
+        Object[] params = new Object[2];
+        Object value = initialValue;
+        for (int i = 0; i < self.length; i++) {
+            params[0] = value;
+            params[1] = self[i];
+            value = closure.call(params);
+        }
+        return value;
+    }
+
+    /**
+     * Sums the items in a collection.  This is equivalent to invoking the
+     * "plus" method on all items in the collection.
+     *
+     * @param self Collection of values to add together.
+     * @return The sum of all of the list itmems.
+     */
+    public static Object sum(Collection self) {
+        return sum(self, null, true);
+    }
+
+    /**
+     * Sums the items in a collection, adding the result to some initial value.
+     *
+     * @param self         a collection of values to sum.
+     * @param initialValue the items in the collection will be summed to this initial value
+     * @return The sum of all of the collection items.
+     */
+    public static Object sum(Collection self, Object initialValue) {
+        return sum(self, initialValue, false);
+    }
+
+    private static Object sum(Collection self, Object initialValue, boolean first) {
+        Object result = initialValue;
+        Object[] param = new Object[1];
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            param[0] = iter.next();
+            if (first) {
+                result = param[0];
+                first = false;
+                continue;
+            }
+            MetaClass metaClass = InvokerHelper.getMetaClass(result);
+            result = metaClass.invokeMethod(result, "plus", param);
+        }
+        return result;
+    }
+
+    /**
+     * Sums the result of apply a closure to each item of a collection.
+     * <code>coll.sum(closure)</code> is equivalent to:
+     * <code>coll.collect(closure).sum()</code>.
+     *
+     * @param self    a Collection
+     * @param closure a single parameter closure that returns a numeric value.
+     * @return The sum of the values returned by applying the closure to each
+     *         item of the list.
+     */
+    public static Object sum(Collection self, Closure closure) {
+        return sum(self, null, closure, true);
+    }
+
+    /**
+     * Sums the result of apply a closure to each item of a collection to sum intial value.
+     * <code>coll.sum(closure)</code> is equivalent to:
+     * <code>coll.collect(closure).sum()</code>.
+     *
+     * @param self         a Collection
+     * @param closure      a single parameter closure that returns a numeric value.
+     * @param initialValue the closure results will be summed to this initial value
+     * @return The sum of the values returned by applying the closure to each
+     *         item of the list.
+     */
+    public static Object sum(Collection self, Object initialValue, Closure closure) {
+        return sum(self, initialValue, closure, false);
+    }
+
+    private static Object sum(Collection self, Object initialValue, Closure closure, boolean first) {
+        Object result = initialValue;
+        Object[] closureParam = new Object[1];
+        Object[] plusParam = new Object[1];
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            closureParam[0] = iter.next();
+            plusParam[0] = closure.call(closureParam);
+            if (first) {
+                result = plusParam[0];
+                first = false;
+                continue;
+            }
+            MetaClass metaClass = InvokerHelper.getMetaClass(result);
+            result = metaClass.invokeMethod(result, "plus", plusParam);
+        }
+        return result;
+    }
+
+    /**
+     * Concatenates the <code>toString()</code> representation of each
+     * item in this collection, with the given String as a separator between
+     * each item.
+     *
+     * @param self      a Collection of objects
+     * @param separator a String separator
+     * @return the joined String
+     */
+    public static String join(Collection self, String separator) {
+        StringBuffer buffer = new StringBuffer();
+        boolean first = true;
+
+        if (separator == null) separator = "";
+
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (first) {
+                first = false;
+            } else {
+                buffer.append(separator);
+            }
+            buffer.append(InvokerHelper.toString(value));
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Concatenates the <code>toString()</code> representation of each
+     * items in this array, with the given String as a separator between each
+     * item.
+     *
+     * @param self      an array of Object
+     * @param separator a String separator
+     * @return the joined String
+     */
+    public static String join(Object[] self, String separator) {
+        StringBuffer buffer = new StringBuffer();
+        boolean first = true;
+
+        if (separator == null) separator = "";
+
+        for (int i = 0; i < self.length; i++) {
+            String value = InvokerHelper.toString(self[i]);
+            if (first) {
+                first = false;
+            } else {
+                buffer.append(separator);
+            }
+            buffer.append(value);
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Adds max() method to Collection objects.
+     *
+     * @param self a Collection
+     * @return the maximum value
+     * @see GroovyCollections#max(Collection)
+     */
+    public static Object max(Collection self) {
+        return GroovyCollections.max(self);
+    }
+
+    /**
+     * Selects the maximum value found in the collection using the given comparator.
+     *
+     * @param self       a Collection
+     * @param comparator a Comparator
+     * @return the maximum value
+     */
+    public static Object max(Collection self, Comparator comparator) {
+        Object answer = null;
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (answer == null || comparator.compare(value, answer) > 0) {
+                answer = value;
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Adds min() method to Collection objects.
+     *
+     * @param self a Collection
+     * @return the minimum value
+     * @see GroovyCollections#min(Collection)
+     */
+    public static Object min(Collection self) {
+        return GroovyCollections.min(self);
+    }
+
+    /**
+     * Selects the minimum value found in the collection using the given comparator.
+     *
+     * @param self       a Collection
+     * @param comparator a Comparator
+     * @return the minimum value
+     */
+    public static Object min(Collection self, Comparator comparator) {
+        Object answer = null;
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (answer == null || comparator.compare(value, answer) < 0) {
+                answer = value;
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Selects the minimum value found in the collection using the given closure
+     * as a comparator.  The closure should return a comparable value (i.e. a
+     * number) for each item passed.  The collection item for which the closure
+     * returns the smallest comparable value will be returned from this method
+     * as the minimum.
+     *
+     * @param self    a Collection
+     * @param closure a closure used as a comparator
+     * @return the minimum value
+     */
+    public static Object min(Collection self, Closure closure) {
+        int params = closure.getMaximumNumberOfParameters();
+        if (params != 1) {
+            return min(self, new ClosureComparator(closure));
+        }
+        Object answer = null;
+        Object answer_value = null;
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            Object item = iter.next();
+            Object value = closure.call(item);
+            if (answer == null || ScriptBytecodeAdapter.compareLessThan(value, answer_value)) {
+                answer = item;
+                answer_value = value;
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Selects the maximum value found in the collection using the given closure
+     * as a comparator.  The closure should return a comparable value (i.e. a
+     * number) for each item passed.  The collection item for which the closure
+     * returns the largest comparable value will be returned from this method
+     * as the maximum.
+     *
+     * @param self    a Collection
+     * @param closure a closure used as a comparator
+     * @return the maximum value
+     */
+    public static Object max(Collection self, Closure closure) {
+        int params = closure.getMaximumNumberOfParameters();
+        if (params != 1) {
+            return max(self, new ClosureComparator(closure));
+        }
+        Object answer = null;
+        Object AnswerValue = null;
+        for (Iterator iter = self.iterator(); iter.hasNext();) {
+            Object item = iter.next();
+            Object value = closure.call(item);
+            if (answer == null || ScriptBytecodeAdapter.compareLessThan(AnswerValue, value)) {
+                answer = item;
+                AnswerValue = value;
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Makes a String look like a Collection by adding support for the size() method
+     *
+     * @param text a String
+     * @return the length of the String
+     */
+    public static int size(String text) {
+        return text.length();
+    }
+
+    /**
+     * Provide standard Groovy size() method for StringBuffers
+     *
+     * @param buffer a StringBuffer
+     * @return the length of the StringBuffer
+     */
+    public static int size(StringBuffer buffer) {
+        return buffer.length();
+    }
+
+    /**
+     * Provide the standard Groovy size method for a file.
+     *
+     * @param self a file object
+     * @return the file's size (length)
+     */
+    public static long size(File self) {
+        return self.length();
+    }
+
+
+    /**
+     * Provide the standard Groovy size method for a matcher.
+     *
+     * @param self a matcher object
+     * @return the matcher's size (count)
+     */
+    public static long size(Matcher self) {
+        return getCount(self);
+    }
+
+    /**
+     * Provide the standard Groovy size method for an array.
+     *
+     * @param self an Array of objects
+     * @return the size (length) of the Array
+     */
+    public static int size(Object[] self) {
+        return self.length;
+    }
+
+    /**
+     * Support the subscript operator for CharSequence.
+     *
+     * @param text  a CharSequence
+     * @param index the index of the Character to get
+     * @return the Character at the given index
+     */
+    public static CharSequence getAt(CharSequence text, int index) {
+        index = normaliseIndex(index, text.length());
+        return text.subSequence(index, index + 1);
+    }
+
+    /**
+     * Support the subscript operator for String.
+     *
+     * @param text  a String
+     * @param index the index of the Character to get
+     * @return the Character at the given index
+     */
+    public static String getAt(String text, int index) {
+        index = normaliseIndex(index, text.length());
+        return text.substring(index, index + 1);
+    }
+
+    /**
+     * Support the range subscript operator for CharSequence
+     *
+     * @param text  a CharSequence
+     * @param range a Range
+     * @return the subsequence CharSequence
+     */
+    public static CharSequence getAt(CharSequence text, Range range) {
+        int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), text.length());
+        int to = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getTo()), text.length());
+
+        boolean reverse = range.isReverse();
+        // If this is a backwards range, reverse the arguments to substring.
+        if (from > to) {
+            int tmp = from;
+            from = to;
+            to = tmp;
+            reverse = !reverse;
+        }
+
+        CharSequence sequence = text.subSequence(from, to + 1);
+        return reverse ? reverse((String) sequence) : sequence;
+    }
+
+    /**
+     * Support the range subscript operator for CharSequence or StringBuffer with IntRange
+     *
+     * @param text  a CharSequence
+     * @param range an IntRange
+     * @return the subsequence CharSequence
+     */
+    public static CharSequence getAt(CharSequence text, IntRange range) {
+        return getAt(text, (Range) range);
+    }
+
+    /**
+     * Support the range subscript operator for CharSequence or StringBuffer with EmptyRange
+     *
+     * @param text  a CharSequence
+     * @param range an EmptyRange
+     * @return the subsequence CharSequence
+     */
+    public static CharSequence getAt(CharSequence text, EmptyRange range) {
+        return "";
+    }
+
+    /**
+     * Support the range subscript operator for String with IntRange
+     *
+     * @param text  a String
+     * @param range an IntRange
+     * @return the resulting String
+     */
+    public static String getAt(String text, IntRange range) {
+        return getAt(text, (Range) range);
+    }
+
+    /**
+     * Support the range subscript operator for String with EmptyRange
+     *
+     * @param text  a String
+     * @param range an EmptyRange
+     * @return the resulting String
+     */
+    public static String getAt(String text, EmptyRange range) {
+        return "";
+    }
+
+    /**
+     * Support the range subscript operator for String
+     *
+     * @param text  a String
+     * @param range a Range
+     * @return a substring corresponding to the Range
+     */
+    public static String getAt(String text, Range range) {
+        int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), text.length());
+        int to = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getTo()), text.length());
+
+        // If this is a backwards range, reverse the arguments to substring.
+        boolean reverse = range.isReverse();
+        if (from > to) {
+            int tmp = to;
+            to = from;
+            from = tmp;
+            reverse = !reverse;
+        }
+
+        String answer = text.substring(from, to + 1);
+        if (reverse) {
+            answer = reverse(answer);
+        }
+        return answer;
+    }
+
+    /**
+     * Creates a new string which is the reverse (backwards) of this string
+     *
+     * @param self a String
+     * @return a new string with all the characters reversed.
+     */
+    public static String reverse(String self) {
+        int size = self.length();
+        StringBuffer buffer = new StringBuffer(size);
+        for (int i = size - 1; i >= 0; i--) {
+            buffer.append(self.charAt(i));
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Transforms a String representing a URL into a URL object.
+     *
+     * @param self the String representing a URL
+     * @return a URL
+     * @throws MalformedURLException is thrown if the URL is not well formed.
+     */
+    public static URL toURL(String self) throws MalformedURLException {
+        return new URL(self);
+    }
+
+    /**
+     * Transforms a String representing a URI into a URI object.
+     *
+     * @param self the String representing a URI
+     * @return a URI
+     * @throws URISyntaxException is thrown if the URI is not well formed.
+     */
+    public static URI toURI(String self) throws URISyntaxException {
+        return new URI(self);
+    }
+
+    /**
+     * Turns a String into a regular expression pattern
+     *
+     * @param self a String to convert into a regular expression
+     * @return the regular expression pattern
+     */
+    public static Pattern bitwiseNegate(String self) {
+        return Pattern.compile(self);
+    }
+
+    /**
+     * Replaces all occurrencies of a captured group by the result of a closure on that text.
+     * <p/>
+     * <p> For examples,
+     * <pre>
+     *     assert "FOOBAR-FOOBAR-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { Object[] it -> it[0].toUpperCase() })
+     * <p/>
+     *     Here,
+     *          it[0] is the global string of the matched group
+     *          it[1] is the first string in the matched group
+     *          it[2] is the second string in the matched group
+     * <p/>
+     * <p/>
+     *     assert "FOO-FOO-" == "foobar-FooBar-".replaceAll("(([fF][oO]{2})[bB]ar)", { x, y, z -> z.toUpperCase() })
+     * <p/>
+     *     Here,
+     *          x is the global string of the matched group
+     *          y is the first string in the matched group
+     *          z is the second string in the matched group
+     * </pre>
+     *
+     * @param self    a String
+     * @param regex   the capturing regex
+     * @param closure the closure to apply on each captured group
+     * @return a String with replaced content
+     */
+    public static String replaceAll(String self, String regex, Closure closure) {
+        Matcher matcher = Pattern.compile(regex).matcher(self);
+        if (matcher.find()) {
+            matcher.reset();
+            StringBuffer sb = new StringBuffer();
+            while (matcher.find()) {
+                int count = matcher.groupCount();
+                List groups = new ArrayList();
+                for (int i = 0; i <= count; i++) {
+                    groups.add(matcher.group(i));
+                }
+                matcher.appendReplacement(sb, String.valueOf(closure.call(groups.toArray())));
+            }
+            matcher.appendTail(sb);
+            return sb.toString();
+        } else {
+            return self;
+        }
+    }
+
+    private static String getPadding(String padding, int length) {
+        if (padding.length() < length) {
+            return multiply(padding, new Integer(length / padding.length() + 1)).substring(0, length);
+        } else {
+            return padding.substring(0, length);
+        }
+    }
+
+    /**
+     * Pad a String with the characters appended to the left
+     *
+     * @param self          a String object
+     * @param numberOfChars the total number of characters
+     * @param padding       the charaters used for padding
+     * @return the String padded to the left
+     */
+    public static String padLeft(String self, Number numberOfChars, String padding) {
+        int numChars = numberOfChars.intValue();
+        if (numChars <= self.length()) {
+            return self;
+        } else {
+            return getPadding(padding, numChars - self.length()) + self;
+        }
+    }
+
+    /**
+     * Pad a String with the spaces appended to the left
+     *
+     * @param self          a String object
+     * @param numberOfChars the total number of characters
+     * @return the String padded to the left
+     */
+
+    public static String padLeft(String self, Number numberOfChars) {
+        return padLeft(self, numberOfChars, " ");
+    }
+
+    /**
+     * Pad a String with the characters appended to the right
+     *
+     * @param self          a String object
+     * @param numberOfChars the total number of characters
+     * @param padding       the charaters used for padding
+     * @return the String padded to the right
+     */
+
+    public static String padRight(String self, Number numberOfChars, String padding) {
+        int numChars = numberOfChars.intValue();
+        if (numChars <= self.length()) {
+            return self;
+        } else {
+            return self + getPadding(padding, numChars - self.length());
+        }
+    }
+
+    /**
+     * Pad a String with the spaces appended to the right
+     *
+     * @param self          a String object
+     * @param numberOfChars the total number of characters
+     * @return the String padded to the right
+     */
+
+    public static String padRight(String self, Number numberOfChars) {
+        return padRight(self, numberOfChars, " ");
+    }
+
+    /**
+     * Center a String and padd it with the characters appended around it
+     *
+     * @param self          a String object
+     * @param numberOfChars the total number of characters
+     * @param padding       the charaters used for padding
+     * @return the String centered with padded character around
+     */
+    public static String center(String self, Number numberOfChars, String padding) {
+        int numChars = numberOfChars.intValue();
+        if (numChars <= self.length()) {
+            return self;
+        } else {
+            int charsToAdd = numChars - self.length();
+            String semiPad = charsToAdd % 2 == 1 ?
+                    getPadding(padding, charsToAdd / 2 + 1) :
+                    getPadding(padding, charsToAdd / 2);
+            if (charsToAdd % 2 == 0)
+                return semiPad + self + semiPad;
+            else
+                return semiPad.substring(0, charsToAdd / 2) + self + semiPad;
+        }
+    }
+
+    /**
+     * Center a String and padd it with spaces appended around it
+     *
+     * @param self          a String object
+     * @param numberOfChars the total number of characters
+     * @return the String centered with padded character around
+     */
+    public static String center(String self, Number numberOfChars) {
+        return center(self, numberOfChars, " ");
+    }
+
+    /**
+     * Support the subscript operator, e.g. matcher[index], for a regex Matcher.
+     * <p/>
+     * For an example using no group match, <code><pre>
+     *    def p = /ab[d|f]/
+     *    def m = "abcabdabeabf" =~ p
+     *    for (i in 0..<m.count) {
+     *        println( "m.groupCount() = " + m.groupCount())
+     *        println( "  " + i + ": " + m[i] )   // m[i] is a String
+     *    }
+     * </pre></code>
+     * <p/>
+     * For an example using group matches, <code><pre>
+     *    def p = /(?:ab([c|d|e|f]))/
+     *    def m = "abcabdabeabf" =~ p
+     *    for (i in 0..<m.count) {
+     *        println( "m.groupCount() = " + m.groupCount())
+     *        println( "  " + i + ": " + m[i] )   // m[i] is a List
+     *    }
+     * </pre></code>
+     * <p/>
+     * For another example using group matches, <code><pre>
+     *    def m = "abcabdabeabfabxyzabx" =~ /(?:ab([d|x-z]+))/
+     *    m.count.times {
+     *        println( "m.groupCount() = " + m.groupCount())
+     *        println( "  " + it + ": " + m[it] )   // m[it] is a List
+     *    }
+     * </pre></code>
+     *
+     * @param matcher a Matcher
+     * @param idx     an index
+     * @return object a matched String if no groups matched, list of matched groups otherwise.
+     */
+    public static Object getAt(Matcher matcher, int idx) {
+        try {
+            int count = getCount(matcher);
+            if (idx < -count || idx >= count) {
+                throw new IndexOutOfBoundsException("index is out of range " + (-count) + ".." + (count - 1) + " (index = " + idx + ")");
+            }
+            idx = normaliseIndex(idx, count);
+            matcher.reset();
+            for (int i = 0; i <= idx; i++) {
+                matcher.find();
+            }
+
+            if (hasGroup(matcher)) {
+                // are we using groups?
+                // yes, so return the specified group as list
+                List list = new ArrayList(matcher.groupCount());
+                for (int i = 0; i <= matcher.groupCount(); i++) {
+                    list.add(matcher.group(i));
+                }
+                return list;
+            } else {
+                // not using groups, so return the nth
+                // occurrence of the pattern
+                return matcher.group();
+            }
+        }
+        catch (IllegalStateException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Set the position of the given Matcher to the given index.
+     *
+     * @param matcher a Matcher
+     * @param idx     the index number
+     */
+    public static void setIndex(Matcher matcher, int idx) {
+        int count = getCount(matcher);
+        if (idx < -count || idx >= count) {
+            throw new IndexOutOfBoundsException("index is out of range " + (-count) + ".." + (count - 1) + " (index = " + idx + ")");
+        }
+        if (idx == 0) {
+            matcher.reset();
+        } else if (idx > 0) {
+            matcher.reset();
+            for (int i = 0; i < idx; i++) {
+                matcher.find();
+            }
+        } else if (idx < 0) {
+            matcher.reset();
+            idx += getCount(matcher);
+            for (int i = 0; i < idx; i++) {
+                matcher.find();
+            }
+        }
+    }
+
+    /**
+     * Find the number of Strings matched to the given Matcher.
+     *
+     * @param matcher a Matcher
+     * @return int  the number of Strings matched to the given matcher.
+     */
+    public static int getCount(Matcher matcher) {
+        int counter = 0;
+        matcher.reset();
+        while (matcher.find()) {
+            counter++;
+        }
+        matcher.reset();
+        return counter;
+    }
+
+    /**
+     * Check whether a Matcher contains a group or not.
+     *
+     * @param matcher a Matcher
+     * @return boolean  <code>true</code> if matcher contains at least one group.
+     */
+    public static boolean hasGroup(Matcher matcher) {
+        return matcher.groupCount() > 0;
+    }
+
+    /**
+     * Support the range subscript operator for a List
+     *
+     * @param self  a List
+     * @param range a Range indicating the items to get
+     * @return a sublist based on range borders or a new list if range is reversed
+     * @see java.util.List#subList(int,int)
+     */
+    public static List getAt(List self, IntRange range) {
+        RangeInfo info = subListBorders(self.size(), range);
+        List answer = self.subList(info.from, info.to);  // sublist is always exclusive, but Ranges are not
+        if (info.reverse) {
+            answer = reverse(answer);
+        }
+        return answer;
+    }
+
+    // helper method for getAt and putAt
+    protected static RangeInfo subListBorders(int size, IntRange range) {
+        int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), size);
+        int to = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getTo()), size);
+        boolean reverse = range.isReverse();
+        if (from > to) {                        // support list[1..-1]
+            int tmp = to;
+            to = from;
+            from = tmp;
+            reverse = !reverse;
+        }
+        return new RangeInfo(from, to + 1, reverse);
+    }
+
+    // helper method for getAt and putAt
+    protected static RangeInfo subListBorders(int size, EmptyRange range) {
+        int from = normaliseIndex(DefaultTypeTransformation.intUnbox(range.getFrom()), size);
+        return new RangeInfo(from, from, false);
+    }
+
+    /**
+     * Allows a List to be used as the indices to be used on a List
+     *
+     * @param self    a List
+     * @param indices a Collection of indices
+     * @return a new list of the values at the given indices
+     */
+    public static List getAt(List self, Collection indices) {
+        List answer = new ArrayList(indices.size());
+        for (Iterator iter = indices.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (value instanceof Range) {
+                answer.addAll(getAt(self, (Range) value));
+            } else if (value instanceof List) {
+                answer.addAll(getAt(self, (List) value));
+            } else {
+                int idx = DefaultTypeTransformation.intUnbox(value);
+                answer.add(getAt(self, idx));
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Allows a List to be used as the indices to be used on a List
+     *
+     * @param self    an Array of Objects
+     * @param indices a Collection of indices
+     * @return a new list of the values at the given indices
+     */
+    public static List getAt(Object[] self, Collection indices) {
+        List answer = new ArrayList(indices.size());
+        for (Iterator iter = indices.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (value instanceof Range) {
+                answer.addAll(getAt(self, (Range) value));
+            } else if (value instanceof Collection) {
+                answer.addAll(getAt(self, (Collection) value));
+            } else {
+                int idx = DefaultTypeTransformation.intUnbox(value);
+                answer.add(getAt(self, idx));
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Allows a List to be used as the indices to be used on a CharSequence
+     *
+     * @param self    a CharSequence
+     * @param indices a Collection of indices
+     * @return a String of the values at the given indices
+     */
+    public static CharSequence getAt(CharSequence self, Collection indices) {
+        StringBuffer answer = new StringBuffer();
+        for (Iterator iter = indices.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (value instanceof Range) {
+                answer.append(getAt(self, (Range) value));
+            } else if (value instanceof Collection) {
+                answer.append(getAt(self, (Collection) value));
+            } else {
+                int idx = DefaultTypeTransformation.intUnbox(value);
+                answer.append(getAt(self, idx));
+            }
+        }
+        return answer.toString();
+    }
+
+    /**
+     * Allows a List to be used as the indices to be used on a String
+     *
+     * @param self    a String
+     * @param indices a Collection of indices
+     * @return a String of the values at the given indices
+     */
+    public static String getAt(String self, Collection indices) {
+        return (String) getAt((CharSequence) self, indices);
+    }
+
+    /**
+     * Allows a List to be used as the indices to be used on a Matcher
+     *
+     * @param self    a Matcher
+     * @param indices a Collection of indices
+     * @return a String of the values at the given indices
+     */
+    public static String getAt(Matcher self, Collection indices) {
+        StringBuffer answer = new StringBuffer();
+        for (Iterator iter = indices.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (value instanceof Range) {
+                answer.append(getAt(self, (Range) value));
+            } else if (value instanceof Collection) {
+                answer.append(getAt(self, (Collection) value));
+            } else {
+                int idx = DefaultTypeTransformation.intUnbox(value);
+                answer.append(getAt(self, idx));
+            }
+        }
+        return answer.toString();
+    }
+
+    /**
+     * Creates a sub-Map containing the given keys. This method is similar to
+     * List.subList() but uses keys rather than index ranges.
+     *
+     * @param map  a Map
+     * @param keys a Collection of keys
+     * @return a new Map containing the given keys
+     */
+    public static Map subMap(Map map, Collection keys) {
+        Map answer = new LinkedHashMap(keys.size());
+        for (Iterator iter = keys.iterator(); iter.hasNext();) {
+            Object key = iter.next();
+            answer.put(key, map.get(key));
+        }
+        return answer;
+    }
+
+    /**
+     * Looks up an item in a Map for the given key and returns the value - unless
+     * there is no entry for the given key in which case add the default value
+     * to the map and return that.
+     *
+     * @param map          a Map
+     * @param key          the key to lookup the value of
+     * @param defaultValue the value to return and add to the map for this key if
+     *                     there is no entry for the given key
+     * @return the value of the given key or the default value, added to the map if the
+     *         key did not exist
+     */
+    public static Object get(Map map, Object key, Object defaultValue) {
+        Object answer = map.get(key);
+        if (answer == null) {
+            answer = defaultValue;
+            map.put(key, answer);
+        }
+        return answer;
+    }
+
+    /**
+     * Support the range subscript operator for an Array
+     *
+     * @param array an Array of Objects
+     * @param range a Range
+     * @return a range of a list from the range's from index up to but not
+     *         including the ranges's to value
+     */
+    public static List getAt(Object[] array, Range range) {
+        List list = Arrays.asList(array);
+        return getAt(list, range);
+    }
+
+    public static List getAt(Object[] array, IntRange range) {
+        List list = Arrays.asList(array);
+        return getAt(list, range);
+    }
+
+    public static List getAt(Object[] array, EmptyRange range) {
+        return new ArrayList();
+    }
+
+    public static List getAt(Object[] array, ObjectRange range) {
+        List list = Arrays.asList(array);
+        return getAt(list, range);
+    }
+
+    /**
+     * Support the subscript operator for an Array.
+     *
+     * @param array an Array of Objects
+     * @param idx   an index
+     * @return the value at the given index
+     */
+    public static Object getAt(Object[] array, int idx) {
+        return array[normaliseIndex(idx, array.length)];
+    }
+
+    /**
+     * Support the subscript operator for an Array.
+     *
+     * @param array an Array of Objects
+     * @param idx   an index
+     * @param value an Object to put at the given index
+     */
+    public static void putAt(Object[] array, int idx, Object value) {
+        if (value instanceof Number) {
+            Class arrayComponentClass = array.getClass().getComponentType();
+
+            if (!arrayComponentClass.equals(value.getClass())) {
+                Object newVal = DefaultTypeTransformation.castToType(value, arrayComponentClass);
+                array[normaliseIndex(idx, array.length)] = newVal;
+                return;
+            }
+        }
+        array[normaliseIndex(idx, array.length)] = value;
+    }
+
+    /**
+     * Allows conversion of arrays into a mutable List.
+     *
+     * @param array an Array of Objects
+     * @return the array as a List
+     */
+    public static List toList(Object[] array) {
+        return new ArrayList(Arrays.asList(array));
+    }
+
+    /**
+     * Support the subscript operator for a List.
+     *
+     * @param self a List
+     * @param idx  an index
+     * @return the value at the given index
+     */
+    public static Object getAt(List self, int idx) {
+        int size = self.size();
+        int i = normaliseIndex(idx, size);
+        if (i < size) {
+            return self.get(i);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * A helper method to allow lists to work with subscript operators.
+     *
+     * @param self  a List
+     * @param idx   an index
+     * @param value the value to put at the given index
+     */
+    public static void putAt(List self, int idx, Object value) {
+        int size = self.size();
+        idx = normaliseIndex(idx, size);
+        if (idx < size) {
+            self.set(idx, value);
+        } else {
+            while (size < idx) {
+                self.add(size++, null);
+            }
+            self.add(idx, value);
+        }
+    }
+
+
+    /**
+     * Support the range subscript operator for StringBuffer.  Index values are
+     * treated as characters within the buffer.
+     *
+     * @param self  a StringBuffer
+     * @param range a Range
+     * @param value the object that's toString() will be inserted
+     */
+    public static void putAt(StringBuffer self, IntRange range, Object value) {
+        RangeInfo info = subListBorders(self.length(), range);
+        self.replace(info.from, info.to, value.toString());
+    }
+
+    /**
+     * Support the range subscript operator for StringBuffer.
+     *
+     * @param self  a StringBuffer
+     * @param range a Range
+     * @param value the object that's toString() will be inserted
+     */
+    public static void putAt(StringBuffer self, EmptyRange range, Object value) {
+        RangeInfo info = subListBorders(self.length(), range);
+        self.replace(info.from, info.to, value.toString());
+    }
+
+    /**
+     * A helper method to allow lists to work with subscript operators.
+     *
+     * @param self  a List
+     * @param range the subset of the list to set
+     * @param value the values to put at the given sublist or a Collection of values
+     */
+    public static void putAt(List self, EmptyRange range, Object value) {
+        RangeInfo info = subListBorders(self.size(), range);
+        List sublist = self.subList(info.from, info.to);
+        sublist.clear();
+        if (value instanceof Collection) {
+            Collection col = (Collection) value;
+            if (col.isEmpty()) return;
+            sublist.addAll(col);
+        } else {
+            sublist.add(value);
+        }
+    }
+
+    private static List resizeListWithRangeAndGetSublist(List self, IntRange range) {
+        RangeInfo info = subListBorders(self.size(), range);
+        int size = self.size();
+        if (info.to >= size) {
+            while (size < info.to) {
+                self.add(size++, null);
+            }
+        }
+        List sublist = self.subList(info.from, info.to);
+        sublist.clear();
+        return sublist;
+    }
+
+    /**
+     * List subscript assignment operator when given a range as the index and
+     * the assignment operand is a collection.
+     * Example: <code>myList[3..5] = anotherList</code>.  Items in the given
+     * range are relaced with items from the collection.
+     *
+     * @param self  a List
+     * @param range the subset of the list to set
+     * @param col   the collection of values to put at the given sublist
+     */
+    public static void putAt(List self, IntRange range, Collection col) {
+        List sublist = resizeListWithRangeAndGetSublist(self, range);
+        if (col.isEmpty()) return;
+        sublist.addAll(col);
+    }
+
+    /**
+     * List subscript assignment operator when given a range as the index.
+     * Example: <code>myList[3..5] = newItem</code>.  Items in the given
+     * range are relaced with the operand.  The <code>value</code> operand is
+     * always treated as a single value.
+     *
+     * @param self  a List
+     * @param range the subset of the list to set
+     * @param value the value to put at the given sublist
+     */
+    public static void putAt(List self, IntRange range, Object value) {
+        List sublist = resizeListWithRangeAndGetSublist(self, range);
+        sublist.add(value);
+    }
+
+    /**
+     * A helper method to allow lists to work with subscript operators.
+     *
+     * @param self   a List
+     * @param splice the subset of the list to set
+     * @param values the value to put at the given sublist
+     * @deprecated replace with putAt(List self, Range range, List value)
+     */
+    public static void putAt(List self, List splice, List values) {
+        List sublist = getSubList(self, splice);
+        sublist.clear();
+        sublist.addAll(values);
+    }
+
+    /**
+     * A helper method to allow lists to work with subscript operators.
+     *
+     * @param self   a List
+     * @param splice the subset of the list to set
+     * @param value  the value to put at the given sublist
+     * @deprecated replace with putAt(List self, Range range, Object value)
+     */
+    public static void putAt(List self, List splice, Object value) {
+        List sublist = getSubList(self, splice);
+        sublist.clear();
+        sublist.add(value);
+    }
+
+    // helper method for putAt(Splice)
+    // todo: remove after putAt(Splice) gets deleted
+    protected static List getSubList(List self, List splice) {
+        int left /* = 0 */;
+        int right = 0;
+        boolean emptyRange = false;
+        if (splice.size() == 2) {
+            left = DefaultTypeTransformation.intUnbox(splice.get(0));
+            right = DefaultTypeTransformation.intUnbox(splice.get(1));
+        } else if (splice instanceof IntRange) {
+            IntRange range = (IntRange) splice;
+            left = range.getFromInt();
+            right = range.getToInt();
+        } else if (splice instanceof EmptyRange) {
+            RangeInfo info = subListBorders(self.size(), (EmptyRange) splice);
+            left = info.from;
+            emptyRange = true;
+        } else {
+            throw new IllegalArgumentException("You must specify a list of 2 indexes to create a sub-list");
+        }
+        int size = self.size();
+        left = normaliseIndex(left, size);
+        right = normaliseIndex(right, size);
+        List sublist /* = null */;
+        if (!emptyRange) {
+            sublist = self.subList(left, right + 1);
+        } else {
+            sublist = self.subList(left, left);
+        }
+        return sublist;
+    }
+
+    /**
+     * Support the subscript operator for a List.
+     *
+     * @param self a Map
+     * @param key  an Object as a key for the map
+     * @return the value corresponding to the given key
+     */
+    public static Object getAt(Map self, Object key) {
+        return self.get(key);
+    }
+
+    /**
+     * <p/>
+     * Returns a new Map containg all entries from <code>left</code> and <code>right</code>,
+     * giving precedence to <code>right</code>.  Any keys appearing in both Maps
+     * will appear in the resultant map with values from the <code>right</code>
+     * operand.
+     * </p>
+     * <p/>
+     * <p/>
+     * Equivalent to <code>Map m = new HashMap(); m.putAll(left); m.putAll(right); return m;</code>
+     * </p>
+     *
+     * @param left  a Map
+     * @param right a Map
+     * @return a new Map containing all entries from left and right
+     */
+    public static Map plus(Map left, Map right) {
+        Map map = new TreeMap(left);
+        map.putAll(right);
+        return map;
+    }
+
+    /**
+     * A helper method to allow lists to work with subscript operators
+     *
+     * @param self  a Map
+     * @param key   an Object as a key for the map
+     * @param value the value to put into the map
+     * @return the value corresponding to the given key
+     */
+    public static Object putAt(Map self, Object key, Object value) {
+        self.put(key, value);
+        return value;
+    }
+
+    /**
+     * This converts a possibly negative index to a real index into the array.
+     *
+     * @param i    the unnormalised index
+     * @param size the array size
+     * @return the normalised index
+     */
+    protected static int normaliseIndex(int i, int size) {
+        int temp = i;
+        if (i < 0) {
+            i += size;
+        }
+        if (i < 0) {
+            throw new ArrayIndexOutOfBoundsException("Negative array index [" + temp + "] too large for array size " + size);
+        }
+        return i;
+    }
+
+    /**
+     * Support the subscript operator for List
+     *
+     * @param coll     a Collection
+     * @param property a String
+     * @return a List
+     */
+    public static List getAt(Collection coll, String property) {
+        List answer = new ArrayList(coll.size());
+        for (Iterator iter = coll.iterator(); iter.hasNext();) {
+            Object item = iter.next();
+            Object value;
+            try {
+                value = InvokerHelper.getProperty(item, property);
+            } catch (MissingPropertyExceptionNoStack mpe) {
+                String causeString = new MissingPropertyException(mpe.getProperty(), mpe.getType()).toString();
+                throw new MissingPropertyException("Exception evaluating property '" + property +
+                        "' for " + coll.getClass().getName() + ", Reason: " + causeString);
+            }
+            answer.add(value);
+        }
+        return answer;
+    }
+
+    /**
+     * A convenience method for creating an immutable map.
+     *
+     * @param self a Map
+     * @return an immutable Map
+     * @see java.util.Collections#unmodifiableMap(java.util.Map)
+     */
+    public static Map asImmutable(Map self) {
+        return Collections.unmodifiableMap(self);
+    }
+
+    /**
+     * A convenience method for creating an immutable sorted map.
+     *
+     * @param self a SortedMap
+     * @return an immutable SortedMap
+     * @see java.util.Collections#unmodifiableSortedMap(java.util.SortedMap)
+     */
+    public static SortedMap asImmutable(SortedMap self) {
+        return Collections.unmodifiableSortedMap(self);
+    }
+
+    /**
+     * A convenience method for creating an immutable list
+     *
+     * @param self a List
+     * @return an immutable List
+     * @see java.util.Collections#unmodifiableList(java.util.List)
+     */
+    public static List asImmutable(List self) {
+        return Collections.unmodifiableList(self);
+    }
+
+    /**
+     * A convenience method for creating an immutable list.
+     *
+     * @param self a Set
+     * @return an immutable Set
+     * @see java.util.Collections#unmodifiableSet(java.util.Set)
+     */
+    public static Set asImmutable(Set self) {
+        return Collections.unmodifiableSet(self);
+    }
+
+    /**
+     * A convenience method for creating an immutable sorted set.
+     *
+     * @param self a SortedSet
+     * @return an immutable SortedSet
+     * @see java.util.Collections#unmodifiableSortedSet(java.util.SortedSet)
+     */
+    public static SortedSet asImmutable(SortedSet self) {
+        return Collections.unmodifiableSortedSet(self);
+    }
+
+    /**
+     * A convenience method for creating an immutable Collection.
+     *
+     * @param self a Collection
+     * @return an immutable Collection
+     * @see java.util.Collections#unmodifiableCollection(java.util.Collection)
+     */
+    public static Collection asImmutable(Collection self) {
+        return Collections.unmodifiableCollection(self);
+    }
+
+    /**
+     * A convenience method for creating a synchronized Map.
+     *
+     * @param self a Map
+     * @return a synchronized Map
+     * @see java.util.Collections#synchronizedMap(java.util.Map)
+     */
+    public static Map asSynchronized(Map self) {
+        return Collections.synchronizedMap(self);
+    }
+
+    /**
+     * A convenience method for creating a synchronized SortedMap.
+     *
+     * @param self a SortedMap
+     * @return a synchronized SortedMap
+     * @see java.util.Collections#synchronizedSortedMap(java.util.SortedMap)
+     */
+    public static SortedMap asSynchronized(SortedMap self) {
+        return Collections.synchronizedSortedMap(self);
+    }
+
+    /**
+     * A convenience method for creating a synchronized Collection.
+     *
+     * @param self a Collection
+     * @return a synchronized Collection
+     * @see java.util.Collections#synchronizedCollection(java.util.Collection)
+     */
+    public static Collection asSynchronized(Collection self) {
+        return Collections.synchronizedCollection(self);
+    }
+
+    /**
+     * A convenience method for creating a synchronized List.
+     *
+     * @param self a List
+     * @return a synchronized List
+     * @see java.util.Collections#synchronizedList(java.util.List)
+     */
+    public static List asSynchronized(List self) {
+        return Collections.synchronizedList(self);
+    }
+
+    /**
+     * A convenience method for creating a synchronized Set.
+     *
+     * @param self a Set
+     * @return a synchronized Set
+     * @see java.util.Collections#synchronizedSet(java.util.Set)
+     */
+    public static Set asSynchronized(Set self) {
+        return Collections.synchronizedSet(self);
+    }
+
+    /**
+     * A convenience method for creating a synchronized SortedSet.
+     *
+     * @param self a SortedSet
+     * @return a synchronized SortedSet
+     * @see java.util.Collections#synchronizedSortedSet(java.util.SortedSet)
+     */
+    public static SortedSet asSynchronized(SortedSet self) {
+        return Collections.synchronizedSortedSet(self);
+    }
+
+    /**
+     * Synonym for {@link #toSpreadMap(Map)}.
+     * @param self a map
+     * @return a newly created Spreadmap
+     */
+    public static SpreadMap spread(Map self) {
+        return toSpreadMap(self);
+    }
+
+    /**
+     * Returns a new <code>SpreadMap</code> from this map.
+     * <p/>
+     * For examples, if there is defined a function like as
+     * <blockquote><pre>
+     *     def fn(a, b, c, d) { return a + b + c + d }
+     * </pre></blockquote>, then all of the following three have the same meaning.
+     * <blockquote><pre>
+     *     println fn(a:1, [b:2, c:3].toSpreadMap(), d:4)
+     *     println fn(a:1, *:[b:2, c:3], d:4)
+     *     println fn(a:1, b:2, c:3, d:4)
+     * </pre></blockquote>
+     * <p/>
+     *
+     * @param self a list to be converted into a spreadmap
+     * @return a newly created Spreadmap if this list is not null and its size is positive.
+     * @see groovy.lang.SpreadMap#SpreadMap(java.util.Map)
+     */
+    public static SpreadMap toSpreadMap(Map self) {
+        if (self == null)
+            throw new GroovyRuntimeException("Fail to convert Map to SpreadMap, because it is null.");
+        else
+            return new SpreadMap(self);
+    }
+
+    /**
+     * Creates a spreadable map from this array.
+     * @param self an object array
+     * @return a newly created Spreadmap
+     * @see groovy.lang.SpreadMap#SpreadMap(java.lang.Object[])
+     */
+    public static SpreadMap toSpreadMap(Object[] self) {
+        if (self == null)
+            throw new GroovyRuntimeException("Fail to convert Object[] to SpreadMap, because it is null.");
+        else if (self.length % 2 != 0)
+            throw new GroovyRuntimeException("Fail to convert Object[] to SpreadMap, because it's size is not even.");
+        else
+            return new SpreadMap(self);
+    }
+
+    /**
+     * Sorts the given collection into a sorted list.  The collection items are
+     * assumed to be comparable.
+     *
+     * @param self the collection to be sorted
+     * @return the sorted collection as a List
+     */
+    public static List sort(Collection self) {
+        List answer = asList(self);
+        Collections.sort(answer, new NumberAwareComparator());
+        return answer;
+    }
+
+    /**
+     * Avoids doing unnecessary work when sorting an already sorted set.
+     *
+     * @param self an identity function for an already sorted set
+     * @return the sorted set
+     */
+    public static SortedSet sort(SortedSet self) {
+        return self;
+    }
+
+    /**
+     * Removes the last item from the List. Using add() and pop()
+     * is similar to push and pop on a Stack.
+     *
+     * @param self a List
+     * @return the item removed from the List
+     * @throws NoSuchElementException if the list is empty and you try to pop() it.
+     */
+    public static Object pop(List self) {
+        if (self.isEmpty()) {
+            throw new NoSuchElementException("Cannot pop() an empty List");
+        }
+        return self.remove(self.size() - 1);
+    }
+
+    /**
+     * Sorts the Collection using the given comparator.  The elements are
+     * sorted into a new list, and the existing collection is unchanged.
+     *
+     * @param self       a collection to be sorted
+     * @param comparator a Comparator used for the comparison
+     * @return a newly created sorted List
+     */
+    public static List sort(Collection self, Comparator comparator) {
+        List list = asList(self);
+        Collections.sort(list, comparator);
+        return list;
+    }
+
+    /**
+     * Sorts this Collection using the given closure as a comparator.  The
+     * closure is passed each item from the collection, and is assumed to
+     * return a comparable value (i.e. an int).
+     *
+     * @param self    a Collection to be sorted
+     * @param closure a Closure used as a comparator
+     * @return a newly created sorted List
+     */
+    public static List sort(Collection self, Closure closure) {
+        List list = asList(self);
+        // use a comparator of one item or two
+        int params = closure.getMaximumNumberOfParameters();
+        if (params == 1) {
+            Collections.sort(list, new OrderBy(closure));
+        } else {
+            Collections.sort(list, new ClosureComparator(closure));
+        }
+        return list;
+    }
+
+    /**
+     * Converts this collection to a List.
+     *
+     * @param self a collection to be converted into a List
+     * @return a newly created List if this collection is not already a List
+     */
+    public static List asList(Collection self) {
+        if (self instanceof List) {
+            return (List) self;
+        } else {
+            return new ArrayList(self);
+        }
+    }
+
+    /**
+     * Converts the given collection to either a List, Set, or
+     * SortedSet.  If the given class is something else, the
+     * call is deferred to {link #asType(Object,Class)}.  If this
+     * collection is already of the given type, the same instance is
+     * returned.
+     *
+     * @param col   a collection
+     * @param clazz the desired class
+     * @return the object resulting from this type conversion
+     * @see #asType(Object,Class)
+     */
+    public static Object asType(Collection col, Class clazz) {
+        if (clazz == List.class) {
+            return asList(col);
+        } else if (clazz == Set.class) {
+            if (col instanceof Set) return col;
+            return new HashSet(col);
+        } else if (clazz == SortedSet.class) {
+            if (col instanceof SortedSet) return col;
+            return new TreeSet(col);
+        }
+        return asType((Object) col, clazz);
+    }
+
+    /**
+     * Coerces the closure to an implementation of the given class.  The class
+     * is assumed to be an interface or class with a single method definition.
+     * The closure is used as the implementation of that single method.
+     *
+     * @param cl    the implementaiton of the single method
+     * @param clazz the target type
+     * @return a Proxy of the given type which wraps this closure.
+     */
+    public static Object asType(Closure cl, Class clazz) {
+        if (clazz.isInterface() && !(clazz.isInstance(cl))) {
+            return Proxy.newProxyInstance(
+                    clazz.getClassLoader(),
+                    new Class[]{clazz},
+                    new ConvertedClosure(cl));
+        }
+        return asType((Object) cl, clazz);
+    }
+
+    /**
+     * Coerces this map to the given type, using the map's keys as the public
+     * method names, and values as the implementation.  Typically the value
+     * would be a closure which behaves like the method implementation.
+     *
+     * @param map   this map
+     * @param clazz the target type
+     * @return a Proxy of the given type, which defers calls to this map's elements.
+     */
+    public static Object asType(Map map, Class clazz) {
+        if (!(clazz.isInstance(map)) && clazz.isInterface()) {
+            return Proxy.newProxyInstance(
+                    clazz.getClassLoader(),
+                    new Class[]{clazz},
+                    new ConvertedMap(map));
+        }
+        try {
+            return asType((Object) map, clazz);
+        } catch (GroovyCastException ce) {
+            try {
+                return ProxyGenerator.instantiateAggregateFromBaseClass(map, clazz);
+            } catch (GroovyRuntimeException cause) {
+                throw new GroovyCastException("Error casting map to " + clazz.getName() +
+                        ", Reason: " + cause.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Reverses the list.  The result is a new List with the identical contents
+     * in reverse order.
+     *
+     * @param self a List
+     * @return a reversed List
+     */
+    public static List reverse(List self) {
+        int size = self.size();
+        List answer = new ArrayList(size);
+        ListIterator iter = self.listIterator(size);
+        while (iter.hasPrevious()) {
+            answer.add(iter.previous());
+        }
+        return answer;
+    }
+
+    /**
+     * Create a Collection as a union of two collections. If the left collection
+     * is a Set, then the returned collection will be a Set otherwise a List.
+     * TODO: remove equivalent numbers after merge, e.g. 1L and 1G?
+     *
+     * @param left  the left Collection
+     * @param right the right Collection
+     * @return the merged Collection
+     */
+    public static Collection plus(Collection left, Collection right) {
+        Collection answer;
+        if (left instanceof Set)
+            answer = new HashSet();
+        else
+            answer = new ArrayList(left.size() + right.size());
+        answer.addAll(left);
+        answer.addAll(right);
+        return answer;
+    }
+
+    /**
+     * Create a collection as a union of a Collection and an Object. If the collection
+     * is a Set, then the returned collection will be a Set otherwise a List.
+     * TODO: remove equivalent numbers after merge, e.g. 1L and 1G?
+     *
+     * @param left  a Collection
+     * @param right an object to add/append
+     * @return the resulting Collection
+     */
+    public static Collection plus(Collection left, Object right) {
+        Collection answer;
+        if (left instanceof Set)
+            answer = new HashSet();
+        else
+            answer = new ArrayList(left.size() + 1);
+        answer.addAll(left);
+        answer.add(right);
+        return answer;
+    }
+
+    /**
+     * Create a List composed of the elements of this list, repeated
+     * a certain number of times.  Note that for non- primitive
+     * elements, multiple references to the same instance will be added.
+     *
+     * @param self   a Collection
+     * @param factor the number of times to append
+     * @return the multiplied list
+     */
+    public static List multiply(Collection self, Number factor) {
+        int size = factor.intValue();
+        List answer = new ArrayList(self.size() * size);
+        for (int i = 0; i < size; i++) {
+            answer.addAll(self);
+        }
+        return answer;
+    }
+
+    /**
+     * Create a List composed of the intersection of both collections.  Any
+     * elements that exist in both collections are added to the resultant list.
+     *
+     * @param left  a Collection
+     * @param right a Collection
+     * @return a List as an intersection of both collections
+     */
+    public static List intersect(Collection left, Collection right) {
+        if (left.isEmpty())
+            return new ArrayList();
+
+        if (left.size() < right.size()) {
+            Collection swaptemp = left;
+            left = right;
+            right = swaptemp;
+        }
+
+        // TODO optimise if same type?
+        // boolean nlgnSort = sameType(new Collection[]{left, right});
+
+        List result = new ArrayList();
+        //creates the collection to look for values.
+        Collection pickFrom = new TreeSet(new NumberAwareComparator());
+        pickFrom.addAll(left);
+
+        for (Iterator iter = right.iterator(); iter.hasNext();) {
+            final Object o = iter.next();
+            if (pickFrom.contains(o))
+                result.add(o);
+        }
+        return result;
+    }
+
+    /**
+     * Returns <code>true</code> if the intersection of two collections is empty.
+     *
+     * @param left  a Collection
+     * @param right a Collection
+     * @return boolean   <code>true</code> if the intersection of two collections
+     *         is empty, <code>false</code> otherwise.
+     */
+    public static boolean disjoint(Collection left, Collection right) {
+
+        if (left.isEmpty() || right.isEmpty())
+            return true;
+
+        Collection pickFrom = new TreeSet(new NumberAwareComparator());
+        pickFrom.addAll(right);
+
+        for (Iterator iter = left.iterator(); iter.hasNext();) {
+            final Object o = iter.next();
+            if (pickFrom.contains(o))
+                return false;
+        }
+        return true;
+    }
+
+    // Default comparator for objects accounting for numbers of different types.
+    // Also handles nulls. Null is less than everything else.
+    private static class NumberAwareComparator implements Comparator {
+        public int compare(Object o1, Object o2) {
+            if (o1 == null) {
+                return o2 == null ? 0 : -1;
+            }
+            if (o2 == null) {
+                return 1;
+            }
+            if (o1 instanceof Number && o2 instanceof Number) {
+                BigDecimal x1 = new BigDecimal(String.valueOf(o1));
+                BigDecimal x2 = new BigDecimal(String.valueOf(o2));
+                return x1.compareTo(x2);
+            }
+            if (o1 instanceof Comparable && o2 instanceof Comparable &&
+                    (o1.getClass().isAssignableFrom(o2.getClass()) ||
+                            o2.getClass().isAssignableFrom(o1.getClass()))) {
+                return ((Comparable) o1).compareTo((Comparable) o2);
+            }
+            int x1 = o1.hashCode();
+            int x2 = o2.hashCode();
+            return (x1 - x2);
+        }
+
+        public boolean equals(Object obj) {
+            return this.equals(obj);
+        }
+    }
+
+    /**
+     * Compare the contents of this array to the contents of the given array.
+     *
+     * @param left an int array
+     * @param right the operand array.
+     * @return true if the contents of both arrays are equal.
+     */
+    public static boolean equals(int[] left, int[] right) {
+        if (left == null) {
+            return right == null;
+        }
+        if (right == null) {
+            return false;
+        }
+        if (left.length != right.length) {
+            return false;
+        }
+        for (int i = 0; i < left.length; i++) {
+            if (left[i] != right[i]) return false;
+        }
+        return true;
+    }
+
+    /**
+     * Determines if the contents of this array are equal to the
+     * contents of the given list, in the same order.  This returns
+     * <code>false</code> if either collection is <code>null</code>.
+     *
+     * @param left  this array
+     * @param right the list being compared
+     * @return true if the contents of both collections are equal
+     */
+    public static boolean equals(Object[] left, List right) {
+        return coercedEquals(left, right);
+    }
+
+    /**
+     * Determines if the contents of this list are equal to the
+     * contents of the given array in the same order.  This returns
+     * <code>false</code> if either collection is <code>null</code>.
+     *
+     * @param left  this List
+     * @param right this Object[] being compared to
+     * @return true if the contents of both collections are equal
+     */
+    public static boolean equals(List left, Object[] right) {
+        return coercedEquals(right, left);
+    }
+
+    private static boolean coercedEquals(Object[] left, List right) {
+        if (left == null) {
+            return right == null;
+        }
+        if (right == null) {
+            return false;
+        }
+        if (left.length != right.size()) {
+            return false;
+        }
+        final NumberAwareComparator numberAwareComparator = new NumberAwareComparator();
+        for (int i = left.length - 1; i >= 0; i--) {
+            final Object o1 = left[i];
+            final Object o2 = right.get(i);
+            if (o1 == null) {
+                if (o2 != null) return false;
+            } else {
+                if (o1 instanceof Number) {
+                    if (!(o2 instanceof Number && numberAwareComparator.compare(o1, o2) == 0)) {
+                        return false;
+                    }
+                } else {
+                    if (!DefaultTypeTransformation.compareEqual(o1, o2)) return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Compare the contents of two Lists.  Order matters.
+     * If numbers exist in the Lists, then they are compared as numbers,
+     * for example 2 == 2L.  If either list is <code>null</code>, the result
+     * is <code>false</code>.
+     *
+     * @param left  this List
+     * @param right the List being compared to.
+     * @return boolean   <code>true</code> if the contents of both lists are identical,
+     *         <code>false</code> otherwise.
+     */
+    public static boolean equals(List left, List right) {
+        if (left == null) {
+            return right == null;
+        }
+        if (right == null) {
+            return false;
+        }
+        if (left.size() != right.size()) {
+            return false;
+        }
+        final NumberAwareComparator numberAwareComparator = new NumberAwareComparator();
+        final Iterator it1 = left.iterator(), it2 = right.iterator();
+        while (it1.hasNext()) {
+            final Object o1 = it1.next();
+            final Object o2 = it2.next();
+            if (o1 == null) {
+                if (o2 != null) return false;
+            } else {
+                if (o1 instanceof Number) {
+                    if (!(o2 instanceof Number && numberAwareComparator.compare(o1, o2) == 0)) {
+                        return false;
+                    }
+                } else {
+                    if (!DefaultTypeTransformation.compareEqual(o1, o2)) return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Compare the contents of two Sets for equality using Groovy's coercion rules.
+     * WARNING: may not be included in 1.1
+     * <p/>
+     * Returns <tt>true</tt> if the two sets have the same size, and every member
+     * of the specified set is contained in this set (or equivalently, every member
+     * of this set is contained in the specified set).
+     * If numbers exist in the Lists, then they are compared as numbers,
+     * for example 2 == 2L.  If either list is <code>null</code>, the result
+     * is <code>false</code>.
+     *
+     * @param self  this List
+     * @param other the List being compared to
+     * @return <tt>true</tt> if the contents of both lists are identical
+     */
+    /*
+    public static boolean coercedEquals(Set self, Set other) {
+        if (self == null) {
+            return other == null;
+        }
+        if (other == null) {
+            return false;
+        }
+        if (self.size() != other.size()) {
+            return false;
+        }
+        final NumberAwareComparator numberAwareComparator = new NumberAwareComparator();
+        final Iterator it1 = self.iterator();
+        Collection otherItems = new HashSet(other);
+        while (it1.hasNext()) {
+            final Object o1 = it1.next();
+            if (o1 == null && !other.contains(null)) return false;
+            final Iterator it2 = otherItems.iterator();
+            Object foundItem = null;
+            while (it2.hasNext() && foundItem == null) {
+                final Object o2 = it2.next();
+                if (o1 instanceof Number) {
+                    if (o2 instanceof Number && numberAwareComparator.compare(o1, o2) == 0) {
+                        foundItem = o2;
+                    }
+                } else {
+                    try {
+                        if (DefaultTypeTransformation.compareEqual(o1, o2)) {
+                            foundItem = o2;
+                        }
+                    } catch (ClassCastException e) {
+                        // ignore
+                    }
+                }
+            }
+            if (foundItem == null) return false;
+            otherItems.remove(foundItem);
+        }
+        return otherItems.size() == 0;
+    }
+*/
+
+    /**
+     * Create a Set composed of the elements of the first set minus the
+     * elements of the given collection.
+     * <p/>
+     * TODO: remove using number comparator?
+     *
+     * @param self     a set object
+     * @param operands the items to remove from the set
+     * @return the resulting set
+     */
+    public static Set minus(Set self, Collection operands) {
+        if (self.size() == 0)
+            return new HashSet();
+        Set ansSet = new HashSet(self);
+        ansSet.removeAll(operands);
+        return ansSet;
+    }
+
+    /**
+     * Create a Set composed of the elements of the first set minus the operand.
+     *
+     * @param self    a set object
+     * @param operand the operand to remove from the set
+     * @return the resulting set
+     */
+    public static Set minus(Set self, Object operand) {
+        Set ansSet = new HashSet();
+        Comparator numberComparator = new NumberAwareComparator();
+        for (Iterator it = self.iterator(); it.hasNext();) {
+            Object o = it.next();
+            if (numberComparator.compare(o, operand) != 0) ansSet.add(o);
+        }
+        return ansSet;
+    }
+
+    /**
+     * Create a List composed of the elements of the first list minus the
+     * elements of the given collection.
+     *
+     * @param self     a List
+     * @param removeMe a Collection of elements to remove
+     * @return a List with the common elements removed
+     */
+    public static List minus(List self, Collection removeMe) {
+
+        if (self.size() == 0)
+            return new ArrayList();
+
+        boolean nlgnSort = sameType(new Collection[]{self, removeMe});
+
+        // We can't use the same tactic as for intersection
+        // since AbstractCollection only does a remove on the first
+        // element it encounters.
+
+        Comparator numberComparator = new NumberAwareComparator();
+
+        if (nlgnSort && (self.get(0) instanceof Comparable)) {
+            //n*LOG(n) version
+            Set answer;
+            if (Number.class.isInstance(self.get(0))) {
+                answer = new TreeSet(numberComparator);
+                answer.addAll(self);
+                for (Iterator it = self.iterator(); it.hasNext();) {
+                    Object o = it.next();
+                    if (Number.class.isInstance(o)) {
+                        for (Iterator it2 = removeMe.iterator(); it2.hasNext();) {
+                            Object o2 = it2.next();
+                            if (Number.class.isInstance(o2)) {
+                                if (numberComparator.compare(o, o2) == 0)
+                                    answer.remove(o);
+                            }
+                        }
+                    } else {
+                        if (removeMe.contains(o))
+                            answer.remove(o);
+                    }
+                }
+            } else {
+                answer = new TreeSet(numberComparator);
+                answer.addAll(self);
+                answer.removeAll(removeMe);
+            }
+
+            List ansList = new ArrayList();
+            for (Iterator it = self.iterator(); it.hasNext();) {
+                Object o = it.next();
+                if (answer.contains(o))
+                    ansList.add(o);
+            }
+            return ansList;
+        } else {
+            //n*n version
+            List tmpAnswer = new LinkedList(self);
+            for (Iterator iter = tmpAnswer.iterator(); iter.hasNext();) {
+                Object element = iter.next();
+                boolean elementRemoved = false;
+                for (Iterator iterator = removeMe.iterator(); iterator.hasNext() && !elementRemoved;) {
+                    Object elt = iterator.next();
+                    if (numberComparator.compare(element, elt) == 0) {
+                        iter.remove();
+                        elementRemoved = true;
+                    }
+                }
+            }
+
+            //remove duplicates
+            //can't use treeset since the base classes are different
+            return new ArrayList(tmpAnswer);
+        }
+    }
+
+    /**
+     * Create a new List composed of the elements of the first list minus the
+     * operand.
+     *
+     * @param self    a List object
+     * @param operand an element to remove from the list
+     * @return the resulting List with the operand removed
+     */
+    public static List minus(List self, Object operand) {
+        Comparator numberComparator = new NumberAwareComparator();
+        List ansList = new ArrayList();
+        for (Iterator it = self.iterator(); it.hasNext();) {
+            Object o = it.next();
+            if (numberComparator.compare(o, operand) != 0) ansList.add(o);
+        }
+        return ansList;
+    }
+
+    /**
+     * Flatten a list.  This collection and any nested collections have their
+     * contents (recursively) added to the new collection.
+     *
+     * @param self a List
+     * @return a flattened List
+     */
+    public static List flatten(List self) {
+        return new ArrayList(flatten(self, new LinkedList()));
+    }
+
+    /**
+     * Flatten a set.  This collection and any nested collections have their
+     * contents (recursively) added to the new collection.
+     *
+     * @param self a Set
+     * @return a flattened Set
+     */
+    public static Set flatten(Set self) {
+        return new HashSet(flatten(self, new LinkedList()));
+    }
+
+    private static List flatten(Collection elements, List addTo) {
+        Iterator iter = elements.iterator();
+        while (iter.hasNext()) {
+            Object element = iter.next();
+            if (element instanceof Collection) {
+                flatten((Collection) element, addTo);
+            } else if (element instanceof Map) {
+                flatten(((Map) element).values(), addTo);
+            } else {
+                addTo.add(element);
+            }
+        }
+        return addTo;
+    }
+
+    /**
+     * Overloads the left shift operator to provide an easy way to append
+     * objects to a Collection.
+     *
+     * @param self  a Collection
+     * @param value an Object to be added to the collection.
+     * @return same collection, after the value was added to it.
+     */
+    public static Collection leftShift(Collection self, Object value) {
+        self.add(value);
+        return self;
+    }
+
+    /**
+     * Overloads the left shift operator to provide an easy way to append multiple
+     * objects as string representations to a String.
+     *
+     * @param self  a String
+     * @param value an Obect
+     * @return a StringBuffer built from this string
+     */
+    public static StringBuffer leftShift(String self, Object value) {
+        return new StringBuffer(self).append(value);
+    }
+
+    protected static StringWriter createStringWriter(String self) {
+        StringWriter answer = new StringWriter();
+        answer.write(self);
+        return answer;
+    }
+
+    protected static StringBufferWriter createStringBufferWriter(StringBuffer self) {
+        return new StringBufferWriter(self);
+    }
+
+    /**
+     * Overloads the left shift operator to provide an easy way to append multiple
+     * objects as string representations to a StringBuffer.
+     *
+     * @param self  a StringBuffer
+     * @param value a value to append
+     * @return the StringBuffer on which this operation was invoked
+     */
+    public static StringBuffer leftShift(StringBuffer self, Object value) {
+        self.append(value);
+        return self;
+    }
+
+    /**
+     * Overloads the left shift operator to provide a mechanism to append
+     * values to a writer.
+     *
+     * @param self  a Writer
+     * @param value a value to append
+     * @return the writer on which this operation was invoked
+     * @throws IOException if an I/O error occurs.
+     */
+    public static Writer leftShift(Writer self, Object value) throws IOException {
+        InvokerHelper.write(self, value);
+        return self;
+    }
+
+    /**
+     * Implementation of the left shift operator for integral types.  Non integral
+     * Number types throw UnsupportedOperationException.
+     *
+     * @param self    a Number object
+     * @param operand the shift distance by which to left shift the number
+     * @return the resulting number
+     */
+    public static Number leftShift(Number self, Number operand) {
+        return NumberMath.leftShift(self, operand);
+    }
+
+    /**
+     * Implementation of the right shift operator for integral types.  Non integral
+     * Number types throw UnsupportedOperationException.
+     *
+     * @param self    a Number object
+     * @param operand the shift distance by which to right shift the number
+     * @return the resulting number
+     */
+    public static Number rightShift(Number self, Number operand) {
+        return NumberMath.rightShift(self, operand);
+    }
+
+    /**
+     * Implementation of the right shift (unsigned) operator for integral types.  Non integral
+     * Number types throw UnsupportedOperationException.
+     *
+     * @param self    a Number object
+     * @param operand the shift distance by which to right shift (unsigned) the number
+     * @return the resulting number
+     */
+    public static Number rightShiftUnsigned(Number self, Number operand) {
+        return NumberMath.rightShiftUnsigned(self, operand);
+    }
+
+    /**
+     * A helper method so that dynamic dispatch of the writer.write(object) method
+     * will always use the more efficient Writable.writeTo(writer) mechanism if the
+     * object implements the Writable interface.
+     *
+     * @param self     a Writer
+     * @param writable an object implementing the Writable interface
+     * @throws IOException if an I/O error occurs.
+     */
+    public static void write(Writer self, Writable writable) throws IOException {
+        writable.writeTo(self);
+    }
+
+    /**
+     * Overloads the leftShift operator to provide an append mechanism to add values to a stream.
+     *
+     * @param self  an OutputStream
+     * @param value a value to append
+     * @return a Writer
+     * @throws IOException if an I/O error occurs.
+     */
+    public static Writer leftShift(OutputStream self, Object value) throws IOException {
+        OutputStreamWriter writer = new FlushingStreamWriter(self);
+        leftShift(writer, value);
+        return writer;
+    }
+
+    /**
+     * Overloads the leftShift operator to add objects to an ObjectOutputStream.
+     *
+     * @param self  an ObjectOutputStream
+     * @param value an object to write to the stream
+     * @throws IOException if an I/O error occurs.
+     * @since 1.1 beta 2
+     */
+    public static void leftShift(ObjectOutputStream self, Object value) throws IOException {
+        self.writeObject(value);
+    }
+
+    /**
+     * Pipe an InputStream into an OutputStream for efficient stream copying.
+     *
+     * @param self stream on which to write
+     * @param in   stream to read from
+     * @return the outputstream itself
+     * @throws IOException if an I/O error occurs.
+     */
+    public static OutputStream leftShift(OutputStream self, InputStream in) throws IOException {
+        byte[] buf = new byte[1024];
+        while (true) {
+            int count = in.read(buf, 0, buf.length);
+            if (count == -1) break;
+            if (count == 0) {
+                Thread.yield();
+                continue;
+            }
+            self.write(buf, 0, count);
+        }
+        self.flush();
+        return self;
+    }
+
+    /**
+     * Overloads the leftShift operator to provide an append mechanism to add bytes to a stream.
+     *
+     * @param self  an OutputStream
+     * @param value a value to append
+     * @return an OutputStream
+     * @throws IOException if an I/O error occurs.
+     */
+    public static OutputStream leftShift(OutputStream self, byte[] value) throws IOException {
+        self.write(value);
+        self.flush();
+        return self;
+    }
+
+    /**
+     * Determines if all items of this array are of the same type.
+     * @param cols an array of collections
+     * @return true if the collections are all of the same type
+     */
+    private static boolean sameType(Collection[] cols) {
+        List all = new LinkedList();
+        for (int i = 0; i < cols.length; i++) {
+            all.addAll(cols[i]);
+        }
+        if (all.size() == 0)
+            return true;
+
+        Object first = all.get(0);
+
+        //trying to determine the base class of the collections
+        //special case for Numbers
+        Class baseClass;
+        if (first instanceof Number) {
+            baseClass = Number.class;
+        } else {
+            baseClass = first.getClass();
+        }
+
+        for (int i = 0; i < cols.length; i++) {
+            for (Iterator iter = cols[i].iterator(); iter.hasNext();) {
+                if (!baseClass.isInstance(iter.next())) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    // Primitive type array methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Support the subscript operator for a byte array
+     *
+     * @param array a byte array
+     * @param index the index of the item to retrieve
+     * @return the byte at the given index
+     */
+    public static Object getAt(byte[] array, int index) {
+        return primitiveArrayGet(array, index);
+    }
+
+    /**
+     * Support the subscript operator for a char array
+     *
+     * @param array a char array
+     * @param index the index of the item to retrieve
+     * @return the char at the given index
+     */
+    public static Object getAt(char[] array, int index) {
+        return primitiveArrayGet(array, index);
+    }
+
+    /**
+     * Support the subscript operator for a short array
+     *
+     * @param array a short array
+     * @param index the index of the item to retrieve
+     * @return the short at the given index
+     */
+    public static Object getAt(short[] array, int index) {
+        return primitiveArrayGet(array, index);
+    }
+
+    /**
+     * Support the subscript operator for an int array
+     *
+     * @param array an int array
+     * @param index the index of the item to retrieve
+     * @return the int at the given index
+     */
+    public static Object getAt(int[] array, int index) {
+        return primitiveArrayGet(array, index);
+    }
+
+    /**
+     * Support the subscript operator for a long array
+     *
+     * @param array a long array
+     * @param index the index of the item to retrieve
+     * @return the long at the given index
+     */
+    public static Object getAt(long[] array, int index) {
+        return primitiveArrayGet(array, index);
+    }
+
+    /**
+     * Support the subscript operator for a float array
+     *
+     * @param array a float array
+     * @param index the index of the item to retrieve
+     * @return the float at the given index
+     */
+    public static Object getAt(float[] array, int index) {
+        return primitiveArrayGet(array, index);
+    }
+
+    /**
+     * Support the subscript operator for a double array
+     *
+     * @param array a double array
+     * @param index the index of the item to retrieve
+     * @return the double at the given index
+     */
+    public static Object getAt(double[] array, int index) {
+        return primitiveArrayGet(array, index);
+    }
+
+    /**
+     * Support the subscript operator for a boolean array
+     *
+     * @param array a boolean array
+     * @param index the index of the item to retrieve
+     * @return the boolean at the given index
+     */
+    public static Object getAt(boolean[] array, int index) {
+        return primitiveArrayGet(array, index);
+    }
+
+    /**
+     * Support the subscript operator with a range for a byte array
+     *
+     * @param array a byte array
+     * @param range a range indicating the indices for the items to retrieve
+     * @return list of the retrieved bytes
+     */
+    public static Object getAt(byte[] array, Range range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with a range for a char array
+     *
+     * @param array a char array
+     * @param range a range indicating the indices for the items to retrieve
+     * @return list of the retrieved chars
+     */
+    public static Object getAt(char[] array, Range range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with a range for a short array
+     *
+     * @param array a short array
+     * @param range a range indicating the indices for the items to retrieve
+     * @return list of the retrieved shorts
+     */
+    public static Object getAt(short[] array, Range range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with a range for an int array
+     *
+     * @param array an int array
+     * @param range a range indicating the indices for the items to retrieve
+     * @return list of the ints at the given indices
+     */
+    public static Object getAt(int[] array, Range range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with a range for a long array
+     *
+     * @param array a long array
+     * @param range a range indicating the indices for the items to retrieve
+     * @return list of the retrieved longs
+     */
+    public static Object getAt(long[] array, Range range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with a range for a float array
+     *
+     * @param array a float array
+     * @param range a range indicating the indices for the items to retrieve
+     * @return list of the retrieved floats
+     */
+    public static Object getAt(float[] array, Range range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with a range for a double array
+     *
+     * @param array a double array
+     * @param range a range indicating the indices for the items to retrieve
+     * @return list of the retrieved doubles
+     */
+    public static Object getAt(double[] array, Range range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with a range for a boolean array
+     *
+     * @param array a boolean array
+     * @param range a range indicating the indices for the items to retrieve
+     * @return list of the retrieved booleans
+     */
+    public static Object getAt(boolean[] array, Range range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an IntRange for a byte array
+     *
+     * @param array a byte array
+     * @param range an IntRange indicating the indices for the items to retrieve
+     * @return list of the retrieved bytes
+     */
+    public static Object getAt(byte[] array, IntRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an IntRange for a char array
+     *
+     * @param array a char array
+     * @param range an IntRange indicating the indices for the items to retrieve
+     * @return list of the retrieved chars
+     */
+    public static Object getAt(char[] array, IntRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an IntRange for a short array
+     *
+     * @param array a short array
+     * @param range an IntRange indicating the indices for the items to retrieve
+     * @return list of the retrieved shorts
+     */
+    public static Object getAt(short[] array, IntRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an IntRange for an int array
+     *
+     * @param array an int array
+     * @param range an IntRange indicating the indices for the items to retrieve
+     * @return list of the retrieved ints
+     */
+    public static Object getAt(int[] array, IntRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an IntRange for a long array
+     *
+     * @param array a long array
+     * @param range an IntRange indicating the indices for the items to retrieve
+     * @return list of the retrieved longs
+     */
+    public static Object getAt(long[] array, IntRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an IntRange for a float array
+     *
+     * @param array a float array
+     * @param range an IntRange indicating the indices for the items to retrieve
+     * @return list of the retrieved floats
+     */
+    public static Object getAt(float[] array, IntRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an IntRange for a double array
+     *
+     * @param array a double array
+     * @param range an IntRange indicating the indices for the items to retrieve
+     * @return list of the retrieved doubles
+     */
+    public static Object getAt(double[] array, IntRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an IntRange for a boolean array
+     *
+     * @param array a boolean array
+     * @param range an IntRange indicating the indices for the items to retrieve
+     * @return list of the retrieved booleans
+     */
+    public static Object getAt(boolean[] array, IntRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an ObjectRange for a byte array
+     *
+     * @param array a byte array
+     * @param range an ObjectRange indicating the indices for the items to retrieve
+     * @return list of the retrieved bytes
+     */
+    public static Object getAt(byte[] array, ObjectRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an ObjectRange for a char array
+     *
+     * @param array a char array
+     * @param range an ObjectRange indicating the indices for the items to retrieve
+     * @return list of the retrieved chars
+     */
+    public static Object getAt(char[] array, ObjectRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an ObjectRange for a short array
+     *
+     * @param array a short array
+     * @param range an ObjectRange indicating the indices for the items to retrieve
+     * @return list of the retrieved shorts
+     */
+    public static Object getAt(short[] array, ObjectRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an ObjectRange for an int array
+     *
+     * @param array an int array
+     * @param range an ObjectRange indicating the indices for the items to retrieve
+     * @return list of the retrieved ints
+     */
+    public static Object getAt(int[] array, ObjectRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an ObjectRange for a long array
+     *
+     * @param array a long array
+     * @param range an ObjectRange indicating the indices for the items to retrieve
+     * @return list of the retrieved longs
+     */
+    public static Object getAt(long[] array, ObjectRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an ObjectRange for a float array
+     *
+     * @param array a float array
+     * @param range an ObjectRange indicating the indices for the items to retrieve
+     * @return list of the retrieved floats
+     */
+    public static Object getAt(float[] array, ObjectRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an ObjectRange for a double array
+     *
+     * @param array a double array
+     * @param range an ObjectRange indicating the indices for the items to retrieve
+     * @return list of the retrieved doubles
+     */
+    public static Object getAt(double[] array, ObjectRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with an ObjectRange for a byte array
+     *
+     * @param array a byte array
+     * @param range an ObjectRange indicating the indices for the items to retrieve
+     * @return list of the retrieved bytes
+     */
+    public static Object getAt(boolean[] array, ObjectRange range) {
+        return primitiveArrayGet(array, range);
+    }
+
+    /**
+     * Support the subscript operator with a collection for a byte array
+     *
+     * @param array a byte array
+     * @param indices a collection of indices for the items to retrieve
+     * @return list of the bytes at the given indices
+     */
+    public static Object getAt(byte[] array, Collection indices) {
+        return primitiveArrayGet(array, indices);
+    }
+
+    /**
+     * Support the subscript operator with a collection for a char array
+     *
+     * @param array a char array
+     * @param indices a collection of indices for the items to retrieve
+     * @return list of the chars at the given indices
+     */
+    public static Object getAt(char[] array, Collection indices) {
+        return primitiveArrayGet(array, indices);
+    }
+
+    /**
+     * Support the subscript operator with a collection for a short array
+     *
+     * @param array a short array
+     * @param indices a collection of indices for the items to retrieve
+     * @return list of the shorts at the given indices
+     */
+    public static Object getAt(short[] array, Collection indices) {
+        return primitiveArrayGet(array, indices);
+    }
+
+    /**
+     * Support the subscript operator with a collection for an int array
+     *
+     * @param array an int array
+     * @param indices a collection of indices for the items to retrieve
+     * @return list of the ints at the given indices
+     */
+    public static Object getAt(int[] array, Collection indices) {
+        return primitiveArrayGet(array, indices);
+    }
+
+    /**
+     * Support the subscript operator with a collection for a long array
+     *
+     * @param array a long array
+     * @param indices a collection of indices for the items to retrieve
+     * @return list of the longs at the given indices
+     */
+    public static Object getAt(long[] array, Collection indices) {
+        return primitiveArrayGet(array, indices);
+    }
+
+    /**
+     * Support the subscript operator with a collection for a float array
+     *
+     * @param array a float array
+     * @param indices a collection of indices for the items to retrieve
+     * @return list of the floats at the given indices
+     */
+    public static Object getAt(float[] array, Collection indices) {
+        return primitiveArrayGet(array, indices);
+    }
+
+    /**
+     * Support the subscript operator with a collection for a double array
+     *
+     * @param array a double array
+     * @param indices a collection of indices for the items to retrieve
+     * @return list of the doubles at the given indices
+     */
+    public static Object getAt(double[] array, Collection indices) {
+        return primitiveArrayGet(array, indices);
+    }
+
+    /**
+     * Support the subscript operator with a collection for a boolean array
+     *
+     * @param array a boolean array
+     * @param indices a collection of indices for the items to retrieve
+     * @return list of the booleans at the given indices
+     */
+    public static Object getAt(boolean[] array, Collection indices) {
+        return primitiveArrayGet(array, indices);
+    }
+
+    /**
+     * Support the subscript operator for a Bitset
+     *
+     * @param self  a BitSet
+     * @param index index to retrieve
+     * @return value of the bit at the given index
+     * @see java.util.BitSet
+     */
+    public static boolean getAt(BitSet self, int index) {
+        return self.get(index);
+    }
+
+    /**
+     * Support retrieving a subset of a BitSet using a Range
+     *
+     * @param self  a BitSet
+     * @param range a Range defining the desired subset
+     * @return a new BitSet that represents the requested subset
+     * @see java.util.BitSet
+     * @see groovy.lang.IntRange
+     */
+    public static BitSet getAt(BitSet self, IntRange range) {
+        int from = DefaultTypeTransformation.intUnbox(range.getFrom());
+        int to = DefaultTypeTransformation.intUnbox(range.getTo());
+
+        BitSet result = new BitSet();
+
+        int numberOfBits = to - from + 1;
+        int adjuster = 1;
+        int offset = from;
+
+        if (range.isReverse()) {
+            adjuster = -1;
+            offset = to;
+        }
+
+        for (int i = 0; i < numberOfBits; i++) {
+            result.set(i, self.get(offset + (adjuster * i)));
+        }
+
+        return result;
+    }
+
+    public static Boolean putAt(boolean[] array, int idx, Boolean newValue) {
+        return (Boolean) primitiveArrayPut(array, idx, newValue);
+    }
+
+    public static Byte putAt(byte[] array, int idx, Object newValue) {
+        if (!(newValue instanceof Byte)) {
+            Number n = (Number) newValue;
+            newValue = new Byte(n.byteValue());
+        }
+        return (Byte) primitiveArrayPut(array, idx, newValue);
+    }
+
+    public static Character putAt(char[] array, int idx, Object newValue) {
+        if (newValue instanceof String) {
+            String s = (String) newValue;
+            if (s.length() != 1) throw new IllegalArgumentException("String of length 1 expected but got a bigger one");
+            char c = s.charAt(0);
+            newValue = new Character(c);
+        }
+        return (Character) primitiveArrayPut(array, idx, newValue);
+    }
+
+    public static Short putAt(short[] array, int idx, Object newValue) {
+        if (!(newValue instanceof Short)) {
+            Number n = (Number) newValue;
+            newValue = new Short(n.shortValue());
+        }
+        return (Short) primitiveArrayPut(array, idx, newValue);
+    }
+
+    public static Integer putAt(int[] array, int idx, Object newValue) {
+        if (!(newValue instanceof Integer)) {
+            Number n = (Number) newValue;
+            newValue = new Integer(n.intValue());
+        }
+        return (Integer) primitiveArrayPut(array, idx, newValue);
+    }
+
+    public static Long putAt(long[] array, int idx, Object newValue) {
+        if (!(newValue instanceof Long)) {
+            Number n = (Number) newValue;
+            newValue = new Long(n.longValue());
+        }
+        return (Long) primitiveArrayPut(array, idx, newValue);
+    }
+
+    public static Float putAt(float[] array, int idx, Object newValue) {
+        if (!(newValue instanceof Float)) {
+            Number n = (Number) newValue;
+            newValue = new Float(n.floatValue());
+        }
+        return (Float) primitiveArrayPut(array, idx, newValue);
+    }
+
+    public static Double putAt(double[] array, int idx, Object newValue) {
+        if (!(newValue instanceof Double)) {
+            Number n = (Number) newValue;
+            newValue = new Double(n.doubleValue());
+        }
+        return (Double) primitiveArrayPut(array, idx, newValue);
+    }
+
+    /**
+     * Support assigning a range of values with a single assignment statement.
+     *
+     * @param self  a BitSet
+     * @param range the range of values to set
+     * @param value value
+     * @see java.util.BitSet
+     * @see groovy.lang.Range
+     */
+    public static void putAt(BitSet self, IntRange range, boolean value) {
+        int from = DefaultTypeTransformation.intUnbox(range.getFrom());
+        int to = DefaultTypeTransformation.intUnbox(range.getTo());
+
+        // If this is a backwards range, reverse the arguments to set.
+        if (from > to) {
+            int tmp = to;
+            to = from;
+            from = tmp;
+        }
+        self.set(from, to + 1, value);
+    }
+
+    /**
+     * Support subscript-style assignment for a BitSet.
+     *
+     * @param self  a BitSet
+     * @param index index of the entry to set
+     * @param value value
+     * @see java.util.BitSet
+     */
+    public static void putAt(BitSet self, int index, boolean value) {
+        self.set(index, value);
+    }
+
+    /**
+     * Allows arrays to behave similar to collections.
+     * @param array a boolean array
+     * @return the length of the array
+     * @see Array#getLength(Object)
+     */
+    public static int size(boolean[] array) {
+        return Array.getLength(array);
+    }
+
+    /**
+     * Allows arrays to behave similar to collections.
+     * @param array a byte array
+     * @return the length of the array
+     * @see Array#getLength(Object)
+     */
+    public static int size(byte[] array) {
+        return Array.getLength(array);
+    }
+
+    /**
+     * Allows arrays to behave similar to collections.
+     * @param array a char array
+     * @return the length of the array
+     * @see Array#getLength(Object)
+     */
+    public static int size(char[] array) {
+        return Array.getLength(array);
+    }
+
+    /**
+     * Allows arrays to behave similar to collections.
+     * @param array a short array
+     * @return the length of the array
+     * @see Array#getLength(Object)
+     */
+    public static int size(short[] array) {
+        return Array.getLength(array);
+    }
+
+    /**
+     * Allows arrays to behave similar to collections.
+     * @param array an int array
+     * @return the length of the array
+     * @see Array#getLength(Object)
+     */
+    public static int size(int[] array) {
+        return Array.getLength(array);
+    }
+
+    /**
+     * Allows arrays to behave similar to collections.
+     * @param array a long array
+     * @return the length of the array
+     * @see Array#getLength(Object)
+     */
+    public static int size(long[] array) {
+        return Array.getLength(array);
+    }
+
+    /**
+     * Allows arrays to behave similar to collections.
+     * @param array a float array
+     * @return the length of the array
+     * @see Array#getLength(Object)
+     */
+    public static int size(float[] array) {
+        return Array.getLength(array);
+    }
+
+    /**
+     * Allows arrays to behave similar to collections.
+     * @param array a double array
+     * @return the length of the array
+     * @see Array#getLength(Object)
+     */
+    public static int size(double[] array) {
+        return Array.getLength(array);
+    }
+
+    /**
+     * Converts this array to a List of the same size, with each element
+     * added to the list.
+     *
+     * @param array an array
+     * @return a list containing the contents of this array.
+     */
+    public static List toList(byte[] array) {
+        return DefaultTypeTransformation.primitiveArrayToList(array);
+    }
+
+    /**
+     * Converts this array to a List of the same size, with each element
+     * added to the list.
+     *
+     * @param array an array
+     * @return a list containing the contents of this array.
+     */
+    public static List toList(char[] array) {
+        return DefaultTypeTransformation.primitiveArrayToList(array);
+    }
+
+    /**
+     * Converts this array to a List of the same size, with each element
+     * added to the list.
+     *
+     * @param array an array
+     * @return a list containing the contents of this array.
+     */
+    public static List toList(short[] array) {
+        return DefaultTypeTransformation.primitiveArrayToList(array);
+    }
+
+    /**
+     * Converts this array to a List of the same size, with each element
+     * added to the list.
+     *
+     * @param array an array
+     * @return a list containing the contents of this array.
+     */
+    public static List toList(int[] array) {
+        return DefaultTypeTransformation.primitiveArrayToList(array);
+    }
+
+    /**
+     * Converts this array to a List of the same size, with each element
+     * added to the list.
+     *
+     * @param array an array
+     * @return a list containing the contents of this array.
+     */
+    public static List toList(long[] array) {
+        return DefaultTypeTransformation.primitiveArrayToList(array);
+    }
+
+    /**
+     * Converts this array to a List of the same size, with each element
+     * added to the list.
+     *
+     * @param array an array
+     * @return a list containing the contents of this array.
+     */
+    public static List toList(float[] array) {
+        return DefaultTypeTransformation.primitiveArrayToList(array);
+    }
+
+    /**
+     * Converts this array to a List of the same size, with each element
+     * added to the list.
+     *
+     * @param array an array
+     * @return a list containing the contents of this array.
+     */
+    public static List toList(double[] array) {
+        return DefaultTypeTransformation.primitiveArrayToList(array);
+    }
+
+    private static final char[] T_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".toCharArray();
+
+    public static Writable encodeBase64(Byte[] data) {
+        return encodeBase64(DefaultTypeTransformation.convertToByteArray(data));
+    }
+
+    /**
+     * Produce a Writable object which writes the base64 encoding of the byte array.
+     * Calling toString() on the result rerurns the encoding as a String
+     *
+     * @param data byte array to be encoded
+     * @return object which will write the base64 encoding of the byte array
+     */
+    public static Writable encodeBase64(final byte[] data) {
+        return new Writable() {
+            public Writer writeTo(final Writer writer) throws IOException {
+                int charCount = 0;
+                final int dLimit = (data.length / 3) * 3;
+
+                for (int dIndex = 0; dIndex != dLimit; dIndex += 3) {
+                    int d = ((data[dIndex] & 0XFF) << 16) | ((data[dIndex + 1] & 0XFF) << 8) | (data[dIndex + 2] & 0XFF);
+
+                    writer.write(T_TABLE[d >> 18]);
+                    writer.write(T_TABLE[(d >> 12) & 0X3F]);
+                    writer.write(T_TABLE[(d >> 6) & 0X3F]);
+                    writer.write(T_TABLE[d & 0X3F]);
+
+                    if (++charCount == 18) {
+                        writer.write('\n');
+                        charCount = 0;
+                    }
+                }
+
+                if (dLimit != data.length) {
+                    int d = (data[dLimit] & 0XFF) << 16;
+
+                    if (dLimit + 1 != data.length) {
+                        d |= (data[dLimit + 1] & 0XFF) << 8;
+                    }
+
+                    writer.write(T_TABLE[d >> 18]);
+                    writer.write(T_TABLE[(d >> 12) & 0X3F]);
+                    writer.write((dLimit + 1 < data.length) ? T_TABLE[(d >> 6) & 0X3F] : '=');
+                    writer.write('=');
+                }
+
+                return writer;
+            }
+
+            public String toString() {
+                StringWriter buffer = new StringWriter();
+
+                try {
+                    writeTo(buffer);
+                } catch (IOException e) {
+                    throw new StringWriterIOException(e);
+                }
+
+                return buffer.toString();
+            }
+        };
+    }
+
+    private static final byte[] TRANSLATE_TABLE = (
+            //
+            "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+                    //                    \t    \n                \r
+                    + "\u0042\u0042\u0041\u0041\u0042\u0042\u0041\u0042"
+                    //
+                    + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+                    //
+                    + "\u0042\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+                    //        sp    !     "     #     $     %     &     '
+                    + "\u0041\u0042\u0042\u0042\u0042\u0042\u0042\u0042"
+                    //         (    )     *     +     ,     -     .     /
+                    + "\u0042\u0042\u0042\u003E\u0042\u0042\u0042\u003F"
+                    //         0    1     2     3     4     5     6     7
+                    + "\u0034\u0035\u0036\u0037\u0038\u0039\u003A\u003B"
+                    //         8    9     :     ;     <     =     >     ?
+                    + "\u003C\u003D\u0042\u0042\u0042\u0040\u0042\u0042"
+                    //         @    A     B     C     D     E     F     G
+                    + "\u0042\u0000\u0001\u0002\u0003\u0004\u0005\u0006"
+                    //         H    I   J K   L     M   N   O
+                    + "\u0007\u0008\t\n\u000B\u000C\r\u000E"
+                    //         P    Q     R     S     T     U     V    W
+                    + "\u000F\u0010\u0011\u0012\u0013\u0014\u0015\u0016"
+                    //         X    Y     Z     [     \     ]     ^    _
+                    + "\u0017\u0018\u0019\u0042\u0042\u0042\u0042\u0042"
+                    //         '    a     b     c     d     e     f     g
+                    + "\u0042\u001A\u001B\u001C\u001D\u001E\u001F\u0020"
+                    //        h   i   j     k     l     m     n     o    p
+                    + "\u0021\"\u0023\u0024\u0025\u0026\u0027\u0028"
+                    //        p     q     r     s     t     u     v     w
+                    + "\u0029\u002A\u002B\u002C\u002D\u002E\u002F\u0030"
+                    //        x     y     z
+                    + "\u0031\u0032\u0033").getBytes();
+
+    /**
+     * Decode the Sting from base64 into a byte array.
+     *
+     * @param value the string to be decoded
+     * @return the decoded bytes as an array
+     */
+    public static byte[] decodeBase64(String value) {
+        int byteShift = 4;
+        int tmp = 0;
+        boolean done = false;
+        final StringBuffer buffer = new StringBuffer();
+
+        for (int i = 0; i != value.length(); i++) {
+            final char c = value.charAt(i);
+            final int sixBit = (c < 123) ? TRANSLATE_TABLE[c] : 66;
+
+            if (sixBit < 64) {
+                if (done)
+                    throw new RuntimeException("= character not at end of base64 value"); // TODO: change this exception type
+
+                tmp = (tmp << 6) | sixBit;
+
+                if (byteShift-- != 4) {
+                    buffer.append((char) ((tmp >> (byteShift * 2)) & 0XFF));
+                }
+
+            } else if (sixBit == 64) {
+
+                byteShift--;
+                done = true;
+
+            } else if (sixBit == 66) {
+                // RFC 2045 says that I'm allowed to take the presence of
+                // these characters as evedence of data corruption
+                // So I will
+                throw new RuntimeException("bad character in base64 value"); // TODO: change this exception type
+            }
+
+            if (byteShift == 0) byteShift = 4;
+        }
+
+        try {
+            return buffer.toString().getBytes("ISO-8859-1");
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("Base 64 decode produced byte values > 255"); // TODO: change this exception type
+        }
+    }
+
+    /**
+     * Implements the getAt(int) method for primitve type arrays.
+     *
+     * @param self an array object
+     * @param idx  the index of interest
+     * @return the returned value from the array
+     */
+    protected static Object primitiveArrayGet(Object self, int idx) {
+        return Array.get(self, normaliseIndex(idx, Array.getLength(self)));
+    }
+
+    /**
+     * Implements the getAt(Range) method for primitve type arrays.
+     *
+     * @param self  an array object
+     * @param range the range of indices of interest
+     * @return the returned values from the array corresponding to the range
+     */
+    protected static List primitiveArrayGet(Object self, Range range) {
+        List answer = new ArrayList();
+        for (Iterator iter = range.iterator(); iter.hasNext();) {
+            int idx = DefaultTypeTransformation.intUnbox(iter.next());
+            answer.add(primitiveArrayGet(self, idx));
+        }
+        return answer;
+    }
+
+    /**
+     * Implements the getAt(Collection) method for primitve type arrays.  Each
+     * value in the collection argument is assumed to be a valid array index.
+     * The value at each index is then added to a list which is returned.
+     *
+     * @param self    an array object
+     * @param indices the indices of interest
+     * @return the returned values from the array
+     */
+    protected static List primitiveArrayGet(Object self, Collection indices) {
+        List answer = new ArrayList();
+        for (Iterator iter = indices.iterator(); iter.hasNext();) {
+            Object value = iter.next();
+            if (value instanceof Range) {
+                answer.addAll(primitiveArrayGet(self, (Range) value));
+            } else if (value instanceof List) {
+                answer.addAll(primitiveArrayGet(self, (List) value));
+            } else {
+                int idx = DefaultTypeTransformation.intUnbox(value);
+                answer.add(primitiveArrayGet(self, idx));
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * Implements the setAt(int idx) method for primitve type arrays.
+     *
+     * @param self     an object
+     * @param idx      the index of interest
+     * @param newValue the new value to be put into the index of interest
+     * @return the added value
+     */
+    protected static Object primitiveArrayPut(Object self, int idx, Object newValue) {
+        Array.set(self, normaliseIndex(idx, Array.getLength(self)), newValue);
+        return newValue;
+    }
+
+    // String methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Converts the given string into a Character object
+     * using the first character in the string.
+     *
+     * @param self a String
+     * @return the first Character
+     */
+    public static Character toCharacter(String self) {
+        /** @todo use cache? */
+        return new Character(self.charAt(0));
+    }
+
+    /**
+     * Converts the given string into a Boolean object.
+     * If the trimmed string is "true", "y" or "1" (ignoring case)
+     * then the result is true othewrwise it is false.
+     *
+     * @param self a String
+     * @return The Boolean value
+     */
+    public static Boolean toBoolean(String self) {
+        final String trimmed = self.trim();
+
+        if ("true".equalsIgnoreCase(trimmed) || "y".equalsIgnoreCase(trimmed) || "1".equals(trimmed)) {
+            return Boolean.TRUE;
+        } else {
+            return Boolean.FALSE;
+        }
+    }
+
+    /**
+     * Convenience method to split a string (with whitespace as delimiter)
+     * Like tokenize, but returns an Array of Strings instead of a List
+     *
+     * @param self the string to split
+     * @return String[] result of split
+     */
+    public static String[] split(String self) {
+        StringTokenizer st = new StringTokenizer(self);
+        String[] strings = new String[st.countTokens()];
+        for (int i = 0; i < strings.length; i++) {
+            strings[i] = st.nextToken();
+        }
+        return strings;
+    }
+
+    /**
+     * Tokenize a String based on the given string delimiter.
+     *
+     * @param self  a String
+     * @param token the delimiter
+     * @return a List of tokens
+     * @see java.util.StringTokenizer#StringTokenizer(java.lang.String, java.lang.String)
+     */
+    public static List tokenize(String self, String token) {
+        return InvokerHelper.asList(new StringTokenizer(self, token));
+    }
+
+    /**
+     * Tokenize a String (with a whitespace as the delimiter).
+     *
+     * @param self a String
+     * @return a List of tokens
+     * @see StringTokenizer#StringTokenizer(java.lang.String)
+     */
+    public static List tokenize(String self) {
+        return InvokerHelper.asList(new StringTokenizer(self));
+    }
+
+    /**
+     * Appends the String representation of the given operand to this string.
+     *
+     * @param left  a String
+     * @param value any Object
+     * @return the new string with the object appended
+     */
+    public static String plus(String left, Object value) {
+        return left + toString(value);
+    }
+
+    /**
+     * Appends a String to the string representation of this number.
+     *
+     * @param value a Number
+     * @param right a String
+     * @return a String
+     */
+    public static String plus(Number value, String right) {
+        return toString(value) + right;
+    }
+
+    /**
+     * Appends a String to this StringBuffer.
+     *
+     * @param left  a StringBuffer
+     * @param value a String
+     * @return a String
+     */
+    public static String plus(StringBuffer left, String value) {
+        return left + value;
+    }
+
+
+    /**
+     * Remove a part of a String.  This essentially replaces the first
+     * occurrence of the operand with '' and returns the result.
+     *
+     * @param left  a String
+     * @param value a String part to remove
+     * @return a String minus the part to be removed
+     * @see String#replaceFirst(String,String)
+     */
+    public static String minus(String left, Object value) {
+        String text = toString(value);
+        return left.replaceFirst(text, "");
+    }
+
+    /**
+     * Provide an implementation of contains() like
+     * {@link Collection#contains(Object)} to make Strings more polymorphic.
+     * This method is not required on JDK 1.5 onwards
+     *
+     * @param self a String
+     * @param text a String to look for
+     * @return true if this string contains the given text
+     */
+    public static boolean contains(String self, String text) {
+        int idx = self.indexOf(text);
+        return idx >= 0;
+    }
+
+    /**
+     * Count the number of occurencies of a substring.
+     *
+     * @param self a String
+     * @param text a substring
+     * @return the number of occurrencies of the given string inside this String
+     */
+    public static int count(String self, String text) {
+        int answer = 0;
+        for (int idx = 0; true; idx++) {
+            idx = self.indexOf(text, idx);
+            if (idx >= 0) {
+                ++answer;
+            } else {
+                break;
+            }
+        }
+        return answer;
+    }
+
+    /**
+     * This method is called by the ++ operator for the class String.
+     * It increments the last character in the given string. If the
+     * character in the string is Character.MAX_VALUE a Character.MIN_VALUE
+     * will be appended. The empty string is incremented to a string
+     * consisting of the character Character.MIN_VALUE.
+     *
+     * @param self a String
+     * @return an incremented String
+     */
+    public static String next(String self) {
+        StringBuffer buffer = new StringBuffer(self);
+        if (buffer.length() == 0) {
+            buffer.append(Character.MIN_VALUE);
+        } else {
+            char last = buffer.charAt(buffer.length() - 1);
+            if (last == Character.MAX_VALUE) {
+                buffer.append(Character.MIN_VALUE);
+            } else {
+                char next = last;
+                next++;
+                buffer.setCharAt(buffer.length() - 1, next);
+            }
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * This method is called by the -- operator for the class String.
+     * It decrements the last character in the given string. If the
+     * character in the string is Character.MIN_VALUE it will be deleted.
+     * The empty string can't be decremented.
+     *
+     * @param self a String
+     * @return a String with a decremented digit at the end
+     */
+    public static String previous(String self) {
+        StringBuffer buffer = new StringBuffer(self);
+        if (buffer.length() == 0) throw new IllegalArgumentException("the string is empty");
+        char last = buffer.charAt(buffer.length() - 1);
+        if (last == Character.MIN_VALUE) {
+            buffer.deleteCharAt(buffer.length() - 1);
+        } else {
+            char next = last;
+            next--;
+            buffer.setCharAt(buffer.length() - 1, next);
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * Executes the given string as a command line process. For more control
+     * over the process mechanism in JDK 1.5 you can use java.lang.ProcessBuilder.
+     *
+     * @param self a command line String
+     * @return the Process which has just started for this command line string
+     * @throws IOException if an IOException occurs.
+     */
+    public static Process execute(String self) throws IOException {
+        return Runtime.getRuntime().exec(self);
+    }
+
+    /**
+     * Executes the command specified by the <code>String</code> array that is the parameter.
+     * The first item in the array is the command the others are the parameters. For more
+     * control over the process mechanism in JDK 1.5 you can use
+     * <code>java.lang.ProcessBuilder</code>.
+     *
+     * @param commandArray an array of <code>String<code> containing the command name and
+     *                     parameters as separate items in the array.
+     * @return the Process which has just started for this command line string.
+     * @throws IOException if an IOException occurs.
+     */
+    public static Process execute(String[] commandArray) throws IOException {
+        return Runtime.getRuntime().exec(commandArray);
+    }
+
+    /**
+     * Executes the command specified by the <code>self</code> with environments <code>envp</code>
+     * under the working directory <code>dir</code>.
+     * For more control over the process mechanism in JDK 1.5 you can use <code>java.lang.ProcessBuilder</code>.
+     *
+     * @param self a command line String to be executed.
+     * @param envp an array of Strings, each element of which
+     *             has environment variable settings in the format
+     *             <i>name</i>=<i>value</i>, or
+     *             <tt>null</tt> if the subprocess should inherit
+     *             the environment of the current process.
+     * @param dir  the working directory of the subprocess, or
+     *             <tt>null</tt> if the subprocess should inherit
+     *             the working directory of the current process.
+     * @return the Process which has just started for this command line string.
+     * @throws IOException if an IOException occurs.
+     */
+    public static Process execute(String self, final String[] envp, File dir) throws IOException {
+        return Runtime.getRuntime().exec(self, envp, dir);
+    }
+
+    /**
+     * Executes the command specified by the <code>String</code> list that is the parameter.
+     * The first item in the array is the command the others are the parameters. All entries
+     * must be <code>String</code>s.  For more control over the process mechanism in JDK 1.5 you
+     * can use <code>java.lang.ProcessBuilder</code>.
+     *
+     * @param commandList a list of <code>String<code> containing the command name and
+     *                    parameters as separate items in the list.
+     * @return the Process which has just started for this command line string.
+     * @throws IOException if an IOException occurs.
+     */
+    public static Process execute(List commandList) throws IOException {
+        final String[] commandArray = new String[commandList.size()];
+        Iterator it = commandList.iterator();
+        for (int i = 0; it.hasNext(); ++i) {
+            commandArray[i] = it.next().toString();
+        }
+        return execute(commandArray);
+    }
+
+    /**
+     * Executes the command specified by the <code>self</code> with environments <code>envp</code>
+     * under the working directory <code>dir</code>.
+     * For more control over the process mechanism in JDK 1.5 you can use <code>java.lang.ProcessBuilder</code>.
+     *
+     * @param self a command line String to be executed.
+     * @param envp a List of Strings, each member of which
+     *             has environment variable settings in the format
+     *             <i>name</i>=<i>value</i>, or
+     *             <tt>null</tt> if the subprocess should inherit
+     *             the environment of the current process.
+     * @param dir  the working directory of the subprocess, or
+     *             <tt>null</tt> if the subprocess should inherit
+     *             the working directory of the current process.
+     * @return the Process which has just started for this command line string.
+     * @throws IOException if an IOException occurs.
+     */
+    public static Process execute(String self, List envp, File dir) throws IOException {
+        if (envp == null) {
+            return execute(self, (String[]) null, dir);
+        }
+        String[] commandArray = new String[envp.size()];
+        Iterator it = envp.iterator();
+        for (int i = 0; it.hasNext(); ++i) {
+            commandArray[i] = it.next().toString();
+        }
+        return execute(self, commandArray, dir);
+    }
+
+    /**
+     * Repeat a String a certain number of times.
+     *
+     * @param self   a String to be repeated
+     * @param factor the number of times the String should be repeated
+     * @return a String composed of a repetition
+     * @throws IllegalArgumentException if the number of repeatition is &lt; 0
+     */
+    public static String multiply(String self, Number factor) {
+        int size = factor.intValue();
+        if (size == 0)
+            return "";
+        else if (size < 0) {
+            throw new IllegalArgumentException("multiply() should be called with a number of 0 or greater not: " + size);
+        }
+        StringBuffer answer = new StringBuffer(self);
+        for (int i = 1; i < size; i++) {
+            answer.append(self);
+        }
+        return answer.toString();
+    }
+
+    /**
+     * Returns the string representation of the given map.
+     *
+     * @param self a Map
+     * @return the string representation
+     * @see #toMapString(Map)
+     */
+    public static String toString(Map self) {
+        return toMapString(self);
+    }
+
+    /**
+     * Returns the string representation of this map.  The string displays the
+     * contents of the map, i.e. <code>{one=1, two=2, three=3}</code>.
+     *
+     * @param self a Map
+     * @return the string representation
+     */
+    public static String toMapString(Map self) {
+        return (self == null) ? "null" : InvokerHelper.toMapString(self);
+    }
+
+    /**
+     * Returns the string representation of the given collection.  The string
+     * displays the contents of the collection, i.e.
+     * <code>[1, 2, a]</code>.
+     *
+     * @param self a Collection
+     * @return the string representation
+     * @see #toListString(Collection)
+     */
+    public static String toString(Collection self) {
+        return toListString(self);
+    }
+
+    /**
+     * Returns the string representation of the given list.  The string
+     * displays the contents of the list, similar to a list literal, i.e.
+     * <code>[1, 2, a]</code>.
+     *
+     * @param self a Collection
+     * @return the string representation
+     */
+    public static String toListString(Collection self) {
+        return (self == null) ? "null" : InvokerHelper.toListString(self);
+    }
+
+    /**
+     * Returns the string representation of this array's contents.
+     *
+     * @param self an Object[]
+     * @return the string representation
+     * @see #toArrayString(Object[])
+     */
+    public static String toString(Object[] self) {
+        return toArrayString(self);
+    }
+
+    /**
+     * Returns the string representation of the given array.  The string
+     * displays the contents of the array, similar to an array literal, i.e.
+     * <code>{1, 2, "a"}</code>.
+     *
+     * @param self an Object[]
+     * @return the string representation
+     */
+    public static String toArrayString(Object[] self) {
+        return (self == null) ? "null" : InvokerHelper.toArrayString(self);
+    }
+
+    /**
+     * Create a String representation of this object.
+     * @param value an object
+     * @return a string.
+     */
+    protected static String toString(Object value) {
+        if (value instanceof Map)
+            return toMapString((Map) value);
+        else if (value instanceof Collection)
+            return toListString((Collection) value);
+        else if (value instanceof Object[])
+            return toArrayString((Object[]) value);
+        else
+            return InvokerHelper.toString(value);
+    }
+
+    // Number based methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Increment a Character by one.
+     *
+     * @param self a Character
+     * @return an incremented Number
+     */
+    public static Number next(Character self) {
+        return plus(self, ONE);
+    }
+
+    /**
+     * Increment a Number by one.
+     *
+     * @param self a Number
+     * @return an incremented Number
+     */
+    public static Number next(Number self) {
+        return plus(self, ONE);
+    }
+
+    /**
+     * Decrement a Character by one.
+     *
+     * @param self a Character
+     * @return a decremented Number
+     */
+    public static Number previous(Character self) {
+        return minus(self, ONE);
+    }
+
+    /**
+     * Decrement a Number by one.
+     *
+     * @param self a Number
+     * @return a decremented Number
+     */
+    public static Number previous(Number self) {
+        return minus(self, ONE);
+    }
+
+    /**
+     * Add a Character and a Number.
+     *
+     * @param left  a Character
+     * @param right a Number
+     * @return the addition of the Character and the Number
+     */
+    public static Number plus(Character left, Number right) {
+        return plus(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Add a Number and a Character.
+     *
+     * @param left  a Number
+     * @param right a Character
+     * @return the addition of the Character and the Number
+     */
+    public static Number plus(Number left, Character right) {
+        return plus(left, new Integer(right.charValue()));
+    }
+
+    /**
+     * Add two Characters.
+     *
+     * @param left  a Character
+     * @param right a Character
+     * @return the addition of both Characters
+     */
+    public static Number plus(Character left, Character right) {
+        return plus(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Add two numbers and return the result.
+     *
+     * @param left  a Number
+     * @param right another Number to add
+     * @return the addition of both Numbers
+     */
+    public static Number plus(Number left, Number right) {
+        return NumberMath.add(left, right);
+    }
+
+    /**
+     * Compare a Character and a Number.
+     *
+     * @param left  a Character
+     * @param right a Number
+     * @return the result of the comparison
+     */
+    public static int compareTo(Character left, Number right) {
+        return compareTo(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Compare a Number and a Character.
+     *
+     * @param left  a Number
+     * @param right a Character
+     * @return the result of the comparison
+     */
+    public static int compareTo(Number left, Character right) {
+        return compareTo(left, new Integer(right.charValue()));
+    }
+
+    /**
+     * Compare two Characters.
+     *
+     * @param left  a Character
+     * @param right a Character
+     * @return the result of the comparison
+     */
+    public static int compareTo(Character left, Character right) {
+        return compareTo(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Compare two Numbers.  Equality (==) for numbers dispatches to this.
+     *
+     * @param left  a Number
+     * @param right another Number to compare to
+     * @return the comparision of both numbers
+     */
+    public static int compareTo(Number left, Number right) {
+        /** @todo maybe a double dispatch thing to handle new large numbers? */
+        return NumberMath.compareTo(left, right);
+    }
+
+    /**
+     * Subtract a Number from a Character.
+     *
+     * @param left  a Character
+     * @param right a Number
+     * @return the addition of the Character and the Number
+     */
+    public static Number minus(Character left, Number right) {
+        return minus(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Subtract a Character from a Number.
+     *
+     * @param left  a Number
+     * @param right a Character
+     * @return the addition of the Character and the Number
+     */
+    public static Number minus(Number left, Character right) {
+        return minus(left, new Integer(right.charValue()));
+    }
+
+    /**
+     * Subtraction two Characters.
+     *
+     * @param left  a Character
+     * @param right a Character
+     * @return the addition of both Characters
+     */
+    public static Number minus(Character left, Character right) {
+        return minus(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Substraction of two Numbers.
+     *
+     * @param left  a Number
+     * @param right another Number to substract to the first one
+     * @return the substraction
+     */
+    public static Number minus(Number left, Number right) {
+        return NumberMath.subtract(left, right);
+    }
+
+    /**
+     * Multiply a Character by a Number.
+     *
+     * @param left  a Character
+     * @param right a Number
+     * @return the multiplication of both
+     */
+    public static Number multiply(Character left, Number right) {
+        return multiply(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Multiply a Number by a Character.
+     *
+     * @param left  a Number
+     * @param right a Character
+     * @return the multiplication of both
+     */
+    public static Number multiply(Number left, Character right) {
+        return multiply(left, new Integer(right.charValue()));
+    }
+
+    /**
+     * Multiply two Characters.
+     *
+     * @param left  a Character
+     * @param right another Character
+     * @return the multiplication of both
+     */
+    public static Number multiply(Character left, Character right) {
+        return multiply(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Multiply two Numbers.
+     *
+     * @param left  a Number
+     * @param right another Number
+     * @return the multiplication of both
+     */
+    //Note:  This method is NOT called if left AND right are both BigIntegers or BigDecimals because
+    //those classes implement a method with a better exact match.
+    public static Number multiply(Number left, Number right) {
+        return NumberMath.multiply(left, right);
+    }
+
+    /**
+     * Multiply a BigDecimal and a Double.
+     * Note: This method was added to enforce the Groovy rule of
+     * BigDecimal*Double == Double. Without this method, the
+     * multiply(BigDecimal) method in BigDecimal would respond
+     * and return a BigDecimal instead. Since BigDecimal is prefered
+     * over Number, the Number*Number method is not choosen as in older
+     * versions of Groovy.
+     *
+     * @param left  a BigDecimal
+     * @param right a Double
+     * @return the multiplication of both
+     */
+    public static Number multiply(BigDecimal left, Double right) {
+        return NumberMath.multiply(left, right);
+    }
+
+    /**
+     * Multiply a BigDecimal and a BigInteger.
+     * Note: This method was added to enforce the Groovy rule of
+     * BigDecimal*long == long. Without this method, the
+     * multiply(BigDecimal) method in BigDecimal would respond
+     * and return a BigDecimal instead. Since BigDecimal is prefered
+     * over Number, the Number*Number method is not choosen as in older
+     * versions of Groovy. Biginteger is the fallback for all integer
+     * types in Groovy
+     *
+     * @param left  a BigDecimal
+     * @param right a BigInteger
+     * @return the multiplication of both
+     */
+    public static Number multiply(BigDecimal left, BigInteger right) {
+        return NumberMath.multiply(left, right);
+    }
+
+    /**
+     * Power of a Number to a certain exponent.  Called by the '**' operator.
+     *
+     * @param self     a Number
+     * @param exponent a Number exponent
+     * @return a Number to the power of a certain exponent
+     */
+    public static Number power(Number self, Number exponent) {
+        double base, exp, answer;
+        base = self.doubleValue();
+        exp = exponent.doubleValue();
+
+        answer = Math.pow(base, exp);
+        if ((double) ((int) answer) == answer) {
+            return new Integer((int) answer);
+        } else if ((double) ((long) answer) == answer) {
+            return new Long((long) answer);
+        } else {
+            return new Double(answer);
+        }
+    }
+
+    /**
+     * Divide a Character by a Number.
+     *
+     * @param left  a Character
+     * @param right a Number
+     * @return the multiplication of both
+     */
+    public static Number div(Character left, Number right) {
+        return div(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Divide a Number by a Character.
+     *
+     * @param left  a Number
+     * @param right a Character
+     * @return the multiplication of both
+     */
+    public static Number div(Number left, Character right) {
+        return div(left, new Integer(right.charValue()));
+    }
+
+    /**
+     * Divide two Characters.
+     *
+     * @param left  a Character
+     * @param right another Character
+     * @return the multiplication of both
+     */
+    public static Number div(Character left, Character right) {
+        return div(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Divide two Numbers.
+     *
+     * @param left  a Number
+     * @param right another Number
+     * @return a Number resulting of the divide operation
+     */
+    //Method name changed from 'divide' to avoid collision with BigInteger method that has
+    //different semantics.  We want a BigDecimal result rather than a BigInteger.
+    public static Number div(Number left, Number right) {
+        return NumberMath.divide(left, right);
+    }
+
+    /**
+     * Integer Divide a Character by a Number.
+     *
+     * @param left  a Character
+     * @param right a Number
+     * @return the integer division of both
+     */
+    public static Number intdiv(Character left, Number right) {
+        return intdiv(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Integer Divide a Number by a Character.
+     *
+     * @param left  a Number
+     * @param right a Character
+     * @return the integer division of both
+     */
+    public static Number intdiv(Number left, Character right) {
+        return intdiv(left, new Integer(right.charValue()));
+    }
+
+    /**
+     * Integer Divide two Characters.
+     *
+     * @param left  a Character
+     * @param right another Character
+     * @return the integer division of both
+     */
+    public static Number intdiv(Character left, Character right) {
+        return intdiv(new Integer(left.charValue()), right);
+    }
+
+    /**
+     * Integer Divide two Numbers.
+     *
+     * @param left  a Number
+     * @param right another Number
+     * @return a Number (an Integer) resulting of the integer division operation
+     */
+    public static Number intdiv(Number left, Number right) {
+        return NumberMath.intdiv(left, right);
+    }
+
+    /**
+     * Bitwise OR together two numbers.
+     *
+     * @param left  a Number
+     * @param right another Number to bitwise OR
+     * @return the bitwise OR of both Numbers
+     */
+    public static Number or(Number left, Number right) {
+        return NumberMath.or(left, right);
+    }
+
+    /**
+     * Bitwise AND together two Numbers.
+     *
+     * @param left  a Number
+     * @param right another Number to bitwise AND
+     * @return the bitwise AND of both Numbers
+     */
+    public static Number and(Number left, Number right) {
+        return NumberMath.and(left, right);
+    }
+
+    /**
+     * Bitwise AND together two BitSets.
+     *
+     * @param left  a BitSet
+     * @param right another BitSet to bitwise AND
+     * @return the bitwise AND of both BitSets
+     */
+    public static BitSet and(BitSet left, BitSet right) {
+        BitSet result = (BitSet) left.clone();
+        result.and(right);
+        return result;
+    }
+
+    /**
+     * Bitwise XOR together two BitSets.  Called when the '^' operator is used
+     * between two bit sets.
+     *
+     * @param left  a BitSet
+     * @param right another BitSet to bitwise AND
+     * @return the bitwise XOR of both BitSets
+     */
+    public static BitSet xor(BitSet left, BitSet right) {
+        BitSet result = (BitSet) left.clone();
+        result.xor(right);
+        return result;
+    }
+
+    /**
+     * Bitwise NEGATE a BitSet.
+     *
+     * @param self a BitSet
+     * @return the bitwise NEGATE of the BitSet
+     */
+    public static BitSet bitwiseNegate(BitSet self) {
+        BitSet result = (BitSet) self.clone();
+        result.flip(0, result.size() - 1);
+        return result;
+    }
+
+    /**
+     * Bitwise OR together two BitSets.  Called when the '|' operator is used
+     * between two bit sets.
+     *
+     * @param left  a BitSet
+     * @param right another BitSet to bitwise AND
+     * @return the bitwise OR of both BitSets
+     */
+    public static BitSet or(BitSet left, BitSet right) {
+        BitSet result = (BitSet) left.clone();
+        result.or(right);
+        return result;
+    }
+
+    /**
+     * Bitwise XOR together two Numbers.  Called when the '|' operator is used.
+     *
+     * @param left  a Number
+     * @param right another Number to bitwse XOR
+     * @return the bitwise XOR of both Numbers
+     */
+    public static Number xor(Number left, Number right) {
+        return NumberMath.xor(left, right);
+    }
+
+    /**
+     * Performs a division modulus operation.  Called by the '%' operator.
+     *
+     * @param left  a Number
+     * @param right another Number to mod
+     * @return the modulus result
+     */
+    public static Number mod(Number left, Number right) {
+        return NumberMath.mod(left, right);
+    }
+
+    /**
+     * Negates the number.  Equivalent to the '-' operator when it preceeds
+     * a single operand, i.e. <code>-10</code>
+     *
+     * @param left a Number
+     * @return the negation of the number
+     */
+    public static Number unaryMinus(Number left) {
+        return NumberMath.unaryMinus(left);
+    }
+
+
+    /**
+     * Executes the closure this many times, starting from zero.  The current
+     * index is passed to the closure each time.
+     * Example:
+     * <pre>10.times {
+     *   println it
+     * }</pre>
+     * Prints the numbers 0 through 9.
+     *
+     * @param self    a Number
+     * @param closure the closure to call a number of times
+     */
+    public static void times(Number self, Closure closure) {
+        for (int i = 0, size = self.intValue(); i < size; i++) {
+            closure.call(new Integer(i));
+            if (closure.getDirective() == Closure.DONE) {
+                break;
+            }
+        }
+    }
+
+    /**
+     * Iterates from this number up to the given number, inclusive,
+     * incrementing by one each time.
+     *
+     * @param self    a Number
+     * @param to      another Number to go up to
+     * @param closure the closure to call
+     */
+    public static void upto(Number self, Number to, Closure closure) {
+        int self1 = self.intValue();
+        int to1 = to.intValue();
+        if (self1 <= to1) {
+            for (int i = self1; i <= to1; i++) {
+                closure.call(new Integer(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number up to the given number, inclusive,
+     * incrementing by one each time.
+  	 *
+     * @param self    a long
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void upto(long self, Number to, Closure closure) {
+        long to1 = to.longValue();
+        if (self <= to1) {
+            for (long i = self; i <= to1; i++) {
+                closure.call(new Long(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number up to the given number, inclusive,
+     * incrementing by one each time.
+  	 *
+     * @param self    a Long
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void upto(Long self, Number to, Closure closure) {
+        long self1 = self.longValue();
+        long to1 = to.longValue();
+        if (self1 <= to1) {
+            for (long i = self1; i <= to1; i++) {
+                closure.call(new Long(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number up to the given number, inclusive,
+     * incrementing by one each time.
+  	 *
+     * @param self    a float
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void upto(float self, Number to, Closure closure) {
+        float to1 = to.floatValue();
+        if (self <= to1) {
+            for (float i = self; i <= to1; i++) {
+                closure.call(new Float(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number up to the given number, inclusive,
+     * incrementing by one each time.
+  	 *
+     * @param self    a Float
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void upto(Float self, Number to, Closure closure) {
+        float self1 = self.floatValue();
+        float to1 = to.floatValue();
+        if (self1 <= to1) {
+            for (float i = self1; i <= to1; i++) {
+                closure.call(new Float(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number up to the given number, inclusive,
+     * incrementing by one each time.
+  	 *
+     * @param self    a double
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void upto(double self, Number to, Closure closure) {
+        double to1 = to.doubleValue();
+        if (self <= to1) {
+            for (double i = self; i <= to1; i++) {
+                closure.call(new Double(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number up to the given number, inclusive,
+     * incrementing by one each time.
+  	 *
+     * @param self    a Double
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void upto(Double self, Number to, Closure closure) {
+        double self1 = self.doubleValue();
+        double to1 = to.doubleValue();
+        if (self1 <= to1) {
+            for (double i = self1; i <= to1; i++) {
+                closure.call(new Double(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number up to the given number, inclusive,
+     * incrementing by one each time.  Example:
+     * <pre>0.upto( 10 ) {
+  	 *   println it
+  	 * }</pre>
+  	 * Prints numbers 0 to 10
+  	 *
+     * @param self    a BigInteger
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void upto(BigInteger self, Number to, Closure closure) {
+        if (to instanceof BigDecimal) {
+            final BigDecimal one = new BigDecimal("1.0");
+            BigDecimal self1 = new BigDecimal(self);
+            BigDecimal to1 = (BigDecimal) to;
+            if (self1.compareTo(to1) <= 0) {
+                for (BigDecimal i = self1; i.compareTo(to1) <= 0; i = i.add(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+        } else if (to instanceof BigInteger) {
+            final BigInteger one = new BigInteger("1");
+            BigInteger to1 = (BigInteger) to;
+            if (self.compareTo(to1) <= 0) {
+                for (BigInteger i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+        } else {
+            final BigInteger one = new BigInteger("1");
+            BigInteger to1 = new BigInteger("" + to);
+            if (self.compareTo(to1) <= 0) {
+                for (BigInteger i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+        }
+    }
+
+    /**
+     * Iterates from this number up to the given number, inclusive,
+     * incrementing by one each time.
+     * <pre>0.upto( 10 ) {
+  	 *   println it
+  	 * }</pre>
+  	 * Prints numbers 0.1, 1.1, 2.1... to 9.1
+     *
+     * @param self    a BigDecimal
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void upto(BigDecimal self, Number to, Closure closure) {
+        final BigDecimal one = new BigDecimal("1.0");
+        if (to instanceof BigDecimal) {
+            BigDecimal to1 = (BigDecimal) to;
+            if (self.compareTo(to1) <= 0) {
+                for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+        } else if (to instanceof BigInteger) {
+            BigDecimal to1 = new BigDecimal((BigInteger) to);
+            if (self.compareTo(to1) <= 0) {
+                for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+        } else {
+            BigDecimal to1 = new BigDecimal("" + to);
+            if (self.compareTo(to1) <= 0) {
+                for (BigDecimal i = self; i.compareTo(to1) <= 0; i = i.add(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".upto(" + to + ")");
+        }
+    }
+
+    /**
+     * Iterates from this number down to the given number, inclusive,
+     * decrementing by one each time.
+     *
+     * @param self    a Number
+     * @param to      another Number to go down to
+     * @param closure the closure to call
+     */
+    public static void downto(Number self, Number to, Closure closure) {
+        int self1 = self.intValue();
+        int to1 = to.intValue();
+        if (self1 >= to1) {
+            for (int i = self1; i >= to1; i--) {
+                closure.call(new Integer(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number down to the given number, inclusive,
+     * decrementing by one each time.
+     *
+     * @param self    a long
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void downto(long self, Number to, Closure closure) {
+        long to1 = to.longValue();
+        if (self >= to1) {
+            for (long i = self; i >= to1; i--) {
+                closure.call(new Long(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number down to the given number, inclusive,
+     * decrementing by one each time.
+     *
+     * @param self    a Long
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void downto(Long self, Number to, Closure closure) {
+        long self1 = self.longValue();
+        long to1 = to.longValue();
+        if (self1 >= to1) {
+            for (long i = self1; i >= to1; i--) {
+                closure.call(new Long(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number down to the given number, inclusive,
+     * decrementing by one each time.
+     *
+     * @param self    a float
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void downto(float self, Number to, Closure closure) {
+        float to1 = to.floatValue();
+        if (self >= to1) {
+            for (float i = self; i >= to1; i--) {
+                closure.call(new Float(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number down to the given number, inclusive,
+     * decrementing by one each time.
+     *
+     * @param self    a Float
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void downto(Float self, Number to, Closure closure) {
+        float self1 = self.floatValue();
+        float to1 = to.floatValue();
+        if (self1 >= to1) {
+            for (float i = self1; i >= to1; i--) {
+                closure.call(new Float(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number down to the given number, inclusive,
+     * decrementing by one each time.
+     *
+     * @param self    a double
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void downto(double self, Number to, Closure closure) {
+        double to1 = to.doubleValue();
+        if (self >= to1) {
+            for (double i = self; i >= to1; i--) {
+                closure.call(new Double(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number down to the given number, inclusive,
+     * decrementing by one each time.
+     *
+     * @param self    a Double
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void downto(Double self, Number to, Closure closure) {
+        double self1 = self.doubleValue();
+        double to1 = to.doubleValue();
+        if (self1 >= to1) {
+            for (double i = self1; i >= to1; i--) {
+                closure.call(new Double(i));
+            }
+        } else
+            throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+    }
+
+    /**
+     * Iterates from this number down to the given number, inclusive,
+     * decrementing by one each time.
+     *
+     * @param self    a BigInteger
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void downto(BigInteger self, Number to, Closure closure) {
+        if (to instanceof BigDecimal) {
+            final BigDecimal one = new BigDecimal("1.0");
+            final BigDecimal to1 = (BigDecimal) to;
+            final BigDecimal selfD = new BigDecimal(self);
+            if (selfD.compareTo(to1) >= 0) {
+                for (BigDecimal i = selfD; i.compareTo(to1) >= 0; i = i.subtract(one)) {
+                    closure.call(i.toBigInteger());
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+        } else if (to instanceof BigInteger) {
+            final BigInteger one = new BigInteger("1");
+            final BigInteger to1 = (BigInteger) to;
+            if (self.compareTo(to1) >= 0) {
+                for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+        } else {
+            final BigInteger one = new BigInteger("1");
+            final BigInteger to1 = new BigInteger("" + to);
+            if (self.compareTo(to1) >= 0) {
+                for (BigInteger i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+        }
+    }
+
+    /**
+     * Iterates from this number down to the given number, inclusive,
+     * decrementing by one each time.  Each number is passed to the closure.
+     * Example:
+     * <pre>10.5.downto(0) {
+     *   println it
+	 * }</pre>
+	 * Prints numbers 10.5, 9.5 ... to 0.5.
+	 *
+     * @param self    a BigDecimal
+     * @param to the end number
+     * @param closure the code to execute for each number
+     */
+    public static void downto(BigDecimal self, Number to, Closure closure) {
+        final BigDecimal one = new BigDecimal("1.0");
+        if (to instanceof BigDecimal) {
+            BigDecimal to1 = (BigDecimal) to;
+            if (self.compareTo(to1) >= 0) {
+                for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+        } else if (to instanceof BigInteger) {
+            BigDecimal to1 = new BigDecimal((BigInteger) to);
+            if (self.compareTo(to1) >= 0) {
+                for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+        } else {
+            BigDecimal to1 = new BigDecimal("" + to);
+            if (self.compareTo(to1) >= 0) {
+                for (BigDecimal i = self; i.compareTo(to1) >= 0; i = i.subtract(one)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self + ".downto(" + to + ")");
+        }
+    }
+
+    /**
+     * Iterates from this number up to the given number using a step increment.
+     * Each intermediate number is passed to the given closure.  Example:
+     * <pre>0.step( 10, 2 ) {
+     *   println it
+     * }</pre>
+     * Prints even numbers 0 through 8.
+     *
+     * @param self       a Number to start with
+     * @param to         a Number to go up to, exclusive
+     * @param stepNumber a Number representing the step increment
+     * @param closure    the closure to call
+     */
+    public static void step(Number self, Number to, Number stepNumber, Closure closure) {
+        if (self instanceof BigDecimal || to instanceof BigDecimal || stepNumber instanceof BigDecimal) {
+            final BigDecimal zero = new BigDecimal("0.0");
+            BigDecimal self1 = (self instanceof BigDecimal) ? (BigDecimal) self : new BigDecimal("" + self);
+            BigDecimal to1 = (to instanceof BigDecimal) ? (BigDecimal) to : new BigDecimal("" + to);
+            BigDecimal stepNumber1 = (stepNumber instanceof BigDecimal) ? (BigDecimal) stepNumber : new BigDecimal("" + stepNumber);
+            if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) {
+                for (BigDecimal i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) {
+                    closure.call(i);
+                }
+            } else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) {
+                for (BigDecimal i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")");
+        } else if (self instanceof BigInteger || to instanceof BigInteger || stepNumber instanceof BigInteger) {
+            final BigInteger zero = new BigInteger("0");
+            BigInteger self1 = (self instanceof BigInteger) ? (BigInteger) self : new BigInteger("" + self);
+            BigInteger to1 = (to instanceof BigInteger) ? (BigInteger) to : new BigInteger("" + to);
+            BigInteger stepNumber1 = (stepNumber instanceof BigInteger) ? (BigInteger) stepNumber : new BigInteger("" + stepNumber);
+            if (stepNumber1.compareTo(zero) > 0 && to1.compareTo(self1) > 0) {
+                for (BigInteger i = self1; i.compareTo(to1) < 0; i = i.add(stepNumber1)) {
+                    closure.call(i);
+                }
+            } else if (stepNumber1.compareTo(zero) < 0 && to1.compareTo(self1) < 0) {
+                for (BigInteger i = self1; i.compareTo(to1) > 0; i = i.add(stepNumber1)) {
+                    closure.call(i);
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")");
+        } else {
+            int self1 = self.intValue();
+            int to1 = to.intValue();
+            int stepNumber1 = stepNumber.intValue();
+            if (stepNumber1 > 0 && to1 > self1) {
+                for (int i = self1; i < to1; i += stepNumber1) {
+                    closure.call(new Integer(i));
+                }
+            } else if (stepNumber1 < 0 && to1 < self1) {
+                for (int i = self1; i > to1; i += stepNumber1) {
+                    closure.call(new Integer(i));
+                }
+            } else
+                throw new GroovyRuntimeException("Infinite loop in " + self1 + ".step(" + to1 + ", " + stepNumber1 + ")");
+        }
+    }
+
+    /**
+     * Get the absolute value
+     *
+     * @param number a Number
+     * @return the absolute value of that Number
+     */
+    //Note:  This method is NOT called if number is a BigInteger or BigDecimal because
+    //those classes implement a method with a better exact match.
+    public static int abs(Number number) {
+        return Math.abs(number.intValue());
+    }
+
+    /**
+     * Get the absolute value
+     *
+     * @param number a Long
+     * @return the absolute value of that Long
+     */
+    public static long abs(Long number) {
+        return Math.abs(number.longValue());
+    }
+
+    /**
+     * Get the absolute value
+     *
+     * @param number a Float
+     * @return the absolute value of that Float
+     */
+    public static float abs(Float number) {
+        return Math.abs(number.floatValue());
+    }
+
+    /**
+     * Get the absolute value
+     *
+     * @param number a Double
+     * @return the absolute value of that Double
+     */
+    public static double abs(Double number) {
+        return Math.abs(number.doubleValue());
+    }
+
+    /**
+     * Get the absolute value
+     *
+     * @param number a Float
+     * @return the absolute value of that Float
+     */
+    public static int round(Float number) {
+        return Math.round(number.floatValue());
+    }
+
+    /**
+     * Round the value
+     *
+     * @param number a Double
+     * @return the absolute value of that Double
+     */
+    public static long round(Double number) {
+        return Math.round(number.doubleValue());
+    }
+
+    /**
+     * Parse a String into an Integer
+     *
+     * @param self a String
+     * @return an Integer
+     */
+    public static Integer toInteger(String self) {
+        return Integer.valueOf(self.trim());
+    }
+
+    /**
+     * Parse a String into a Long
+     *
+     * @param self a String
+     * @return a Long
+     */
+    public static Long toLong(String self) {
+        return Long.valueOf(self.trim());
+    }
+
+    /**
+     * Parse a String into a Float
+     *
+     * @param self a String
+     * @return a Float
+     */
+    public static Float toFloat(String self) {
+        return Float.valueOf(self.trim());
+    }
+
+    /**
+     * Parse a String into a Double
+     *
+     * @param self a String
+     * @return a Double
+     */
+    public static Double toDouble(String self) {
+        return Double.valueOf(self.trim());
+    }
+
+    /**
+     * Parse a String into a BigInteger
+     *
+     * @param self a String
+     * @return a BigInteger
+     */
+    public static BigInteger toBigInteger(String self) {
+        return new BigInteger(self.trim());
+    }
+
+    /**
+     * Parse a String into a BigDecimal
+     *
+     * @param self a String
+     * @return a BigDecimal
+     */
+    public static BigDecimal toBigDecimal(String self) {
+        return new BigDecimal(self.trim());
+    }
+
+    /**
+     * Determine if a String can be parsed into an Integer.
+     *
+     * @param self a String
+     * @return true if the string can be parsed
+     */
+    public static boolean isInteger(String self) {
+        try {
+            Integer.valueOf(self.trim());
+            return true;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+
+    /**
+     * Determine if a String can be parsed into a Long.
+     *
+     * @param self a String
+     * @return true if the string can be parsed
+     */
+    public static boolean isLong(String self) {
+        try {
+            Long.valueOf(self.trim());
+            return true;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+
+    /**
+     * Determine if a String can be parsed into a Float.
+     *
+     * @param self a String
+     * @return true if the string can be parsed
+     */
+    public static boolean isFloat(String self) {
+        try {
+            Float.valueOf(self.trim());
+            return true;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+
+    /**
+     * Determine if a String can be parsed into a Double.
+     *
+     * @param self a String
+     * @return true if the string can be parsed
+     */
+    public static boolean isDouble(String self) {
+        try {
+            Double.valueOf(self.trim());
+            return true;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+
+    /**
+     * Determine if a String can be parsed into a BigInteger.
+     *
+     * @param self a String
+     * @return true if the string can be parsed
+     */
+    public static boolean isBigInteger(String self) {
+        try {
+            new BigInteger(self.trim());
+            return true;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+
+    /**
+     * Determine if a String can be parsed into a BigDecimal.
+     *
+     * @param self a String
+     * @return true if the string can be parsed
+     */
+    public static boolean isBigDecimal(String self) {
+        try {
+            new BigDecimal(self.trim());
+            return true;
+        } catch (NumberFormatException nfe) {
+            return false;
+        }
+    }
+
+    /**
+     * Determine if a String can be parsed into a Number.
+     * Synonym for 'isBigDecimal()'.
+     *
+     * @param self a String
+     * @return true if the string can be parsed
+     * @see #isBigDecimal(String)
+     */
+    public static boolean isNumber(String self) {
+        return isBigDecimal(self);
+    }
+
+    /**
+     * Transform a Number into an Integer
+     *
+     * @param self a Number
+     * @return an Integer
+     */
+    public static Integer toInteger(Number self) {
+        return new Integer(self.intValue());
+    }
+
+    /**
+     * Transform a Number into a Long
+     *
+     * @param self a Number
+     * @return an Long
+     */
+    public static Long toLong(Number self) {
+        return new Long(self.longValue());
+    }
+
+    /**
+     * Transform a Number into a Float
+     *
+     * @param self a Number
+     * @return an Float
+     */
+    public static Float toFloat(Number self) {
+        return new Float(self.floatValue());
+    }
+
+    /**
+     * Transform a Number into a Double
+     *
+     * @param self a Number
+     * @return an Double
+     */
+    public static Double toDouble(Number self) {
+        return new Double(self.doubleValue());
+    }
+
+    /**
+     * Transform a Number into a BigDecimal
+     *
+     * @param self a Number
+     * @return an BigDecimal
+     */
+    public static BigDecimal toBigDecimal(Number self) {
+        return new BigDecimal(self.doubleValue());
+    }
+
+    /**
+     * Transform this number to a the given type, using the 'as' operator.  The
+     * following types are supported in addition to the default
+     * {@link #asType(Object,Class)}:
+     * <ul>
+     *  <li>BigDecimal</li>
+     *  <li>BigInteger</li>
+     *  <li>Double</li>
+     *  <li>Float</li>
+     * </ul>
+     * @param self this number
+     * @param c the desired type of the transformed result
+     * @return an instance of the given type
+     */
+    public static Object asType(Number self, Class c) {
+        if (c == BigDecimal.class) {
+            return toBigDecimal(self);
+        } else if (c == BigInteger.class) {
+            return toBigInteger(self);
+        } else if (c == Double.class) {
+            return toDouble(self);
+        } else if (c == Float.class) {
+            return toFloat(self);
+        }
+        return asType((Object) self, c);
+    }
+
+    /**
+     * Transform this Number into a BigInteger.
+     *
+     * @param self a Number
+     * @return an BigInteger
+     */
+    public static BigInteger toBigInteger(Number self) {
+        return new BigInteger(Long.toString(self.longValue()));
+    }
+
+    // Date methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Increment a Date by one day.
+     *
+     * @param self a Date
+     * @return the next days date
+     */
+    public static Date next(Date self) {
+        return plus(self, 1);
+    }
+
+    /**
+     * Increment a java.sql.Date by one day.
+     *
+     * @param self a java.sql.Date
+     * @return the next days date
+     */
+    public static java.sql.Date next(java.sql.Date self) {
+        return new java.sql.Date(next((Date) self).getTime());
+    }
+
+    /**
+     * Decrement a Date by one day.
+     *
+     * @param self a Date
+     * @return the previous days date
+     */
+    public static Date previous(Date self) {
+        return minus(self, 1);
+    }
+
+    /**
+     * Decrement a java.sql.Date by one day.
+     *
+     * @param self a java.sql.Date
+     * @return the previous days date
+     */
+    public static java.sql.Date previous(java.sql.Date self) {
+        return new java.sql.Date(previous((Date) self).getTime());
+    }
+
+    /**
+     * Add a number of days to this date and returns the new date.
+     *
+     * @param self a Date
+     * @param days the number of days to increase
+     * @return the new date
+     */
+    public static Date plus(Date self, int days) {
+        Calendar calendar = (Calendar) Calendar.getInstance().clone();
+        calendar.setTime(self);
+        calendar.add(Calendar.DAY_OF_YEAR, days);
+        return calendar.getTime();
+    }
+
+    /**
+     * Add a number of days to this date and returns the new date.
+     *
+     * @param self a java.sql.Date
+     * @param days the number of days to increase
+     * @return the new date
+     */
+    public static java.sql.Date plus(java.sql.Date self, int days) {
+        return new java.sql.Date(plus((Date) self, days).getTime());
+    }
+
+    /**
+     * Subtract a number of days from this date and returns the new date.
+     *
+     * @param self a Date
+     * @param days the number of days to subtract
+     * @return the new date
+     */
+    public static Date minus(Date self, int days) {
+        return plus(self, -days);
+    }
+
+    /**
+     * Subtract a number of days from this date and returns the new date.
+     *
+     * @param self a java.sql.Date
+     * @param days the number of days to subtract
+     * @return the new date
+     */
+    public static java.sql.Date minus(java.sql.Date self, int days) {
+        return new java.sql.Date(minus((Date) self, days).getTime());
+    }
+
+    // Boolean based methods
+    //-------------------------------------------------------------------------
+
+    public static Boolean and(Boolean left, Boolean right) {
+        return Boolean.valueOf(left.booleanValue() && right.booleanValue());
+    }
+
+    public static Boolean or(Boolean left, Boolean right) {
+        return Boolean.valueOf(left.booleanValue() || right.booleanValue());
+    }
+
+    public static Boolean xor(Boolean left, Boolean right) {
+        return Boolean.valueOf(left.booleanValue() ^ right.booleanValue());
+    }
+
+//    public static Boolean negate(Boolean left) {
+//        return Boolean.valueOf(!left.booleanValue());
+//    }
+
+    // File and stream based methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Create an object input stream for this file.
+     *
+     * @param file a file
+     * @return an object input stream
+     * @throws IOException if an IOException occurs.
+     */
+    public static ObjectInputStream newObjectInputStream(File file) throws IOException {
+        return new ObjectInputStream(new FileInputStream(file));
+    }
+
+    /**
+     * Create an object output stream for this file.
+     *
+     * @param file a file
+     * @return an object output stream
+     * @throws IOException if an IOException occurs.
+     */
+    public static ObjectOutputStream newObjectOutputStream(File file) throws IOException {
+        return new ObjectOutputStream(new FileOutputStream(file));
+    }
+
+    /**
+     * Iterates through the given file object by object.
+     *
+     * @param self    a File
+     * @param closure a closure
+     * @throws IOException            if an IOException occurs.
+     * @throws ClassNotFoundException if the class  is not found.
+     * @see #eachObject(ObjectInputStream,Closure)
+     */
+    public static void eachObject(File self, Closure closure) throws IOException, ClassNotFoundException {
+        eachObject(newObjectInputStream(self), closure);
+    }
+
+    /**
+     * Iterates through the given object stream object by object. The
+     * ObjectInputStream is closed afterwards.
+     *
+     * @param ois     an ObjectInputStream, closed after the operation
+     * @param closure a closure
+     * @throws IOException            if an IOException occurs.
+     * @throws ClassNotFoundException if the class  is not found.
+     */
+    public static void eachObject(ObjectInputStream ois, Closure closure) throws IOException, ClassNotFoundException {
+        try {
+            while (true) {
+                try {
+                    Object obj = ois.readObject();
+                    // we allow null objects in the object stream
+                    closure.call(obj);
+                } catch (EOFException e) {
+                    break;
+                }
+            }
+            InputStream temp = ois;
+            ois = null;
+            temp.close();
+        } finally {
+            if (ois != null) {
+                try {
+                    ois.close();
+                }
+                catch (Exception e) {
+                    // ignore this exception since there
+                    // has to be another already
+                    LOG.warning("Caught exception closing ObjectInputStream: " + e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a new ObjectInputStream for this file and pass it to the closure.
+     * This method ensures the stream is closed after the closure returns.
+     *
+     * @param file    a File
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see #withStream(InputStream,Closure)
+     */
+    public static void withObjectInputStream(File file, Closure closure) throws IOException {
+        withStream(newObjectInputStream(file), closure);
+    }
+
+    /**
+     * Create a new ObjectOutputStream for this file and then pass it to the
+     * closure.  This method ensures the stream is closed after the closure
+     * returns.
+     *
+     * @param file    a File
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see #withStream(OutputStream,Closure)
+     */
+    public static void withObjectOutputStream(File file, Closure closure) throws IOException {
+        withStream(newObjectOutputStream(file), closure);
+    }
+
+    /**
+     * Iterates through this file line by line.  Each line is passed
+     * to the given closure.  The file reader is closed before this method
+     * returns.
+     *
+     * @param self    a File
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see #eachLine(Reader,Closure)
+     */
+    public static void eachLine(File self, Closure closure) throws IOException {
+        eachLine(newReader(self), closure);
+    }
+
+    /**
+     * Iterates through the given reader line by line.  Each line is passed
+     * to the given closure.  The Reader is closed before this method returns.
+     *
+     * @param self    a Reader, closed after the method returns
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     */
+    public static void eachLine(Reader self, Closure closure) throws IOException {
+        BufferedReader br /* = null */;
+
+        if (self instanceof BufferedReader)
+            br = (BufferedReader) self;
+        else
+            br = new BufferedReader(self);
+
+        try {
+            while (true) {
+                String line = br.readLine();
+                if (line == null) {
+                    break;
+                } else {
+                    closure.call(line);
+                }
+            }
+            Reader temp = self;
+            self = null;
+            temp.close();
+        } finally {
+            closeReaderWithWarning(self);
+            closeReaderWithWarning(br);
+        }
+    }
+
+    /**
+     * Iterates through this file line by line, splitting on the seperator.
+     * The list of tokens for each line is then passed to the given closure.
+     *
+     * @param self    a File
+     * @param sep     a String separator
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see #splitEachLine(Reader,String,Closure)
+     */
+    public static void splitEachLine(File self, String sep, Closure closure) throws IOException {
+        splitEachLine(newReader(self), sep, closure);
+    }
+
+    /**
+     * Iterates through the given reader line by line, splitting each line using
+     * the given separator.  The list of tokens for each line is then passed to
+     * the given closure.  The Reader is closed afterwards.
+     *
+     * @param self    a Reader, closed after the method returns
+     * @param sep     a String separator
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see String#split(String)
+     */
+    public static void splitEachLine(Reader self, String sep, Closure closure) throws IOException {
+        BufferedReader br /* = null */;
+
+        if (self instanceof BufferedReader)
+            br = (BufferedReader) self;
+        else
+            br = new BufferedReader(self);
+
+        try {
+            while (true) {
+                String line = br.readLine();
+                if (line == null) {
+                    break;
+                } else {
+                    List vals = Arrays.asList(line.split(sep));
+                    closure.call(vals);
+                }
+            }
+            Reader temp = self;
+            self = null;
+            temp.close();
+        } finally {
+            closeReaderWithWarning(self);
+            closeReaderWithWarning(br);
+        }
+    }
+
+    /**
+     * Read a single, whole line from the given Reader.
+     *
+     * @param self a Reader
+     * @return a line
+     * @throws IOException if an IOException occurs.
+     */
+    public static String readLine(Reader self) throws IOException {
+        if (self instanceof BufferedReader) {
+            BufferedReader br = (BufferedReader) self;
+            return br.readLine();
+        } else if (self.markSupported()) {
+            return readLineFromReaderWithMark(self);
+        }
+        return readLineFromReaderWithoutMark(self);
+    }
+
+
+    private static int charBufferSize = 4096;     // half the default stream buffer size
+    private static int expectedLineLength = 160;  // double the default line length
+    private static int EOF = -1;                  // End Of File
+
+
+    /*
+    * This method tries to read subsequent buffers from the reader using a mark
+    */
+    private static String readLineFromReaderWithMark(final Reader input)
+            throws IOException {
+        char[] cbuf = new char[charBufferSize];
+        try {
+            input.mark(charBufferSize);
+        } catch (IOException e) {
+            // this should never happen
+            LOG.warning("Caught exception setting mark on supporting reader: " + e);
+            // fallback
+            return readLineFromReaderWithoutMark(input);
+        }
+
+        // could be changed into do..while, but then
+        // we might create an additional StringBuffer
+        // instance at the end of the stream
+        int count = input.read(cbuf);
+        if (count == EOF) // we are at the end of the input data
+            return null;
+
+        StringBuffer line = new StringBuffer(expectedLineLength);
+        // now work on the buffer(s)
+        int ls = lineSeparatorIndex(cbuf, count);
+        while (ls == -1) {
+            line.append(cbuf, 0, count);
+            count = input.read(cbuf);
+            if (count == EOF) {
+                // we are at the end of the input data
+                return line.toString();
+            }
+            ls = lineSeparatorIndex(cbuf, count);
+        }
+        line.append(cbuf, 0, ls);
+
+        // correct ls if we have \r\n
+        int skipLS = 1;
+        if (ls + 1 < count) {
+            // we are not at the end of the buffer
+            if (cbuf[ls] == '\r' && cbuf[ls + 1] == '\n') {
+                skipLS++;
+            }
+        } else {
+            if (cbuf[ls] == '\r' && input.read() == '\n') {
+                skipLS++;
+            }
+        }
+
+        //reset() and skip over last linesep
+        input.reset();
+        input.skip(line.length() + skipLS);
+        return line.toString();
+    }
+
+    /*
+    * This method reads without a buffer.
+    * It returns too many empty lines if \r\n combinations
+    * are used. Nothing can be done because we can't push
+    * back the character we have just read.
+    */
+    private static String readLineFromReaderWithoutMark(Reader input)
+            throws IOException {
+
+        int c = input.read();
+        if (c == -1)
+            return null;
+        StringBuffer line = new StringBuffer(expectedLineLength);
+
+        while (c != EOF && c != '\n' && c != '\r') {
+            char ch = (char) c;
+            line.append(ch);
+            c = input.read();
+        }
+        return line.toString();
+    }
+
+    /*
+     * searches for \n or \r
+     * Returns -1 if not found.
+     */
+    private static int lineSeparatorIndex(char[] array, int length) {
+        for (int k = 0; k < length; k++) {
+            if (isLineSeparator(array[k])) {
+                return k;
+            }
+        }
+        return -1;
+    }
+
+    /*
+    * true if either \n or \r
+    */
+    private static boolean isLineSeparator(char c) {
+        return c == '\n' || c == '\r';
+    }
+
+    /**
+     * Read a single, whole line from the given InputStream
+     *
+     * @param stream an InputStream
+     * @return a line
+     * @throws IOException if an IOException occurs.
+     * @deprecated use Reader#readLine instead please
+     */
+    public static String readLine(InputStream stream) throws IOException {
+        throw new DeprecationException(
+                "readLine() on InputStream is no longer supported. " +
+                        "Either use a Reader or encapsulate the InputStream" +
+                        "with a BufferedReader and an InputStreamReader."
+        );
+    }
+
+    /**
+     * Reads the file into a list of Strings, with one item for each line.
+     *
+     * @param file a File
+     * @return a List of lines
+     * @throws IOException if an IOException occurs.
+     */
+    public static List readLines(File file) throws IOException {
+        IteratorClosureAdapter closure = new IteratorClosureAdapter(file);
+        eachLine(file, closure);
+        return closure.asList();
+    }
+
+    /**
+     * Read the content of the File using the specified encoding and return it
+     * as a String.
+     *
+     * @param file    the file whose content we want to read
+     * @param charset the charset used to read the content of the file
+     * @return a String containing the content of the file
+     * @throws IOException if an IOException occurs.
+     */
+    public static String getText(File file, String charset) throws IOException {
+        BufferedReader reader = newReader(file, charset);
+        return getText(reader);
+    }
+
+    /**
+     * Read the content of the File and returns it as a String.
+     *
+     * @param file the file whose content we want to read
+     * @return a String containing the content of the file
+     * @throws IOException if an IOException occurs.
+     */
+    public static String getText(File file) throws IOException {
+        BufferedReader reader = newReader(file);
+        return getText(reader);
+    }
+
+    /**
+     * Read the content of this URL and returns it as a String.
+     *
+     * @param url URL to read content from
+     * @return the text from that URL
+     * @throws IOException if an IOException occurs.
+     */
+    public static String getText(URL url) throws IOException {
+        return getText(url, CharsetToolkit.getDefaultSystemCharset().toString());
+    }
+
+    /**
+     * Read the data from this URL and return it as a String.  The connection
+     * stream is closed before this method returns.
+     *
+     * @param url     URL to read content from
+     * @param charset opens the stream with a specified charset
+     * @return the text from that URL
+     * @throws IOException if an IOException occurs.
+     * @see URLConnection#getInputStream()
+     */
+    public static String getText(URL url, String charset) throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(url.openConnection().getInputStream(), charset));
+        return getText(reader);
+    }
+
+    /**
+     * Read the content of this InputStream and return it as a String.
+     * The stream is closed before this method returns.
+     *
+     * @param is an input stream
+     * @return the text from that URL
+     * @throws IOException if an IOException occurs.
+     */
+    public static String getText(InputStream is) throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+        return getText(reader);
+    }
+
+    /**
+     * Read the content of this InputStream using specified charset and return
+     * it as a String.  The stream is closed before this method returns.
+     *
+     * @param is      an input stream
+     * @param charset opens the stream with a specified charset
+     * @return the text from that URL
+     * @throws IOException if an IOException occurs.
+     */
+    public static String getText(InputStream is, String charset) throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is, charset));
+        return getText(reader);
+    }
+
+    /**
+     * Read the content of the Reader and return it as a String.  The reader
+     * is closed before this method returns.
+     *
+     * @param reader a Reader whose content we want to read
+     * @return a String containing the content of the buffered reader
+     * @throws IOException if an IOException occurs.
+     * @see #getText(BufferedReader)
+     */
+    public static String getText(Reader reader) throws IOException {
+        BufferedReader bufferedReader = new BufferedReader(reader);
+        return getText(bufferedReader);
+    }
+
+    /**
+     * Read the content of the BufferedReader and return it as a String.
+     * The BufferedReader is closed afterwards.
+     *
+     * @param reader a BufferedReader whose content we want to read
+     * @return a String containing the content of the buffered reader
+     * @throws IOException if an IOException occurs.
+     */
+    public static String getText(BufferedReader reader) throws IOException {
+        StringBuffer answer = new StringBuffer();
+        // reading the content of the file within a char buffer
+        // allow to keep the correct line endings
+        char[] charBuffer = new char[4096];
+        int nbCharRead /* = 0*/;
+        try {
+            while ((nbCharRead = reader.read(charBuffer)) != -1) {
+                // appends buffer
+                answer.append(charBuffer, 0, nbCharRead);
+            }
+            Reader temp = reader;
+            reader = null;
+            temp.close();
+        } finally {
+            closeReaderWithWarning(reader);
+        }
+        return answer.toString();
+    }
+
+    /**
+     * Write the text and append a newline (using the platform's line-ending).
+     *
+     * @param writer a BufferedWriter
+     * @param line   the line to write
+     * @throws IOException if an IOException occurs.
+     */
+    public static void writeLine(BufferedWriter writer, String line) throws IOException {
+        writer.write(line);
+        writer.newLine();
+    }
+
+    /**
+     * Write the text to the File.
+     *
+     * @param file a File
+     * @param text the text to write to the File
+     * @throws IOException if an IOException occurs.
+     */
+    public static void write(File file, String text) throws IOException {
+        BufferedWriter writer = null;
+        try {
+            writer = newWriter(file);
+            writer.write(text);
+            writer.flush();
+
+            Writer temp = writer;
+            writer = null;
+            temp.close();
+        } finally {
+            closeWriterWithWarning(writer);
+        }
+    }
+
+    /**
+     * Write the text to the File.
+     *
+     * @param file a File
+     * @param text the text to write to the File
+     * @return the original file
+     * @throws IOException if an IOException occurs.
+     */
+    public static File leftShift(File file, Object text) throws IOException {
+        append(file, text);
+        return file;
+    }
+
+    /**
+     * Write the text to the File, using the specified encoding.
+     *
+     * @param file    a File
+     * @param text    the text to write to the File
+     * @param charset the charset used
+     * @throws IOException if an IOException occurs.
+     */
+    public static void write(File file, String text, String charset) throws IOException {
+        BufferedWriter writer = null;
+        try {
+            writer = newWriter(file, charset);
+            writer.write(text);
+            writer.flush();
+
+            Writer temp = writer;
+            writer = null;
+            temp.close();
+        } finally {
+            closeWriterWithWarning(writer);
+        }
+    }
+
+    /**
+     * Append the text at the end of the File.
+     *
+     * @param file a File
+     * @param text the text to append at the end of the File
+     * @throws IOException if an IOException occurs.
+     */
+    public static void append(File file, Object text) throws IOException {
+        BufferedWriter writer = null;
+        try {
+            writer = newWriter(file, true);
+            InvokerHelper.write(writer, text);
+            writer.flush();
+
+            Writer temp = writer;
+            writer = null;
+            temp.close();
+        } finally {
+            closeWriterWithWarning(writer);
+        }
+    }
+
+    /**
+     * Append the text at the end of the File, using a specified encoding.
+     *
+     * @param file    a File
+     * @param text    the text to append at the end of the File
+     * @param charset the charset used
+     * @throws IOException if an IOException occurs.
+     */
+    public static void append(File file, Object text, String charset) throws IOException {
+        BufferedWriter writer = null;
+        try {
+            writer = newWriter(file, charset, true);
+            InvokerHelper.write(writer, text);
+            writer.flush();
+
+            Writer temp = writer;
+            writer = null;
+            temp.close();
+        } finally {
+            closeWriterWithWarning(writer);
+        }
+    }
+
+    /**
+     * Reads the reader into a list of Strings, with one entry for each line.
+     * The reader is closed before this method returns.
+     *
+     * @param reader a Reader
+     * @return a List of lines
+     * @throws IOException if an IOException occurs.
+     */
+    public static List readLines(Reader reader) throws IOException {
+        IteratorClosureAdapter closure = new IteratorClosureAdapter(reader);
+        eachLine(reader, closure);
+        return closure.asList();
+    }
+
+    /**
+     * This method is used to throw useful exceptions when the eachFile* and eachDir closure methods
+     * are used incorrectly.
+     *
+     * @param dir The directory to check
+     * @throws FileNotFoundException    if the given directory does not exist
+     * @throws IllegalArgumentException if the provided File object does not represent a directory
+     */
+    private static void checkDir(File dir) throws FileNotFoundException, IllegalArgumentException {
+        if (!dir.exists())
+            throw new FileNotFoundException(dir.getAbsolutePath());
+        if (!dir.isDirectory())
+            throw new IllegalArgumentException("The provided File object is not a directory: " + dir.getAbsolutePath());
+    }
+
+    /**
+     * Common code for {@link #eachFile(File,Closure)} and {@link #eachDir(File,Closure)}
+     *
+     * @param self    a file object
+     * @param closure the closure to invoke
+     * @param onlyDir if normal file should be skipped
+     * @throws FileNotFoundException    if the given directory does not exist
+     * @throws IllegalArgumentException if the provided File object does not represent a directory
+     */
+    private static void eachFile(final File self, final Closure closure, final boolean onlyDir)
+            throws FileNotFoundException, IllegalArgumentException {
+        checkDir(self);
+        final File[] files = self.listFiles();
+        // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836
+        if (files == null) return;
+        for (int i = 0; i < files.length; i++) {
+            if (!onlyDir || files[i].isDirectory()) {
+                closure.call(files[i]);
+            }
+        }
+    }
+
+    /**
+     * Invokes the closure for each file in the given directory
+     *
+     * @param self    a File
+     * @param closure a closure
+     * @throws FileNotFoundException    if the given directory does not exist
+     * @throws IllegalArgumentException if the provided File object does not represent a directory
+     * @see File#listFiles()
+     */
+    public static void eachFile(final File self, final Closure closure) throws FileNotFoundException, IllegalArgumentException {
+        eachFile(self, closure, false);
+    }
+
+    /**
+     * Invokes the closure for each directory in this directory,
+     * ignoring regular files.
+     *
+     * @param self    a directory
+     * @param closure a closure
+     * @throws FileNotFoundException    if the given directory does not exist
+     * @throws IllegalArgumentException if the provided File object does not represent a directory
+     */
+    public static void eachDir(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException {
+        eachFile(self, closure, true);
+    }
+
+    /**
+     * Common code for {@link #eachFileRecurse(File,Closure)} and {@link #eachDirRecurse(File,Closure)}
+     *
+     * @param self    a file object
+     * @param closure the closure to invoke on each file
+     * @param onlyDir if normal file should be skipped
+     * @throws FileNotFoundException    if the given directory does not exist
+     * @throws IllegalArgumentException if the provided File object does not represent a directory
+     */
+    private static void eachFileRecurse(final File self, final Closure closure, final boolean onlyDir)
+            throws FileNotFoundException, IllegalArgumentException {
+        checkDir(self);
+        final File[] files = self.listFiles();
+        // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836
+        if (files == null) return;
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].isDirectory()) {
+                closure.call(files[i]);
+                eachFileRecurse(files[i], closure, onlyDir);
+            } else if (!onlyDir) {
+                closure.call(files[i]);
+            }
+        }
+    }
+
+    /**
+     * Invokes the closure for each descendant file in this directory.
+     * Sub-directories are recursively searched in a depth-first fashion.
+     *
+     * @param self    a File
+     * @param closure a closure
+     * @throws FileNotFoundException    if the given directory does not exist
+     * @throws IllegalArgumentException if the provided File object does not represent a directory
+     */
+    public static void eachFileRecurse(File self, Closure closure) throws FileNotFoundException, IllegalArgumentException {
+        eachFileRecurse(self, closure, false);
+    }
+
+    /**
+     * Invokes the closure for each descendant directory of this directory.
+     * Sub-directories are recursively searched in a depth-first fashion.
+     * Only directories are passed to the closure; regular files are ignored.
+     *
+     * @param self    a directory
+     * @param closure a closure
+     * @throws FileNotFoundException    if the given directory does not exist
+     * @throws IllegalArgumentException if the provided File object does not represent a directory
+     * @since 1.1 beta 1
+     * @see #eachFileRecurse(File,Closure,boolean)
+     */
+    public static void eachDirRecurse(final File self, final Closure closure) throws FileNotFoundException, IllegalArgumentException {
+        eachFileRecurse(self, closure, true);
+    }
+
+    /**
+     * Common code for {@link #eachFileMatch(File,Object,Closure)} and {@link #eachDirMatch(File,Object,Closure)}
+     *
+     * @param self    a file
+     * @param filter  the filter to perform on the directory (using the isCase(object) method)
+     * @param closure the closure to invoke
+     * @param onlyDir if normal file should be skipped
+     * @throws FileNotFoundException    if the given directory does not exist
+     * @throws IllegalArgumentException if the provided File object does not represent a directory
+     */
+    private static void eachFileMatch(final File self, final Object filter, final Closure closure, final boolean onlyDir)
+            throws FileNotFoundException, IllegalArgumentException {
+        checkDir(self);
+        final File[] files = self.listFiles();
+        // null check because of http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4803836
+        if (files == null) return;
+        final MetaClass metaClass = InvokerHelper.getMetaClass(filter);
+        for (int i = 0; i < files.length; i++) {
+            final File currentFile = files[i];
+            if ((!onlyDir || currentFile.isDirectory())
+                    && DefaultTypeTransformation.castToBoolean(metaClass.invokeMethod(filter, "isCase", currentFile.getName()))) {
+                closure.call(currentFile);
+            }
+        }
+    }
+
+    /**
+     * Invokes the closure for each file matching the given filter in the given directory
+     * - calling the isCase() method used by switch statements.  This method can be used
+     * with different kinds of filters like regular expresions, classes, ranges etc.
+     *
+     * @param self    a file
+     * @param filter  the filter to perform on the directory (using the isCase(object) method)
+     * @param closure the closure to invoke
+     * @throws FileNotFoundException    if the given directory does not exist
+     * @throws IllegalArgumentException if the provided File object does not represent a directory
+     */
+    public static void eachFileMatch(final File self, final Object filter, final Closure closure)
+            throws FileNotFoundException, IllegalArgumentException {
+        eachFileMatch(self, filter, closure, false);
+    }
+
+    /**
+     * Invokes the closure for each directory matching the given filter in the given directory
+     * - calling the isCase() method used by switch statements.  This method can be used
+     * with different kinds of filters like regular expresions, classes, ranges etc.
+     *
+     * @param self    a file
+     * @param filter  the filter to perform on the directory (using the isCase(object) method)
+     * @param closure the closure to invoke
+     * @throws FileNotFoundException    if the given directory does not exist
+     * @throws IllegalArgumentException if the provided File object does not represent a directory
+     * @since 1.1 beta 1
+     */
+    public static void eachDirMatch(final File self, final Object filter, final Closure closure) throws FileNotFoundException, IllegalArgumentException {
+        eachFileMatch(self, filter, closure, true);
+    }
+
+    /**
+     * Allows a simple syntax for using timers.  This timer will execute the
+     * given closure after the given delay.
+     *
+     * @param timer   a timer object
+     * @param delay   the delay in milliseconds before running the closure code
+     * @param closure the closure to invoke
+     * @return The timer task which has been scheduled.
+     */
+    public static TimerTask runAfter(Timer timer, int delay, final Closure closure) {
+        TimerTask timerTask = new TimerTask() {
+            public void run() {
+                closure.call();
+            }
+        };
+        timer.schedule(timerTask, delay);
+        return timerTask;
+    }
+
+    /**
+     * Create a buffered reader for this file.
+     *
+     * @param file a File
+     * @return a BufferedReader
+     * @throws IOException if an IOException occurs.
+     */
+    public static BufferedReader newReader(File file) throws IOException {
+        CharsetToolkit toolkit = new CharsetToolkit(file);
+        return toolkit.getReader();
+    }
+
+    /**
+     * Create a buffered reader for this file, with using specified
+     * charset as the encoding.
+     *
+     * @param file    a File
+     * @param charset the charset with which we want to write in the File
+     * @return a BufferedReader
+     * @throws FileNotFoundException        if the File was not found
+     * @throws UnsupportedEncodingException if the encoding specified is not supported
+     */
+    public static BufferedReader newReader(File file, String charset)
+            throws FileNotFoundException, UnsupportedEncodingException {
+        return new BufferedReader(new InputStreamReader(new FileInputStream(file), charset));
+    }
+
+    /**
+     * Creates a reader for this input stream.
+     *
+     * @param self an input stream
+     * @return a reader
+     */
+    public static BufferedReader newReader(InputStream self) {
+        return new BufferedReader(new InputStreamReader(self));
+    }
+
+    /**
+     * Create a new BufferedReader for this file and then
+     * passes it into the closure, ensuring the reader is closed after the
+     * closure returns.
+     *
+     * @param file    a file object
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withReader(File file, Closure closure) throws IOException {
+        withReader(newReader(file), closure);
+    }
+
+    /**
+     * Create a buffered output stream for this file.
+     *
+     * @param file a file object
+     * @return the created OutputStream
+     * @throws IOException if an IOException occurs.
+     */
+    public static BufferedOutputStream newOutputStream(File file) throws IOException {
+        return new BufferedOutputStream(new FileOutputStream(file));
+    }
+
+    /**
+     * Creates a new data output stream for this file.
+     *
+     * @param file a file object
+     * @return the created DataOutputStream
+     * @throws IOException if an IOException occurs.
+     */
+    public static DataOutputStream newDataOutputStream(File file) throws IOException {
+        return new DataOutputStream(new FileOutputStream(file));
+    }
+
+    /**
+     * Creates a new OutputStream for this file and passes it into the closure.
+     * This method ensures the stream is closed after the closure returns.
+     *
+     * @param file    a File
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see #withStream(OutputStream,Closure)
+     */
+    public static void withOutputStream(File file, Closure closure) throws IOException {
+        withStream(newOutputStream(file), closure);
+    }
+
+    /**
+     * Create a new InputStream for this file and passes it into the closure.
+     * This method ensures the stream is closed after the closure returns.
+     *
+     * @param file    a File
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see #withStream(InputStream,Closure)
+     */
+    public static void withInputStream(File file, Closure closure) throws IOException {
+        withStream(newInputStream(file), closure);
+    }
+
+    /**
+     * Create a new DataOutputStream for this file and passes it into the closure.
+     * This method ensures the stream is closed after the closure returns.
+     *
+     * @param file    a File
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see #withStream(OutputStream,Closure)
+     */
+    public static void withDataOutputStream(File file, Closure closure) throws IOException {
+        withStream(newDataOutputStream(file), closure);
+    }
+
+    /**
+     * Create a new DataInputStream for this file and passes it into the closure.
+     * This method ensures the stream is closed after the closure returns.
+     *
+     * @param file    a File
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see #withStream(InputStream,Closure)
+     */
+    public static void withDataInputStream(File file, Closure closure) throws IOException {
+        withStream(newDataInputStream(file), closure);
+    }
+
+    /**
+     * Create a buffered writer for this file.
+     *
+     * @param file a File
+     * @return a BufferedWriter
+     * @throws IOException if an IOException occurs.
+     */
+    public static BufferedWriter newWriter(File file) throws IOException {
+        return new BufferedWriter(new FileWriter(file));
+    }
+
+    /**
+     * Creates a buffered writer for this file, optionally appending to the
+     * existing file content.
+     *
+     * @param file   a File
+     * @param append true if data should be appended to the file
+     * @return a BufferedWriter
+     * @throws IOException if an IOException occurs.
+     */
+    public static BufferedWriter newWriter(File file, boolean append) throws IOException {
+        return new BufferedWriter(new FileWriter(file, append));
+    }
+
+    /**
+     * Helper method to create a buffered writer for a file.  If the given
+     * charset is "UTF-16BE" or "UTF-16LE", the requisite byte order mark is
+     * written to the stream before the writer is returned.
+     *
+     * @param file    a File
+     * @param charset the name of the encoding used to write in this file
+     * @param append  true if in append mode
+     * @return a BufferedWriter
+     * @throws IOException if an IOException occurs.
+     */
+    public static BufferedWriter newWriter(File file, String charset, boolean append) throws IOException {
+        if (append) {
+            return new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, append), charset));
+        } else {
+            // first write the Byte Order Mark for Unicode encodings
+            FileOutputStream stream = new FileOutputStream(file);
+            if ("UTF-16BE".equals(charset)) {
+                writeUtf16Bom(stream, true);
+            } else if ("UTF-16LE".equals(charset)) {
+                writeUtf16Bom(stream, false);
+            }
+            return new BufferedWriter(new OutputStreamWriter(stream, charset));
+        }
+    }
+
+    /**
+     * Creates a buffered writer for this file, writing data using the given
+     * encoding.
+     *
+     * @param file    a File
+     * @param charset the name of the encoding used to write in this file
+     * @return a BufferedWriter
+     * @throws IOException if an IOException occurs.
+     */
+    public static BufferedWriter newWriter(File file, String charset) throws IOException {
+        return newWriter(file, charset, false);
+    }
+
+    /**
+     * Write a Byte Order Mark at the begining of the file
+     *
+     * @param stream    the FileOuputStream to write the BOM to
+     * @param bigEndian true if UTF 16 Big Endian or false if Low Endian
+     * @throws IOException if an IOException occurs.
+     */
+    private static void writeUtf16Bom(FileOutputStream stream, boolean bigEndian) throws IOException {
+        if (bigEndian) {
+            stream.write(-2);
+            stream.write(-1);
+        } else {
+            stream.write(-1);
+            stream.write(-2);
+        }
+    }
+
+    /**
+     * Creates a new BufferedWriter for this file, passes it to the closure, and
+     * ensures the stream is flushed and closed after the closure returns.
+     *
+     * @param file    a File
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withWriter(File file, Closure closure) throws IOException {
+        withWriter(newWriter(file), closure);
+    }
+
+    /**
+     * Creates a new BufferedWriter for this file, passes it to the closure, and
+     * ensures the stream is flushed and closed after the closure returns.
+     * The writer will use the given charset encoding.
+     *
+     * @param file    a File
+     * @param charset the charset used
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withWriter(File file, String charset, Closure closure) throws IOException {
+        withWriter(newWriter(file, charset), closure);
+    }
+
+    /**
+     * Create a new BufferedWriter which will append to this
+     * file.  The writer is passed to the closure and will be closed before
+     * this method returns.
+     *
+     * @param file    a File
+     * @param charset the charset used
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withWriterAppend(File file, String charset, Closure closure) throws IOException {
+        withWriter(newWriter(file, charset, true), closure);
+    }
+
+    /**
+     * Create a new BufferedWriter for this file in append mode.  The writer
+     * is passed to the closure and is closed after the closure returns.
+     *
+     * @param file    a File
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withWriterAppend(File file, Closure closure) throws IOException {
+        withWriter(newWriter(file, true), closure);
+    }
+
+    /**
+     * Create a new PrintWriter for this file.
+     *
+     * @param file a File
+     * @return the created PrintWriter
+     * @throws IOException if an IOException occurs.
+     */
+    public static PrintWriter newPrintWriter(File file) throws IOException {
+        return new PrintWriter(newWriter(file));
+    }
+
+    /**
+     * Create a new PrintWriter for this file, using specified
+     * charset.
+     *
+     * @param file    a File
+     * @param charset the charset
+     * @return a PrintWriter
+     * @throws IOException if an IOException occurs.
+     */
+    public static PrintWriter newPrintWriter(File file, String charset) throws IOException {
+        return new PrintWriter(newWriter(file, charset));
+    }
+
+    /**
+     * Create a new PrintWriter for this file which is then
+     * passed it into the given closure.  This method ensures its the writer
+     * is closed after the closure returns.
+     *
+     * @param file    a File
+     * @param closure the closure to invoke with the PrintWriter
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withPrintWriter(File file, Closure closure) throws IOException {
+        withWriter(newPrintWriter(file), closure);
+    }
+
+    /**
+     * Create a new PrintWriter with a specified charset for
+     * this file.  The writer is passed to the closure, and will be closed
+     * before this method returns.
+     *
+     * @param file    a File
+     * @param charset the charset
+     * @param closure the closure to invoke with the PrintWriter
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withPrintWriter(File file, String charset, Closure closure) throws IOException {
+        withWriter(newPrintWriter(file, charset), closure);
+    }
+
+    /**
+     * Allows this writer to be used within the closure, ensuring that it
+     * is flushed and closed before this method returns.
+     *
+     * @param writer  the writer which is used and then closed
+     * @param closure the closure that the writer is passed into
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withWriter(Writer writer, Closure closure) throws IOException {
+        try {
+            closure.call(writer);
+            try {
+                writer.flush();
+            } catch (IOException e) {
+                // try to continue even in case of error
+            }
+            Writer temp = writer;
+            writer = null;
+            temp.close();
+        } finally {
+            closeWriterWithWarning(writer);
+        }
+    }
+
+    /**
+     * Allows this reader to be used within the closure, ensuring that it
+     * is closed before this method returns.
+     *
+     * @param reader  the reader which is used and then closed
+     * @param closure the closure that the writer is passed into
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withReader(Reader reader, Closure closure) throws IOException {
+        try {
+            closure.call(reader);
+
+            Reader temp = reader;
+            reader = null;
+            temp.close();
+        } finally {
+            closeReaderWithWarning(reader);
+        }
+    }
+
+    /**
+     * Allows this input stream to be used within the closure, ensuring that it
+     * is flushed and closed before this method returns.
+     *
+     * @param stream  the stream which is used and then closed
+     * @param closure the closure that the stream is passed into
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withStream(InputStream stream, Closure closure) throws IOException {
+        try {
+            closure.call(stream);
+
+            InputStream temp = stream;
+            stream = null;
+            temp.close();
+        } finally {
+            closeInputStreamWithWarning(stream);
+        }
+    }
+
+    /**
+     * Reads the stream into a list, with one element for each line.
+     *
+     * @param stream a stream
+     * @return a List of lines
+     * @throws IOException if an IOException occurs.
+     * @see #readLines(Reader)
+     */
+    public static List readLines(InputStream stream) throws IOException {
+        return readLines(new BufferedReader(new InputStreamReader(stream)));
+    }
+
+    /**
+     * Iterates through this stream, passing each line to the closure.  The
+     * stream is closed after the closure returns.
+     *
+     * @param stream  a stream
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see #eachLine(Reader,Closure)
+     */
+    public static void eachLine(InputStream stream, Closure closure) throws IOException {
+        eachLine(new InputStreamReader(stream), closure);
+    }
+
+    /**
+     * Iterates through the lines read from the URL's associated input stream
+     *
+     * @param url     a URL to open and read
+     * @param closure a closure to apply on each line
+     * @throws IOException if an IOException occurs.
+     */
+    public static void eachLine(URL url, Closure closure) throws IOException {
+        eachLine(url.openConnection().getInputStream(), closure);
+    }
+
+    /**
+     * Helper method to create a new BufferedReader for a URL and then
+     * passes it to the closure.  The reader is closed after the closure returns.
+     *
+     * @param url     a URL
+     * @param closure the closure to invoke with the reader
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withReader(URL url, Closure closure) throws IOException {
+        withReader(url.openConnection().getInputStream(), closure);
+    }
+
+    /**
+     * Helper method to create a new BufferedReader for a stream and then
+     * passes it into the closure.  The reader is closed after the closure returns.
+     *
+     * @param in      a stream
+     * @param closure the closure to invoke with the InputStream
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withReader(InputStream in, Closure closure) throws IOException {
+        withReader(new InputStreamReader(in), closure);
+    }
+
+    /**
+     * Creates a writer from this stream, passing it to the given closure.
+     * This method ensures the stream is closed after the closure returns.
+     *
+     * @param stream  the stream which is used and then closed
+     * @param closure the closure that the writer is passed into
+     * @throws IOException if an IOException occurs.
+     * @see #withWriter(Writer,Closure)
+     */
+    public static void withWriter(OutputStream stream, Closure closure) throws IOException {
+        withWriter(new OutputStreamWriter(stream), closure);
+    }
+
+    /**
+     * Creates a writer from this stream, passing it to the given closure.
+     * This method ensures the stream is closed after the closure returns.
+     *
+     * @param stream  the stream which is used and then closed
+     * @param charset the charset used
+     * @param closure the closure that the writer is passed into
+     * @throws IOException if an IOException occurs.
+     * @see #withWriter(Writer,Closure)
+     */
+    public static void withWriter(OutputStream stream, String charset, Closure closure) throws IOException {
+        withWriter(new OutputStreamWriter(stream, charset), closure);
+    }
+
+    /**
+     * Passes this OutputStream to the closure, ensuring that the stream
+     * is closed after the closure returns, regardless of errors.
+     *
+     * @param os      the stream which is used and then closed
+     * @param closure the closure that the stream is passed into
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withStream(OutputStream os, Closure closure) throws IOException {
+        try {
+            closure.call(os);
+            os.flush();
+
+            OutputStream temp = os;
+            os = null;
+            temp.close();
+        } finally {
+            closeOutputStreamWithWarning(os);
+        }
+    }
+
+    /**
+     * Creates a buffered input stream for this file.
+     *
+     * @param file a File
+     * @return a BufferedInputStream of the file
+     * @throws FileNotFoundException if the file is not found.
+     */
+    public static BufferedInputStream newInputStream(File file) throws FileNotFoundException {
+        return new BufferedInputStream(new FileInputStream(file));
+    }
+
+    /**
+     * Create a data input stream for this file
+     *
+     * @param file a File
+     * @return a DataInputStream of the file
+     * @throws FileNotFoundException if the file is not found.
+     */
+    public static DataInputStream newDataInputStream(File file) throws FileNotFoundException {
+        return new DataInputStream(new FileInputStream(file));
+    }
+
+    /**
+     * Traverse through each byte of this File
+     *
+     * @param self    a File
+     * @param closure a closure
+     * @throws IOException if an IOException occurs.
+     * @see #eachByte(InputStream,Closure)
+     */
+    public static void eachByte(File self, Closure closure) throws IOException {
+        BufferedInputStream is = newInputStream(self);
+        eachByte(is, closure);
+    }
+
+    /**
+     * Traverse through each byte of the specified stream. The
+     * stream is closed after the closure returns.
+     *
+     * @param is      stream to iterate over, closed after the method call
+     * @param closure closure to apply to each byte
+     * @throws IOException if an IOException occurs.
+     */
+    public static void eachByte(InputStream is, Closure closure) throws IOException {
+        try {
+            while (true) {
+                int b = is.read();
+                if (b == -1) {
+                    break;
+                } else {
+                    closure.call(new Byte((byte) b));
+                }
+            }
+
+            InputStream temp = is;
+            is = null;
+            temp.close();
+        } finally {
+            closeInputStreamWithWarning(is);
+        }
+    }
+
+    /**
+     * Reads the InputStream from this URL, passing each byte to the given
+     * closure.  The URL stream will be closed before this method returns.
+     *
+     * @param url     url to iterate over
+     * @param closure closure to apply to each byte
+     * @throws IOException if an IOException occurs.
+     * @see #eachByte(InputStream,Closure)
+     */
+    public static void eachByte(URL url, Closure closure) throws IOException {
+        InputStream is = url.openConnection().getInputStream();
+        eachByte(is, closure);
+    }
+
+    /**
+     * Transforms each character from this reader by passing it to the given
+     * closure.  The Closure should return each transformed character, which
+     * will be passed to the Writer.  The reader and writer will be both be
+     * closed before this method returns.
+     *
+     * @param self    a Reader object
+     * @param writer  a Writer to receive the transformed characters
+     * @param closure a closure that performs the required transformation
+     * @throws IOException if an IOException occurs.
+     */
+    public static void transformChar(Reader self, Writer writer, Closure closure) throws IOException {
+        int c;
+        try {
+            char[] chars = new char[1];
+            while ((c = self.read()) != -1) {
+                chars[0] = (char) c;
+                writer.write((String) closure.call(new String(chars)));
+            }
+            writer.flush();
+
+            Writer temp2 = writer;
+            writer = null;
+            temp2.close();
+            Reader temp1 = self;
+            self = null;
+            temp1.close();
+        } finally {
+            closeReaderWithWarning(self);
+            closeWriterWithWarning(writer);
+        }
+    }
+
+    /**
+     * Transforms the lines from a reader with a Closure and
+     * write them to a writer. Both Reader and Writer are
+     * closed after the operation.
+     *
+     * @param reader  Lines of text to be transformed. Reader is closed afterwards.
+     * @param writer  Where transformed lines are written. Writer is closed afterwards.
+     * @param closure Single parameter closure that is called to transform each line of
+     *                text from the reader, before writing it to the writer.
+     * @throws IOException if an IOException occurs.
+     */
+    public static void transformLine(Reader reader, Writer writer, Closure closure) throws IOException {
+        BufferedReader br = new BufferedReader(reader);
+        BufferedWriter bw = new BufferedWriter(writer);
+        String line;
+        try {
+            while ((line = br.readLine()) != null) {
+                Object o = closure.call(line);
+                if (o != null) {
+                    bw.write(o.toString());
+                    bw.newLine();
+                }
+            }
+            bw.flush();
+
+            Writer temp2 = writer;
+            writer = null;
+            temp2.close();
+            Reader temp1 = reader;
+            reader = null;
+            temp1.close();
+        } finally {
+            closeReaderWithWarning(br);
+            closeReaderWithWarning(reader);
+            closeWriterWithWarning(bw);
+            closeWriterWithWarning(writer);
+        }
+    }
+
+    /**
+     * Filter the lines from a reader and write them on the writer,
+     * according to a closure which returns true if the line should be included.
+     * Both Reader and Writer are closed after the operation.
+     *
+     * @param reader  a reader, closed after the call
+     * @param writer  a writer, closed after the call
+     * @param closure the closure which returns booleans
+     * @throws IOException if an IOException occurs.
+     */
+    public static void filterLine(Reader reader, Writer writer, Closure closure) throws IOException {
+        BufferedReader br = new BufferedReader(reader);
+        BufferedWriter bw = new BufferedWriter(writer);
+        String line;
+        try {
+            while ((line = br.readLine()) != null) {
+                if (DefaultTypeTransformation.castToBoolean(closure.call(line))) {
+                    bw.write(line);
+                    bw.newLine();
+                }
+            }
+            bw.flush();
+
+            Writer temp2 = writer;
+            writer = null;
+            temp2.close();
+            Reader temp1 = reader;
+            reader = null;
+            temp1.close();
+        } finally {
+            closeReaderWithWarning(br);
+            closeReaderWithWarning(reader);
+            closeWriterWithWarning(bw);
+            closeWriterWithWarning(writer);
+        }
+
+    }
+
+    /**
+     * Filters the lines of a File and creates a Writeable in return to
+     * stream the filtered lines.
+     *
+     * @param self    a File
+     * @param closure a closure which returns a boolean indicating to filter
+     *                the line or not
+     * @return a Writable closure
+     * @throws IOException if <code>self</code> is not readable
+     * @see #filterLine(Reader,Closure)
+     */
+    public static Writable filterLine(File self, Closure closure) throws IOException {
+        return filterLine(newReader(self), closure);
+    }
+
+    /**
+     * Filter the lines from this File, and write them to the given writer based
+     * on the given closure predicate.
+     *
+     * @param self    a File
+     * @param writer  a writer destination to write filtered lines to
+     * @param closure a closure which takes each line as a parameter and returns
+     *                <code>true</code> if the line should be written to this writer.
+     * @throws IOException if <code>self</code> is not readable
+     * @see #filterLine(Reader,Writer,Closure)
+     */
+    public static void filterLine(File self, Writer writer, Closure closure) throws IOException {
+        filterLine(newReader(self), writer, closure);
+    }
+
+    /**
+     * Filter the lines from this Reader, and return a Writable which can be
+     * used to stream the filtered lines to a destination.  The closure should
+     * return <code>true</code> if the line should be passed to the writer.
+     *
+     * @param reader  this reader
+     * @param closure a closure used for filtering
+     * @return a Writable which will use the closure to filter each line
+     *         from the reader when the Writable#writeTo(Writer) is called.
+     */
+    public static Writable filterLine(Reader reader, final Closure closure) {
+        final BufferedReader br = new BufferedReader(reader);
+        return new Writable() {
+            public Writer writeTo(Writer out) throws IOException {
+                BufferedWriter bw = new BufferedWriter(out);
+                String line;
+                while ((line = br.readLine()) != null) {
+                    if (DefaultTypeTransformation.castToBoolean(closure.call(line))) {
+                        bw.write(line);
+                        bw.newLine();
+                    }
+                }
+                bw.flush();
+                return out;
+            }
+
+            public String toString() {
+                StringWriter buffer = new StringWriter();
+                try {
+                    writeTo(buffer);
+                } catch (IOException e) {
+                    throw new StringWriterIOException(e);
+                }
+                return buffer.toString();
+            }
+        };
+    }
+
+    /**
+     * Filter lines from an input stream using a closure predicate.  The closure
+     * will be passed each line as a String, and it should return
+     * <code>true</code> if the line should be passed to the writer.
+     *
+     * @param self      an input stream
+     * @param predicate a closure which returns boolean and takes a line
+     * @return a writable which writes out the filtered lines
+     * @see #filterLine(Reader, Closure)
+     */
+    public static Writable filterLine(InputStream self, Closure predicate) {
+        return filterLine(newReader(self), predicate);
+    }
+
+    /**
+     * Uses a closure to filter lines from this InputStream and pass them to
+     * the given writer. The closure will be passed each line as a String, and
+     * it should return <code>true</code> if the line should be passed to the
+     * writer.
+     *
+     * @param self      the InputStream
+     * @param writer    a writer to write output to
+     * @param predicate a closure which returns true if a line should be accepted
+     * @throws IOException if an IOException occurs.
+     * @see #filterLine(Reader,Writer,Closure)
+     */
+    public static void filterLine(InputStream self, Writer writer, Closure predicate)
+            throws IOException {
+        filterLine(newReader(self), writer, predicate);
+    }
+
+    /**
+     * Reads the content of the file into a byte array.
+     *
+     * @param file a File
+     * @return a byte array with the contents of the file.
+     * @throws IOException if an IOException occurs.
+     */
+    public static byte[] readBytes(File file) throws IOException {
+        byte[] bytes = new byte[(int) file.length()];
+        FileInputStream fileInputStream = new FileInputStream(file);
+        DataInputStream dis = new DataInputStream(fileInputStream);
+        try {
+            dis.readFully(bytes);
+
+            InputStream temp = dis;
+            dis = null;
+            temp.close();
+        } finally {
+            if (dis != null) {
+                try {
+                    dis.close();
+                } catch (IOException e) {
+                    LOG.warning("Caught exception closing DataInputStream: " + e);
+                }
+            }
+        }
+        return bytes;
+    }
+
+    // ================================
+    // Socket and ServerSocket methods
+
+    /**
+     * Passes the Socket's InputStream and OutputStream to the closure.  The
+     * streams will be closed after the closure returns, even if an exception
+     * is thrown.
+     *
+     * @param socket  a Socket
+     * @param closure a Closure
+     * @throws IOException if an IOException occurs.
+     */
+    public static void withStreams(Socket socket, Closure closure) throws IOException {
+        InputStream input = socket.getInputStream();
+        OutputStream output = socket.getOutputStream();
+        try {
+            closure.call(new Object[]{input, output});
+
+            InputStream temp1 = input;
+            input = null;
+            temp1.close();
+            OutputStream temp2 = output;
+            output = null;
+            temp2.close();
+        } finally {
+            closeInputStreamWithWarning(input);
+            closeOutputStreamWithWarning(output);
+        }
+    }
+
+    /**
+     * Creates an InputObjectStream and an OutputObjectStream from a Socket, and
+     * passes them to the closure.  The streams will be closed after the closure
+     * returns, even if an exception is thrown.
+     *
+     * @param socket  this Socket
+     * @param closure a Closure
+     * @throws IOException if an IOException occurs.
+     * @since 1.1 beta 2
+     */
+    public static void withObjectStreams(Socket socket, Closure closure) throws IOException {
+        InputStream input = socket.getInputStream();
+        OutputStream output = socket.getOutputStream();
+        ObjectOutputStream oos = new ObjectOutputStream(output);
+        ObjectInputStream ois = new ObjectInputStream(input);
+        try {
+            closure.call(new Object[]{ois, oos});
+
+            InputStream temp1 = ois;
+            ois = null;
+            temp1.close();
+            temp1 = input;
+            input = null;
+            temp1.close();
+            OutputStream temp2 = oos;
+            oos = null;
+            temp2.close();
+            temp2 = output;
+            output = null;
+            temp2.close();
+        } finally {
+            closeInputStreamWithWarning(ois);
+            closeInputStreamWithWarning(input);
+            closeOutputStreamWithWarning(oos);
+            closeOutputStreamWithWarning(output);
+        }
+    }
+
+    // TODO reduce duplication by using Closable if we raise minimum requirement to Java 1.5
+    private static void closeInputStreamWithWarning(InputStream input) {
+        if (input != null) {
+            try {
+                input.close();
+            } catch (IOException e) {
+                LOG.warning("Caught exception closing InputStream: " + e);
+            }
+        }
+    }
+
+    private static void closeOutputStreamWithWarning(OutputStream output) {
+        if (output != null) {
+            try {
+                output.close();
+            } catch (IOException e) {
+                LOG.warning("Caught exception closing OutputStream: " + e);
+            }
+        }
+    }
+
+    private static void closeReaderWithWarning(Reader reader) {
+        if (reader != null) {
+            try {
+                reader.close();
+            } catch (Exception e) {
+                // ignore this exception since this
+                // is only our internal problem
+                LOG.warning("Caught exception closing Reader: " + e);
+            }
+        }
+    }
+
+    private static void closeWriterWithWarning(Writer writer) {
+        if (writer != null) {
+            try {
+                writer.close();
+            } catch (IOException e) {
+                LOG.warning("Caught exception closing Writer: " + e);
+            }
+        }
+    }
+
+    /**
+     * Overloads the left shift operator to provide an append mechanism to
+     * add things to the output stream of a socket
+     *
+     * @param self  a Socket
+     * @param value a value to append
+     * @return a Writer
+     * @throws IOException if an IOException occurs.
+     */
+    public static Writer leftShift(Socket self, Object value) throws IOException {
+        return leftShift(self.getOutputStream(), value);
+    }
+
+    /**
+     * Overloads the left shift operator to provide an append mechanism
+     * to add bytes to the output stream of a socket
+     *
+     * @param self  a Socket
+     * @param value a value to append
+     * @return an OutputStream
+     * @throws IOException if an IOException occurs.
+     */
+    public static OutputStream leftShift(Socket self, byte[] value) throws IOException {
+        return leftShift(self.getOutputStream(), value);
+    }
+
+    /**
+     * Accepts a connection and passes the resulting Socket to the closure
+     * which runs in a new Thread.
+     *
+     * @param serverSocket a ServerSocket
+     * @param closure      a Closure
+     * @return a Socket
+     * @throws IOException if an IOException occurs.
+     * @see java.net.ServerSocket#accept()
+     */
+    public static Socket accept(ServerSocket serverSocket, final Closure closure) throws IOException {
+        final Socket socket = serverSocket.accept();
+        new Thread(new Runnable() {
+            public void run() {
+                try {
+                    closure.call(socket);
+                } finally {
+                    try {
+                        socket.close();
+                    } catch (IOException e) {
+                        LOG.warning("Caught exception closing socket: " + e);
+                    }
+                }
+            }
+        }).start();
+        return socket;
+    }
+
+
+    /**
+     * Converts this File to a {@link Writable}.
+     *
+     * @param file a File
+     * @return a File which wraps the input file and which implements Writable
+     */
+    public static File asWritable(File file) {
+        return new WritableFile(file);
+    }
+
+    /**
+     * Converts this File to a {@link Writable} or delegates to default
+     * {@link #asType(Object,Class)}.
+     *
+     * @param f a File
+     * @param c the desired class
+     * @return the converted object
+     */
+    public static Object asType(File f, Class c) {
+        if (c == Writable.class) {
+            return asWritable(f);
+        }
+        return asType((Object) f, c);
+    }
+
+    /**
+     * Allows a file to return a Writable implementation that can output itself
+     * to a Writer stream.
+     *
+     * @param file     a File
+     * @param encoding the encoding to be used when reading the file's contents
+     * @return File which wraps the input file and which implements Writable
+     */
+    public static File asWritable(File file, String encoding) {
+        return new WritableFile(file, encoding);
+    }
+
+    /**
+     * Converts the given String into a List of strings of one character.
+     *
+     * @param self a String
+     * @return a List of characters (a 1-character String)
+     */
+    public static List toList(String self) {
+        int size = self.length();
+        List answer = new ArrayList(size);
+        for (int i = 0; i < size; i++) {
+            answer.add(self.substring(i, i + 1));
+        }
+        return answer;
+    }
+
+    /**
+     * Converts the GString to a File, or delegates to the default
+     * {@link #asType(Object,Class)}
+     *
+     * @param self a GString
+     * @param c    the desired class
+     * @return the converted object
+     */
+    public static Object asType(GString self, Class c) {
+        if (c == File.class) {
+            return new File(self.toString());
+        }
+        return asType((Object) self, c);
+    }
+
+    /**
+     * <p>Provides a method to perform custom 'dynamic' type conversion
+     * to the given class using the <code>as</code> operator.</p>
+     * <strong>Example:</strong> <code>'123' as Double</code>
+     * <p>By default, the following types are supported:
+     * <ul>
+     * <li>List</li>
+     * <li>BigDecimal</li>
+     * <li>BigInteger</li>
+     * <li>Character</li>
+     * <li>Character</li>
+     * <li>Double</li>
+     * <li>Float</li>
+     * <li>File</li>
+     * </ul>
+     * If any other type is given, the call is delegated to
+     * {@link #asType(Object,Class)}.
+     *
+     * @param self a String
+     * @param c    the desired class
+     * @return the converted object
+     */
+    public static Object asType(String self, Class c) {
+        if (c == List.class) {
+            return toList(self);
+        } else if (c == BigDecimal.class) {
+            return toBigDecimal(self);
+        } else if (c == BigInteger.class) {
+            return toBigInteger(self);
+        } else if (c == Character.class) {
+            return toCharacter(self);
+        } else if (c == Double.class) {
+            return toDouble(self);
+        } else if (c == Float.class) {
+            return toFloat(self);
+        } else if (c == File.class) {
+            return new File(self);
+        }
+        return asType((Object) self, c);
+    }
+
+    // Process methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * An alias method so that a process appears similar to System.out, System.in, System.err;
+     * you can use process.in, process.out, process.err in a similar fashion.
+     *
+     * @param self a Process instance
+     * @return the InputStream for the process
+     */
+    public static InputStream getIn(Process self) {
+        return self.getInputStream();
+    }
+
+    /**
+     * Read the text of the output stream of the Process.
+     *
+     * @param self a Process instance
+     * @return the text of the output
+     * @throws IOException if an IOException occurs.
+     */
+    public static String getText(Process self) throws IOException {
+        return getText(new BufferedReader(new InputStreamReader(self.getInputStream())));
+    }
+
+    /**
+     * An alias method so that a process appears similar to System.out, System.in, System.err;
+     * you can use process.in, process.out, process.err in a similar fashion.
+     *
+     * @param self a Process instance
+     * @return the error InputStream for the process
+     */
+    public static InputStream getErr(Process self) {
+        return self.getErrorStream();
+    }
+
+    /**
+     * An alias method so that a process appears similar to System.out, System.in, System.err;
+     * you can use process.in, process.out, process.err in a similar fashion.
+     *
+     * @param self a Process instance
+     * @return the OutputStream for the process
+     */
+    public static OutputStream getOut(Process self) {
+        return self.getOutputStream();
+    }
+
+    /**
+     * Overloads the left shift operator (&lt;&lt;) to provide an append mechanism
+     * to pipe data to a Process.
+     *
+     * @param self  a Process instance
+     * @param value a value to append
+     * @return a Writer
+     * @throws IOException if an IOException occurs.
+     */
+    public static Writer leftShift(Process self, Object value) throws IOException {
+        return leftShift(self.getOutputStream(), value);
+    }
+
+    /**
+     * Overloads the left shift operator to provide an append mechanism
+     * to pipe into a Process
+     *
+     * @param self  a Process instance
+     * @param value data to append
+     * @return an OutputStream
+     * @throws IOException if an IOException occurs.
+     */
+    public static OutputStream leftShift(Process self, byte[] value) throws IOException {
+        return leftShift(self.getOutputStream(), value);
+    }
+
+    /**
+     * Wait for the process to finish during a certain amount of time, otherwise stops the process.
+     *
+     * @param self           a Process
+     * @param numberOfMillis the number of milliseconds to wait before stopping the process
+     */
+    public static void waitForOrKill(Process self, long numberOfMillis) {
+        ProcessRunner runnable = new ProcessRunner(self);
+        Thread thread = new Thread(runnable);
+        thread.start();
+        runnable.waitForOrKill(numberOfMillis);
+    }
+
+    /**
+     * Gets the output and error streams from a process and reads them
+     * to keep the process from blocking due to a full ouput buffer. For this
+     * two Threads are started, so this method will return immediately.
+     *
+     * @param self a Process
+     */
+    public static void consumeProcessOutput(Process self) {
+        Dumper d = new Dumper(self.getErrorStream());
+        Thread t = new Thread(d);
+        t.start();
+        d = new Dumper(self.getInputStream());
+        t = new Thread(d);
+        t.start();
+    }
+
+    /**
+     * Process each regex group matched substring of the given string. If the closure
+     * parameter takes one argument, an array with all match groups is passed to it.
+     * If the closure takes as many arguments as there are match groups, then each
+     * parameter will be one match group.
+     *
+     * @param self    the source string
+     * @param regex   a Regex string
+     * @param closure a closure with one parameter or as much parameters as groups
+     */
+    public static void eachMatch(String self, String regex, Closure closure) {
+        Pattern p = Pattern.compile(regex);
+        Matcher m = p.matcher(self);
+        while (m.find()) {
+            int count = m.groupCount();
+            List groups = new ArrayList();
+            for (int i = 0; i <= count; i++) {
+                groups.add(m.group(i));
+            }
+            if (groups.size() == 1 || closure.getMaximumNumberOfParameters() < groups.size()) {
+                // not enough parameters there to give each group part
+                // it's own parameter, so try a closure with one parameter
+                // and give it all groups as a array
+                closure.call((Object) groups.toArray());
+            } else {
+                closure.call(groups.toArray());
+            }
+        }
+    }
+
+    /**
+     * Process each matched substring of the given group matcher. The object
+     * passed to the closure is an array of strings, matched per a successful match.
+     *
+     * @param self    the source matcher
+     * @param closure a closure
+     * @return the matcher
+     */
+    public static Matcher each(Matcher self, Closure closure) {
+        while (self.find()) {
+            int count = self.groupCount();
+            List groups = new ArrayList();
+            for (int i = 0; i <= count; i++) {
+                groups.add(self.group(i));
+            }
+            closure.call(groups.toArray());
+        }
+        return self;
+    }
+
+    /**
+     * Iterates over every element of the collection and returns the index of the first object
+     * that matches the condition specified in the closure
+     *
+     * @param self    the iteration object over which we iterate
+     * @param closure the filter to perform a match on the collection
+     * @return an integer that is the index of the first macthed object.
+     */
+    public static int findIndexOf(Object self, Closure closure) {
+        int i = 0;
+        for (Iterator iter = InvokerHelper.asIterator(self); iter.hasNext(); i++) {
+            Object value = iter.next();
+            if (DefaultTypeTransformation.castToBoolean(closure.call(value))) {
+                break;
+            }
+        }
+        return i;
+    }
+
+    /**
+     * Iterates through the classloader parents until it finds a loader with a class
+     * named "org.codehaus.groovy.tools.RootLoader". If there is no such class
+     * <code>null</code> will be returned. The name is used for comparison because
+     * a direct comparison using == may fail as the class may be loaded through
+     * different classloaders.
+     *
+     * @param self a ClassLoader
+     * @return the rootLoader for the ClassLoader
+     * @see org.codehaus.groovy.tools.RootLoader
+     */
+    public static ClassLoader getRootLoader(ClassLoader self) {
+        while (true) {
+            if (self == null) return null;
+            if (isRootLoaderClassOrSubClass(self)) return self;
+            self = self.getParent();
+        }
+    }
+
+    private static boolean isRootLoaderClassOrSubClass(ClassLoader self) {
+        Class current = self.getClass();
+        while(!current.getName().equals(Object.class.getName())) {
+            if(current.getName().equals(RootLoader.class.getName())) return true;
+            current = current.getSuperclass();
+        }
+        
+        return false;
+    }
+
+
+    /**
+     * Converts a given object to a type. This method is used through
+     * the "as" operator and is overloadable as any other operator.
+     *
+     * @param obj  the object to convert
+     * @param type the goal type
+     * @return the resulting object
+     */
+    public static Object asType(Object obj, Class type) {
+        return DefaultTypeTransformation.castToType(obj, type);
+    }
+
+    /**
+     * Convenience method to dynamically create a new instance of this
+     * class.  Calls the default constructor.
+     *
+     * @param c a class
+     * @return a new instance of this class
+     */
+    public static Object newInstance(Class c) {
+        return InvokerHelper.getInstance().invokeConstructorOf(c, null);
+    }
+
+    /**
+     * Helper to construct a new instance from the given arguments.
+     * The constructor is called based on the number and types in the
+     * args array.  Use <code>newInstance(null)</code> or simply
+     * <code>newInstance()</code> for the default (no-arg) constructor.
+     *
+     * @param c    a class
+     * @param args the constructor arguments
+     * @return a new instance of this class.
+     */
+    public static Object newInstance(Class c, Object[] args) {
+        if (args == null) args = new Object[]{null};
+        return InvokerHelper.getInstance().invokeConstructorOf(c, args);
+    }
+
+
+    /**
+     * Adds a "metaClass" property to all class objects so you can use the syntax
+     * <code>String.metaClass.myMethod = { println "foo" }</code>
+     *
+     * @param c The java.lang.Class instance
+     * @return An MetaClass instance
+     */
+    public static MetaClass getMetaClass(Class c) {
+        MetaClassRegistry metaClassRegistry = GroovySystem.getMetaClassRegistry();
+        MetaClass mc = metaClassRegistry.getMetaClass(c);
+        if (mc instanceof ExpandoMetaClass
+                || mc instanceof DelegatingMetaClass && ((DelegatingMetaClass) mc).getAdaptee() instanceof ExpandoMetaClass)
+            return mc;
+        else {
+            MetaClass emc = ExpandoMetaClassCreationHandle.instance.create(c, metaClassRegistry);
+            emc.initialize();
+            metaClassRegistry.setMetaClass(c, emc);
+            return emc;
+        }
+    }
+
+    /**
+     * Obtains a MetaClass for an object either from the registry or in the case of
+     * a GroovyObject from the object itself.
+     *
+     * @param obj The object in question
+     * @return The MetaClass
+     */
+    public static MetaClass getMetaClass(Object obj) {
+        if (obj instanceof GroovyObject) {
+            return ((GroovyObject) obj).getMetaClass();
+        }
+        return GroovySystem.getMetaClassRegistry().getMetaClass(obj.getClass());
+    }
+
+    /**
+     * A Runnable which waits for a process to complete together with a notification scheme
+     * allowing another thread to wait a maximum number of seconds for the process to complete
+     * before killing it.
+     */
+    protected static class ProcessRunner implements Runnable {
+        Process process;
+        private boolean finished;
+
+        public ProcessRunner(Process process) {
+            this.process = process;
+        }
+
+        public void run() {
+            try {
+                process.waitFor();
+            } catch (InterruptedException e) {
+                // Ignore
+            }
+            synchronized (this) {
+                notifyAll();
+                finished = true;
+            }
+        }
+
+        public synchronized void waitForOrKill(long millis) {
+            if (!finished) {
+                try {
+                    wait(millis);
+                } catch (InterruptedException e) {
+                    // Ignore
+                }
+                if (!finished) {
+                    process.destroy();
+                }
+            }
+        }
+    }
+
+    protected static class RangeInfo {
+        protected int from, to;
+        protected boolean reverse;
+
+        public RangeInfo(int from, int to, boolean reverse) {
+            this.from = from;
+            this.to = to;
+            this.reverse = reverse;
+        }
+    }
+
+    private static class Dumper implements Runnable {
+        InputStream in;
+
+        public Dumper(InputStream in) {
+            this.in = in;
+        }
+
+        public void run() {
+            InputStreamReader isr = new InputStreamReader(in);
+            BufferedReader br = new BufferedReader(isr);
+            try {
+                while (br.readLine() != null) {
+                }
+            } catch (IOException e) {
+                throw new GroovyRuntimeException("exception while reading process stream", e);
+            }
+        }
+    }
+
+    /**
+     * Attempts to create an Iterator for the given object by first
+     * converting it to a Collection.
+     *
+     * @param o an object
+     * @return an Iterator for the given Object.
+     * @see DefaultTypeTransformation#asCollection(Object)
+     */
+    public static Iterator iterator(Object o) {
+        return DefaultTypeTransformation.asCollection(o).iterator();
+    }
+
+    /**
+     * Allows an Enumeration to behave like an Iterator.  Note that the
+     * {@link Iterator#remove() remove()} method is unsupported since the
+     * underlying Enumeration does not provide a mechanism for removing items.
+     *
+     * @param enumeration an Enumeration object
+     * @return an Iterator for the given Enumeration
+     */
+    public static Iterator iterator(final Enumeration enumeration) {
+        return new Iterator() {
+            private Object last;
+
+            public boolean hasNext() {
+                return enumeration.hasMoreElements();
+            }
+
+            public Object next() {
+                last = enumeration.nextElement();
+                return last;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException("Cannot remove() from an Enumeration");
+            }
+        };
+    }
+
+    // TODO move into DOMCategory once we can make use of optional categories transparent
+
+    /**
+     * Makes NodeList iterable by returning a read-only Iterator which traverses
+     * over each Node.
+     *
+     * @param nodeList a NodeList
+     * @return an Iterator for a NodeList
+     */
+    public static Iterator iterator(final NodeList nodeList) {
+        return new Iterator() {
+            private int current /* = 0 */;
+
+            public boolean hasNext() {
+                return current < nodeList.getLength();
+            }
+
+            public Object next() {
+                return nodeList.item(current++);
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException("Cannot remove() from a NodeList iterator");
+            }
+        };
+    }
+
+    /**
+     * Retuns an {@link Iterator} which traverses each match.
+     *
+     * @param matcher a Matcher object
+     * @return an Iterator for a Matcher
+     * @see Matcher#group()
+     */
+    public static Iterator iterator(final Matcher matcher) {
+        return new Iterator() {
+            private boolean found /* = false */;
+            private boolean done /* = false */;
+
+            public boolean hasNext() {
+                if (done) {
+                    return false;
+                }
+                if (!found) {
+                    found = matcher.find();
+                    if (!found) {
+                        done = true;
+                    }
+                }
+                return found;
+            }
+
+            public Object next() {
+                if (!found) {
+                    if (!hasNext()) {
+                        throw new NoSuchElementException();
+                    }
+                }
+                found = false;
+                return matcher.group();
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * Creates an iterator which will traverse through the reader a line at a time.
+     *
+     * @param self a Reader object
+     * @return an Iterator for the Reader
+     * @see java.io.BufferedReader#readLine()
+     */
+    public static Iterator iterator(Reader self) {
+        final BufferedReader bufferedReader;
+        if (self instanceof BufferedReader)
+            bufferedReader = (BufferedReader) self;
+        else
+            bufferedReader = new BufferedReader(self);
+        return new Iterator() {
+            String nextVal /* = null */;
+            boolean nextMustRead = true;
+            boolean hasNext = true;
+
+            public boolean hasNext() {
+                if (nextMustRead && hasNext) {
+                    try {
+                        nextVal = readNext();
+                        nextMustRead = false;
+                    } catch (IOException e) {
+                        hasNext = false;
+                    }
+                }
+                return hasNext;
+            }
+
+            public Object next() {
+                String retval = null;
+                if (nextMustRead) {
+                    try {
+                        retval = readNext();
+                    } catch (IOException e) {
+                        hasNext = false;
+                    }
+                } else
+                    retval = nextVal;
+                nextMustRead = true;
+                return retval;
+            }
+
+            private String readNext() throws IOException {
+                String nv = bufferedReader.readLine();
+                if (nv == null)
+                    hasNext = false;
+                return nv;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException("Cannot remove() from a Reader Iterator");
+            }
+        };
+    }
+
+    /**
+     * Standard iterator for a input stream which iterates through the stream
+     * content in a byte-based fashion.
+     *
+     * @param self an InputStream object
+     * @return an Iterator for the InputStream
+     */
+    public static Iterator iterator(InputStream self) {
+        return iterator(new DataInputStream(self));
+    }
+
+    /**
+     * Standard iterator for a data input stream which iterates through the
+     * stream content a byte at a time.
+     *
+     * @param self a DataInputStream object
+     * @return an Iterator for the DataInputStream
+     */
+    public static Iterator iterator(final DataInputStream self) {
+        return new Iterator() {
+            Byte nextVal;
+            boolean nextMustRead = true;
+            boolean hasNext = true;
+
+            public boolean hasNext() {
+                if (nextMustRead && hasNext) {
+                    try {
+                        byte bPrimitive = self.readByte();
+                        nextVal = new Byte(bPrimitive);
+                        nextMustRead = false;
+                    } catch (IOException e) {
+                        hasNext = false;
+                    }
+                }
+                return hasNext;
+            }
+
+            public Object next() {
+                Byte retval = null;
+                if (nextMustRead) {
+                    try {
+                        byte b = self.readByte();
+                        retval = new Byte(b);
+                    } catch (IOException e) {
+                        hasNext = false;
+                    }
+                } else
+                    retval = nextVal;
+                nextMustRead = true;
+                return retval;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException("Cannot remove() from an InputStream Iterator");
+            }
+        };
+    }
+
+    /**
+     * Standard iterator for a text file which iterates through the file content
+     * one line at a time.
+     *
+     * @param self a file object
+     * @return a line-based iterator
+     * @throws IOException if there is a problem processing the file (e.g. file is not found)
+     * @deprecated use File#eachLine instead please
+     */
+    public static Iterator iterator(File self) throws IOException {
+        throw new DeprecationException(
+                "Iterators on files are not supported any more. " +
+                        "Use File.eachLine() instead. Alternatively you can use FileReader.iterator() " +
+                        "and provide your own exception handling."
+        );
+    }
+
+    /**
+     * An identity function for iterators, supporting 'duck-typing' when trying to get an
+     * iterator for each object within a collection, some of which may already be iterators.
+     *
+     * @param self an iterator object
+     * @return itself
+     */
+    public static Iterator iterator(Iterator self) {
+        return self;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java b/groovy/src/main/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java
new file mode 100644
index 0000000..07017a3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+
+import java.util.regex.Matcher;
+
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+/**
+ * This class defines all the new static groovy methods which appear on normal JDK
+ * classes inside the Groovy environment. Static methods are used with the
+ * first parameter as the destination class.
+ *
+ * @author Guillaume Laforge
+ * @author Dierk Koenig
+ * @author Joachim Baumann
+ * @version $Revision$
+ */
+public class DefaultGroovyStaticMethods {
+
+    /**
+     * Start a Thread with the given closure as a Runnable instance.
+     *
+     * @param self the thread on which the method is called
+     * @param closure the Runnable closure
+     * @return the started thread
+     */
+    public static Thread start(Thread self, Closure closure) {
+        Thread thread = new Thread(closure);
+        thread.start();
+        return thread;
+    }
+
+    /**
+     * Start a daemon Thread with the given closure as a Runnable instance.
+     *
+     * @param self the thread on which the method is called
+     * @param closure the Runnable closure
+     * @return the started thread
+     */
+    public static Thread startDaemon(Thread self, Closure closure) {
+        Thread thread = new Thread(closure);
+        thread.setDaemon(true);
+        thread.start();
+        return thread;
+    }
+
+    /**
+     * Get the last hidden matcher that system used to do a match.
+     * 
+     * @param matcher
+     * @return the last regex matcher
+     */
+    public static Matcher getLastMatcher(Matcher matcher) {
+        return RegexSupport.getLastMatcher();
+    }
+
+    /**
+     * This method is used by both sleep() methods to imlement sleeping
+     * for the given time even if interrupted
+     * @param object receiver
+     * @param millis the number of milliseconds to sleep
+     * @param closure optional closure called when interrupted
+     *                if the closure returns true the sleep continues
+     */
+    protected static void sleepImpl(Object object, long millis, Closure closure) {
+        long start = System.currentTimeMillis();
+        long rest = millis;
+        long current;
+        while (rest > 0) {
+            try {
+                Thread.sleep(rest);
+                rest = 0;
+            } catch (InterruptedException e) {
+                if (closure != null) {
+                    if (DefaultTypeTransformation.castToBoolean(closure.call(e))) {
+                        return;
+                    }
+                }
+                current = System.currentTimeMillis(); // compensate for closure's time
+                rest = millis + start - current;
+            }
+        }
+    }
+
+    /**
+     * Sleep for so many milliseconds, even if interrupted.
+     * 
+     * @param object receiver
+     * @param milliseconds the number of milliseconds to sleep
+     */
+    public static void sleep(Object object, long milliseconds) {
+	sleepImpl(object, milliseconds, null);
+    }
+
+    /**
+     * Sleep for so many milliseconds
+     * 
+     * @param object receiver
+     * @param milliseconds the number of milliseconds to sleep
+     * @param onInterrupt interrupt handler, InterruptedException is passed to the Closure
+     *                    if it returns true, the sleep continues
+     */
+    public static void sleep(Object object, long milliseconds, Closure onInterrupt){
+	sleepImpl(object, milliseconds, onInterrupt);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/DefaultMethodKey.java b/groovy/src/main/org/codehaus/groovy/runtime/DefaultMethodKey.java
new file mode 100644
index 0000000..370e36e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/DefaultMethodKey.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+/**
+ * A default implementation of MethodKey
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DefaultMethodKey extends MethodKey{
+
+    private final Class[] parameterTypes;
+
+    public DefaultMethodKey(Class sender, String name, Class[] parameterTypes, boolean isCallToSuper) {
+        super(sender, name,isCallToSuper);
+        this.parameterTypes = parameterTypes;
+    }
+
+    public int getParameterCount() {
+        return parameterTypes.length;
+    }
+
+    public Class getParameterType(int index) {
+        Class c = parameterTypes[index];
+        if (c==null) return Object.class;
+        return c;
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/FlushingStreamWriter.java b/groovy/src/main/org/codehaus/groovy/runtime/FlushingStreamWriter.java
new file mode 100644
index 0000000..801a234
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/FlushingStreamWriter.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import java.io.OutputStreamWriter;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * Stream writer which flushes after each write operation.
+ *
+ * @author Guillaume Laforge
+ */
+public class FlushingStreamWriter extends OutputStreamWriter {
+
+    public FlushingStreamWriter(OutputStream out) {
+        super(out);
+    }
+
+    public void write(char[] cbuf, int off, int len) throws IOException {
+        super.write(cbuf, off, len);
+        flush();
+    }
+
+    public void write(int c) throws IOException {
+        super.write(c);
+        flush();
+    }
+
+    public void write(String str, int off, int len) throws IOException {
+        super.write(str, off, len);
+        flush();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/GStringImpl.java b/groovy/src/main/org/codehaus/groovy/runtime/GStringImpl.java
new file mode 100644
index 0000000..3a7a0fc
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/GStringImpl.java
@@ -0,0 +1,64 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.runtime;

+

+import groovy.lang.GString;

+

+/**

+ * Default implementation of a GString used by the compiler. A GString

+ * consist of a list of values and strings which can be combined to 

+ * create a new String.

+ * 

+ * @see groovy.lang.GString

+ *  

+ * @author Jochen Theodorou

+ */

+public class GStringImpl extends GString {

+  private String[] strings;

+	

+   /**

+    * Create a new GString with values and strings.

+    * <p>

+    * Each value is prefixed by a string, after the last value 

+    * an additional String might be used. This means 

+    * <code>strings.length==values.length  || strings.length==values.length+1</code>.

+    * </p><p>

+    * <b>NOTE:</b> The lengths are <b>not</b> checked. Using different lengths might result 

+    * in unpredictable behaviour.

+    * </p>

+    *   

+    * @param values the value parts

+    * @param strings the string parts

+    */

+	public GStringImpl(Object[] values, String[] strings) {

+		super(values);

+		this.strings = strings;

+	}

+	

+	/**

+	 * Get the strings of this GString.

+	 * <p>

+	 * This methods returns the same array as used in the constructor. Changing

+	 * the values will result in changes of the GString. It is not recommended 

+	 * to do so.

+	 * </p>

+	 */

+	public String[] getStrings() {

+		return strings;

+	}

+	

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/GeneratedClosure.java b/groovy/src/main/org/codehaus/groovy/runtime/GeneratedClosure.java
new file mode 100644
index 0000000..c6263aa
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/GeneratedClosure.java
@@ -0,0 +1,26 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.runtime;

+

+/**

+ * Marker interface to identify closures generated by the groovy compiler.

+ * For internal use only!

+ * 

+ * @author Jochen Theodorou

+ * @since 1.1

+ * @see org.codehaus.groovy.runtime.metaclass.ClosureMetaClass

+ */

+public interface GeneratedClosure {}

diff --git a/groovy/src/main/org/codehaus/groovy/runtime/GroovyCategorySupport.java b/groovy/src/main/org/codehaus/groovy/runtime/GroovyCategorySupport.java
new file mode 100644
index 0000000..bc5af9a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/GroovyCategorySupport.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+import groovy.lang.MetaMethod;
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.CachedMethod;
+import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.*;
+
+/**
+ * @author sam
+ * @author Paul King
+ */
+public class GroovyCategorySupport {
+
+    private static int categoriesInUse = 0;
+
+    /**
+     * This method is used to pull all the new methods out of the local thread context with a particular name.
+     *
+     * @param categorizedClass a class subject to the category methods in the thread context
+     * @param name the method name of interest
+     * @return the list of methods
+     */
+    public static List getCategoryMethods(Class categorizedClass, String name) {
+        Map properties = getProperties();
+        List methodList = new ArrayList();
+        for (Iterator i = properties.keySet().iterator(); i.hasNext(); ) {
+            Class current = (Class) i.next();
+            if (current.isAssignableFrom(categorizedClass)) {
+                Map metaMethodsMap = (Map) properties.get(current);
+                List newMethodList = (List) metaMethodsMap.get(name);
+                if (newMethodList != null) {
+                    methodList.addAll(newMethodList);
+                }
+            }
+        }
+        if (methodList.isEmpty()) return null;
+        return methodList;
+    }
+
+    /**
+     * This method is used to pull all the new methods out of the local thread context.
+     *
+     * @param categorizedClass a class subject to the category methods in the thread context
+     * @return the list of methods
+     */
+    public static List getCategoryMethods(Class categorizedClass) {
+        Map properties = getProperties();
+        List methodList = new ArrayList();
+        for (Iterator i = properties.keySet().iterator(); i.hasNext(); ) {
+            Class current = (Class) i.next();
+            if (current.isAssignableFrom(categorizedClass)) {
+                Map metaMethodsMap = (Map) properties.get(current);
+                Collection collection = metaMethodsMap.values();
+                for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
+                    List newMethodList = (List) iterator.next();
+                    if (newMethodList != null) {
+                        methodList.addAll(newMethodList);
+                    }
+                }
+            }
+        }
+        if (methodList.isEmpty()) return null;
+        return methodList;
+    }
+
+    public static Object getClosestMatchingCategoryMethod(Class sender, MetaMethod orig, MetaMethod element) {
+        // for now just compare MetaMethods
+        if (orig instanceof CategoryMethod && element instanceof CategoryMethod) {
+            CategoryMethod o = (CategoryMethod) orig;
+            CategoryMethod e = (CategoryMethod) element;
+            if (o.compareTo(e) < 0) {
+                return orig;
+            }
+        }
+        return element;
+    }
+    
+    private static class CategoryMethod extends NewInstanceMetaMethod implements Comparable {
+        private final Class metaClass;
+
+        public CategoryMethod(CachedMethod metaMethod, Class metaClass) {
+            super(metaMethod);
+            this.metaClass = metaClass;
+        }
+
+        public boolean isCacheable() { return false; }
+
+        /**
+         * Sort by most specific to least specific.
+         *
+         * @param o the object to compare against
+         */
+        public int compareTo(Object o) {
+            CategoryMethod thatMethod = (CategoryMethod) o;
+            Class thisClass = metaClass;
+            Class thatClass = thatMethod.metaClass;
+            if (thisClass == thatClass) return 0;
+            Class loop = thisClass;
+            while(loop != null && loop != Object.class) {
+                loop = thisClass.getSuperclass();
+                if (loop == thatClass) {
+                    return -1;
+                }
+            }
+            loop = thatClass;
+            while (loop != null && loop != Object.class) {
+                loop = thatClass.getSuperclass();
+                if (loop == thisClass) {
+                    return 1;
+                }
+            }
+            return 0;
+        }
+    }
+
+    /**
+     * Create a scope based on given categoryClass and invoke closure within that scope.
+     *
+     * @param categoryClass the class containing category methods
+	 * @param closure the closure during which to make the category class methods available
+     * @return the value returned from the closure
+	 */
+	public static Object use(Class categoryClass, Closure closure) {
+		newScope();
+		try {
+			use(categoryClass);
+			return closure.call();
+		} finally {
+			endScope();
+		}
+	}
+
+    /**
+     * Create a scope based on given categoryClasses and invoke closure within that scope.
+     *
+     * @param categoryClasses the list of classes containing category methods
+     * @param closure the closure during which to make the category class methods available
+     * @return the value returned from the closure
+     */
+    public static Object use(List categoryClasses, Closure closure) {
+        newScope();
+        try {
+            for (Iterator i = categoryClasses.iterator(); i.hasNext(); ) {
+                Class clazz = (Class) i.next();
+                use(clazz);
+            }
+            return closure.call();
+        } finally {
+            endScope();
+        }
+    }
+
+    /**
+     * Delegated to from the global use(CategoryClass) method.  It scans the Category class for static methods
+     * that take 1 or more parameters.  The first parameter is the class you are adding the category method to,
+     * additional parameters are those parameters needed by that method.  A use statement cannot be undone and
+     * is valid only for the current thread.
+     *
+     * @param categoryClass the class containing category methods
+     */
+    private static void use(Class categoryClass) {
+        Map properties = getProperties();
+        List stack = (List) LOCAL.get();
+        LinkedList clonedLists = new LinkedList();
+        
+        Method[] methods = categoryClass.getMethods();
+        for (int i = 0; i < methods.length; i++) {
+            Method method = methods[i];
+            if (Modifier.isStatic(method.getModifiers())) {
+                final CachedMethod cachedMethod = CachedMethod.find(method);
+                CachedClass[] paramTypes = cachedMethod.getParameterTypes();
+                if (paramTypes.length > 0) {
+                    CachedClass metaClass = paramTypes[0];
+                    Map metaMethodsMap = getMetaMethods(properties, metaClass.getCachedClass());
+                    List methodList = getMethodList(metaMethodsMap, method.getName());
+                    MetaMethod mmethod = new CategoryMethod(cachedMethod, metaClass.getCachedClass());
+                    methodList.add(mmethod);
+                    Collections.sort(methodList);
+                }
+            }
+        }
+    }
+
+    private static final ThreadLocal LOCAL = new ThreadLocal() {
+        protected Object initialValue() {
+        		List stack = new ArrayList();
+        		stack.add(Collections.EMPTY_MAP);
+        		return stack;
+        	}
+    };
+
+    private static synchronized void newScope() {
+        categoriesInUse++;
+        List stack = (List) LOCAL.get();
+        Map properties = (Map) stack.get(stack.size() - 1);
+        Map newMap = new WeakHashMap(properties.size());
+        for (Iterator iterator = properties.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            newMap.put(entry.getKey(), copyMapOfList((Map) entry.getValue()));
+        }        
+        stack.add(newMap);
+    }
+
+    private static synchronized void endScope() {
+        List stack = (List) LOCAL.get();
+    	stack.remove(stack.size() - 1);
+        categoriesInUse--;
+    }
+    
+    private static Map copyMapOfList(Map m) {
+        Map ret = new HashMap(m.size());
+        for (Iterator iterator = m.entrySet().iterator(); iterator.hasNext();) {
+            Map.Entry entry = (Map.Entry) iterator.next();
+            List l = (List) entry.getValue();
+            l = new ArrayList(l);
+            ret.put(entry.getKey(), l);
+        }
+        return ret;
+    }
+    
+    private static Map getProperties() {
+        List stack = (List) LOCAL.get();
+        return (Map) stack.get(stack.size() - 1);
+    }
+
+    public static boolean hasCategoryInAnyThread() {
+        return categoriesInUse!=0;
+    }
+
+    private static List getMethodList(Map metaMethodsMap, String name) {
+        List methodList = (List) metaMethodsMap.get(name);
+        if (methodList == null) {
+            methodList = new ArrayList(1);
+            metaMethodsMap.put(name, methodList);
+        }
+        return methodList;
+    }
+
+    private static Map getMetaMethods(Map properties, Class metaClass) {
+        Map metaMethodsMap = (Map) properties.get(metaClass);
+        if (metaMethodsMap == null) {
+            metaMethodsMap = new HashMap();
+            properties.put(metaClass, metaMethodsMap);
+        }
+        return metaMethodsMap;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/Invoker.java b/groovy/src/main/org/codehaus/groovy/runtime/Invoker.java
new file mode 100644
index 0000000..27e2f16
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/Invoker.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.*;
+import org.codehaus.groovy.runtime.wrappers.PojoWrapper;
+
+/**
+ * A helper class to invoke methods or extract properties on arbitrary Java objects dynamically
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class Invoker {
+
+    protected static final Object[] EMPTY_ARGUMENTS = {
+    };
+    protected static final Class[] EMPTY_TYPES = {
+    };
+
+    public MetaClassRegistry getMetaRegistry() {
+        return metaRegistry;
+    }
+
+    private final MetaClassRegistry metaRegistry = GroovySystem.getMetaClassRegistry();
+
+    public MetaClass getMetaClass(Object object) {
+        return metaRegistry.getMetaClass(object.getClass());
+    }
+
+    /**
+     * Invokes the given method on the object.
+     */
+    public Object invokeMethod(Object object, String methodName, Object arguments) {
+        /*
+        System
+            .out
+            .println(
+                "Invoker - Invoking method on object: "
+                    + object
+                    + " method: "
+                    + methodName
+                    + " arguments: "
+                    + InvokerHelper.toString(arguments));
+                    */
+
+        if (object == null) {
+            object = NullObject.getNullObject();
+            //throw new NullPointerException("Cannot invoke method " + methodName + "() on null object");
+        }
+
+        // if the object is a Class, call a static method from that class
+        if (object instanceof Class) {
+            Class theClass = (Class) object;
+            MetaClass metaClass = metaRegistry.getMetaClass(theClass);
+            return metaClass.invokeStaticMethod(object, methodName, asArray(arguments));
+        }
+        else // it's an instance
+        {
+            // if it's not an object implementing GroovyObject (thus not builder, nor a closure)
+            if (!(object instanceof GroovyObject)) {
+                return invokePojoMethod(object, methodName, arguments);
+            }
+            // it's an object implementing GroovyObject
+            else {
+                return invokePogoMethod(object, methodName, arguments);
+            }
+        }
+    }
+
+    private Object invokePojoMethod(Object object, String methodName, Object arguments) {
+        Class theClass = object.getClass();
+        MetaClass metaClass = metaRegistry.getMetaClass(theClass);
+        return metaClass.invokeMethod(object, methodName, asArray(arguments));
+    }
+
+    private Object invokePogoMethod(Object object, String methodName, Object arguments) {
+        GroovyObject groovy = (GroovyObject) object;
+        boolean intercepting = groovy instanceof GroovyInterceptable;
+        try {
+            // if it's a pure interceptable object (even intercepting toString(), clone(), ...)
+            if (intercepting) {
+                return groovy.invokeMethod(methodName, asUnwrappedArray(arguments));
+            }
+            //else try a statically typed method or a GDK method
+            return groovy.getMetaClass().invokeMethod(object, methodName, asArray(arguments));
+        } catch (MissingMethodException e) {
+            if (!intercepting && e.getMethod().equals(methodName) && object.getClass() == e.getType()) {
+                // in case there's nothing else, invoke the object's own invokeMethod()
+                return groovy.invokeMethod(methodName, asUnwrappedArray(arguments));
+            }
+            throw e;
+        }
+    }
+
+    public Object invokeSuperMethod(Object object, String methodName, Object arguments) {
+        if (object == null) {
+            throw new NullPointerException("Cannot invoke method " + methodName + "() on null object");
+        }
+
+        Class theClass = object.getClass();
+
+        MetaClass metaClass = metaRegistry.getMetaClass(theClass.getSuperclass());
+        return metaClass.invokeMethod(object, methodName, asArray(arguments));
+    }
+
+    public Object invokeStaticMethod(Class type, String method, Object arguments) {
+        MetaClass metaClass = metaRegistry.getMetaClass(type);
+        return metaClass.invokeStaticMethod(type, method, asArray(arguments));
+    }
+
+    public Object invokeConstructorOf(Class type, Object arguments) {
+        MetaClass metaClass = metaRegistry.getMetaClass(type);
+        return metaClass.invokeConstructor(asArray(arguments));
+    }
+
+    /**
+     * Converts the given object into an array; if its an array then just
+     * cast otherwise wrap it in an array
+     */
+    public Object[] asArray(Object arguments) {
+
+    	if (arguments == null) {
+    		return EMPTY_ARGUMENTS;
+    	}
+    	if (arguments instanceof Object[]) {
+    		return  (Object[]) arguments;
+    	}
+    	return new Object[]{arguments};
+    }
+
+    public Object[] asUnwrappedArray(Object arguments) {
+
+        Object[] args = asArray(arguments);
+
+        for (int i=0; i<args.length; i++) {
+            if (args[i] instanceof PojoWrapper) {
+                args[i] = ((PojoWrapper)args[i]).unwrap();
+            }
+        }
+
+        return args;
+    }
+
+    /**
+     * Looks up the given property of the given object
+     */
+    public Object getProperty(Object object, String property) {
+        if (object == null) {
+            throw new NullPointerException("Cannot get property: " + property + " on null object");
+        }
+        if (object instanceof GroovyObject) {
+            GroovyObject pogo = (GroovyObject) object;
+            return pogo.getProperty(property);
+        }
+        if (object instanceof Class) {
+            Class c = (Class) object;
+            return metaRegistry.getMetaClass(c).getProperty(object, property);
+        }
+        return metaRegistry.getMetaClass(object.getClass()).getProperty(object, property);
+    }
+
+    /**
+     * Sets the property on the given object
+     */
+    public void setProperty(Object object, String property, Object newValue) {
+        if (object == null) {
+            throw new GroovyRuntimeException("Cannot set property on null object");
+        }
+        if (object instanceof GroovyObject) {
+            GroovyObject pogo = (GroovyObject) object;
+            pogo.setProperty(property, newValue);
+        }
+        else {
+            if (object instanceof Class)
+                metaRegistry.getMetaClass((Class) object).setProperty((Class) object, property, newValue);
+            else
+                metaRegistry.getMetaClass(object.getClass()).setProperty(object, property, newValue);
+        }
+    }
+
+    /**
+     * Looks up the given attribute (field) on the given object
+     */
+    public Object getAttribute(Object object, String attribute) {
+        if (object == null) {
+            throw new NullPointerException("Cannot get attribute: " + attribute + " on null object");
+
+            /**
+             } else if (object instanceof GroovyObject) {
+             GroovyObject pogo = (GroovyObject) object;
+             return pogo.getAttribute(attribute);
+             } else if (object instanceof Map) {
+             Map map = (Map) object;
+             return map.get(attribute);
+             */
+        }
+        else {
+            if (object instanceof Class) {
+                return metaRegistry.getMetaClass((Class) object).getAttribute(object, attribute);
+            } else if (object instanceof GroovyObject) {
+                return ((GroovyObject)object).getMetaClass().getAttribute(object, attribute);
+            } else {
+                return metaRegistry.getMetaClass(object.getClass()).getAttribute(object, attribute);
+            }
+	}
+    }
+
+    /**
+     * Sets the given attribute (field) on the given object
+     */
+    public void setAttribute(Object object, String attribute, Object newValue) {
+        if (object == null) {
+            throw new GroovyRuntimeException("Cannot set attribute on null object");
+            /*
+        } else if (object instanceof GroovyObject) {
+            GroovyObject pogo = (GroovyObject) object;
+            pogo.setProperty(attribute, newValue);
+        } else if (object instanceof Map) {
+            Map map = (Map) object;
+            map.put(attribute, newValue);
+            */
+        }
+        else {
+            if (object instanceof Class) {
+                metaRegistry.getMetaClass((Class) object).setAttribute(object, attribute, newValue);
+            } else if (object instanceof GroovyObject) {
+                ((GroovyObject)object).getMetaClass().setAttribute(object, attribute, newValue);
+            } else {
+                metaRegistry.getMetaClass(object.getClass()).setAttribute(object, attribute, newValue);
+            }
+	}
+    }
+
+    /**
+     * Returns the method pointer for the given object name
+     */
+    public Closure getMethodPointer(Object object, String methodName) {
+        if (object == null) {
+            throw new NullPointerException("Cannot access method pointer for '" + methodName + "' on null object");
+        }
+        return MetaClassHelper.getMethodPointer(object, methodName);
+    }
+
+    public void removeMetaClass(Class clazz) {
+        metaRegistry.removeMetaClass(clazz);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/InvokerHelper.java b/groovy/src/main/org/codehaus/groovy/runtime/InvokerHelper.java
new file mode 100644
index 0000000..6e4b4de
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/InvokerHelper.java
@@ -0,0 +1,687 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.*;
+import groovy.xml.dom.DOMUtil;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.runtime.typehandling.IntegerCache;
+import org.w3c.dom.Element;
+
+import java.beans.Introspector;
+import java.io.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A static helper class to make bytecode generation easier and act as a facade over the Invoker
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class InvokerHelper {
+    public static final Object[] EMPTY_ARGS = {
+    };
+
+    private static final Object[] EMPTY_MAIN_ARGS = new Object[]{new String[0]};
+
+    private static final Invoker SINGLETON = new Invoker();
+    private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+
+
+    public static MetaClass getMetaClass(Object object) {
+        return getInstance().getMetaClass(object);
+    }
+
+    public static void removeClass(Class clazz) {
+        getInstance().removeMetaClass(clazz);
+        Introspector.flushFromCaches(clazz);
+    }
+
+    public static Invoker getInstance() {
+        return SINGLETON;
+    }
+
+    public static Object invokeNoArgumentsMethod(Object object, String methodName) {
+        return getInstance().invokeMethod(object, methodName, EMPTY_ARGS);
+    }
+
+    public static Object invokeMethod(Object object, String methodName, Object arguments) {
+        return getInstance().invokeMethod(object, methodName, arguments);
+    }
+
+    public static Object invokeSuperMethod(Object object, String methodName, Object arguments) {
+        return getInstance().invokeSuperMethod(object, methodName, arguments);
+    }
+
+    public static Object invokeMethodSafe(Object object, String methodName, Object arguments) {
+        if (object != null) {
+            return getInstance().invokeMethod(object, methodName, arguments);
+        }
+        return null;
+    }
+
+    public static Object invokeStaticMethod(Class type, String methodName, Object arguments) {
+        return getInstance().invokeStaticMethod(type, methodName, arguments);
+    }
+
+    public static Object invokeStaticMethod(String klass, String methodName, Object arguments) throws ClassNotFoundException {
+        Class type = Class.forName(klass);
+        return getInstance().invokeStaticMethod(type, methodName, arguments);
+    }
+
+
+    public static Object invokeStaticNoArgumentsMethod(Class type, String methodName) {
+        return getInstance().invokeStaticMethod(type, methodName, EMPTY_ARGS);
+    }
+
+    public static Object invokeConstructorOf(Class type, Object arguments) {
+        return getInstance().invokeConstructorOf(type, arguments);
+    }
+
+    public static Object invokeConstructorOf(String klass, Object arguments) throws ClassNotFoundException {
+        Class type = Class.forName(klass);
+        return getInstance().invokeConstructorOf(type, arguments);
+    }
+
+    public static Object invokeNoArgumentsConstructorOf(Class type) {
+        return getInstance().invokeConstructorOf(type, EMPTY_ARGS);
+    }
+
+    public static Object invokeClosure(Object closure, Object arguments) {
+        return getInstance().invokeMethod(closure, "doCall", arguments);
+    }
+
+    public static List asList(Object value) {
+        if (value == null) {
+            return Collections.EMPTY_LIST;
+        }
+        if (value instanceof List) {
+            return (List) value;
+        }
+        if (value.getClass().isArray()) {
+            return Arrays.asList((Object[]) value);
+        }
+        if (value instanceof Enumeration) {
+            List answer = new ArrayList();
+            for (Enumeration e = (Enumeration) value; e.hasMoreElements();) {
+                answer.add(e.nextElement());
+            }
+            return answer;
+        }
+        // lets assume its a collection of 1
+        return Collections.singletonList(value);
+    }
+
+    public static String toString(Object arguments) {
+        if (arguments instanceof Object[])
+            return toArrayString((Object[]) arguments);
+        if (arguments instanceof Collection)
+            return toListString((Collection) arguments);
+        if (arguments instanceof Map)
+            return toMapString((Map) arguments);
+        if (arguments instanceof Collection)
+            return format(arguments, true);
+        return format(arguments, false);
+    }
+
+    public static String inspect(Object self) {
+        return format(self, true);
+    }
+
+    public static Object getAttribute(Object object, String attribute) {
+        return getInstance().getAttribute(object, attribute);
+    }
+
+    public static void setAttribute(Object object, String attribute, Object newValue) {
+        getInstance().setAttribute(object, attribute, newValue);
+    }
+
+    public static Object getProperty(Object object, String property) {
+        return getInstance().getProperty(object, property);
+    }
+
+    public static Object getPropertySafe(Object object, String property) {
+        if (object != null) {
+            return getInstance().getProperty(object, property);
+        }
+        return null;
+    }
+
+    public static void setProperty(Object object, String property, Object newValue) {
+        getInstance().setProperty(object, property, newValue);
+    }
+
+    /**
+     * This is so we don't have to reorder the stack when we call this method.
+     * At some point a better name might be in order.
+     */
+    public static void setProperty2(Object newValue, Object object, String property) {
+        getInstance().setProperty(object, property, newValue);
+    }
+
+
+    /**
+     * This is so we don't have to reorder the stack when we call this method.
+     * At some point a better name might be in order.
+     */
+    public static void setGroovyObjectProperty(Object newValue, GroovyObject object, String property) {
+        object.setProperty(property, newValue);
+    }
+
+    public static Object getGroovyObjectProperty(GroovyObject object, String property) {
+        return object.getProperty(property);
+    }
+
+
+    /**
+     * This is so we don't have to reorder the stack when we call this method.
+     * At some point a better name might be in order.
+     */
+    public static void setPropertySafe2(Object newValue, Object object, String property) {
+        if (object != null) {
+            setProperty2(newValue, object, property);
+        }
+    }
+
+    /**
+     * Returns the method pointer for the given object name
+     */
+    public static Closure getMethodPointer(Object object, String methodName) {
+        return getInstance().getMethodPointer(object, methodName);
+    }
+
+    public static Object unaryMinus(Object value) {
+        if (value instanceof Integer) {
+            Integer number = (Integer) value;
+            return IntegerCache.integerValue(-number.intValue());
+        }
+        if (value instanceof Long) {
+            Long number = (Long) value;
+            return new Long(-number.longValue());
+        }
+        if (value instanceof BigInteger) {
+            return ((BigInteger) value).negate();
+        }
+        if (value instanceof BigDecimal) {
+            return ((BigDecimal) value).negate();
+        }
+        if (value instanceof Double) {
+            Double number = (Double) value;
+            return new Double(-number.doubleValue());
+        }
+        if (value instanceof Float) {
+            Float number = (Float) value;
+            return new Float(-number.floatValue());
+        }
+        if (value instanceof ArrayList) {
+            // value is an list.
+            List newlist = new ArrayList();
+            Iterator it = ((ArrayList) value).iterator();
+            for (; it.hasNext();) {
+                newlist.add(unaryMinus(it.next()));
+            }
+            return newlist;
+        }
+        return invokeMethod(value, "negative", EMPTY_OBJECT_ARRAY);
+    }
+
+    public static Object unaryPlus(Object value) {
+        if (value instanceof Integer ||
+                value instanceof Long ||
+                value instanceof BigInteger ||
+                value instanceof BigDecimal ||
+                value instanceof Double ||
+                value instanceof Float) {
+            return value;
+        }
+        if (value instanceof ArrayList) {
+            // value is an list.
+            List newlist = new ArrayList();
+            Iterator it = ((ArrayList) value).iterator();
+            for (; it.hasNext();) {
+                newlist.add(unaryPlus(it.next()));
+            }
+            return newlist;
+        }
+        return invokeMethod(value, "positive", EMPTY_OBJECT_ARRAY);
+    }
+
+    /**
+     * Find the right hand regex within the left hand string and return a matcher.
+     *
+     * @param left  string to compare
+     * @param right regular expression to compare the string to
+     */
+    public static Matcher findRegex(Object left, Object right) {
+        String stringToCompare;
+        if (left instanceof String) {
+            stringToCompare = (String) left;
+        } else {
+            stringToCompare = toString(left);
+        }
+        String regexToCompareTo;
+        if (right instanceof String) {
+            regexToCompareTo = (String) right;
+        } else if (right instanceof Pattern) {
+            Pattern pattern = (Pattern) right;
+            return pattern.matcher(stringToCompare);
+        } else {
+            regexToCompareTo = toString(right);
+        }
+        Matcher matcher = Pattern.compile(regexToCompareTo).matcher(stringToCompare);
+        return matcher;
+    }
+
+
+    /**
+     * Find the right hand regex within the left hand string and return a matcher.
+     *
+     * @param left  string to compare
+     * @param right regular expression to compare the string to
+     */
+    public static boolean matchRegex(Object left, Object right) {
+        Pattern pattern;
+        if (right instanceof Pattern) {
+            pattern = (Pattern) right;
+        } else {
+            pattern = Pattern.compile(toString(right));
+        }
+        String stringToCompare = toString(left);
+        Matcher matcher = pattern.matcher(stringToCompare);
+        RegexSupport.setLastMatcher(matcher);
+        return matcher.matches();
+    }
+
+    public static Tuple createTuple(Object[] array) {
+        return new Tuple(array);
+    }
+
+    public static SpreadMap spreadMap(Object value) {
+        if (value instanceof Map) {
+            Object[] values = new Object[((Map) value).keySet().size() * 2];
+            int index = 0;
+            Iterator it = ((Map) value).keySet().iterator();
+            for (; it.hasNext();) {
+                Object key = it.next();
+                values[index++] = key;
+                values[index++] = ((Map) value).get(key);
+            }
+            return new SpreadMap(values);
+        }
+        throw new SpreadMapEvaluatingException("Cannot spread the map " + value.getClass().getName() + ", value " + value);
+    }
+
+    public static List createList(Object[] values) {
+        List answer = new ArrayList(values.length);
+        answer.addAll(Arrays.asList(values));
+        return answer;
+    }
+
+    public static Map createMap(Object[] values) {
+        Map answer = new LinkedHashMap(values.length / 2);
+        int i = 0;
+        while (i < values.length - 1) {
+            if ((values[i] instanceof SpreadMap) && (values[i + 1] instanceof Map)) {
+                Map smap = (Map) values[i + 1];
+                Iterator iter = smap.keySet().iterator();
+                for (; iter.hasNext();) {
+                    Object key = iter.next();
+                    answer.put(key, smap.get(key));
+                }
+                i += 2;
+            } else {
+                answer.put(values[i++], values[i++]);
+            }
+        }
+        return answer;
+    }
+
+    public static void assertFailed(Object expression, Object message) {
+        if (message == null || "".equals(message)) {
+            throw new AssertionError("Expression: " + expression);
+        }
+        throw new AssertionError(String.valueOf(message) + ". Expression: " + expression);
+    }
+
+    public static Object runScript(Class scriptClass, String[] args) {
+        Binding context = new Binding(args);
+        Script script = createScript(scriptClass, context);
+        return invokeMethod(script, "run", EMPTY_ARGS);
+    }
+
+    public static Script createScript(Class scriptClass, Binding context) {
+        Script script = null;
+        // for empty scripts
+        if (scriptClass == null) {
+            script = new Script() {
+                public Object run() {
+                    return null;
+                }
+            };
+        } else {
+            try {
+                final GroovyObject object = (GroovyObject) scriptClass
+                        .newInstance();
+                if (object instanceof Script) {
+                    script = (Script) object;
+                } else {
+                    // it could just be a class, so lets wrap it in a Script
+                    // wrapper
+                    // though the bindings will be ignored
+                    script = new Script() {
+                        public Object run() {
+                            object.invokeMethod("main", EMPTY_MAIN_ARGS);
+                            return null;
+                        }
+                    };
+                    setProperties(object, context.getVariables());
+                }
+            } catch (Exception e) {
+                throw new GroovyRuntimeException(
+                        "Failed to create Script instance for class: "
+                                + scriptClass + ". Reason: " + e, e);
+            }
+        }
+        script.setBinding(context);
+        return script;
+    }
+
+    /**
+     * Sets the properties on the given object
+     */
+    public static void setProperties(Object object, Map map) {
+        MetaClass mc = getInstance().getMetaClass(object);
+        for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
+            Map.Entry entry = (Map.Entry) iter.next();
+            String key = entry.getKey().toString();
+
+            Object value = entry.getValue();
+            try {
+                mc.setProperty(object, key, value);
+            } catch (MissingPropertyException mpe) {
+                // Ignore
+            }
+        }
+    }
+
+    public static String getVersion() {
+        String version = null;
+        Package p = Package.getPackage("groovy.lang");
+        if (p != null) {
+            version = p.getImplementationVersion();
+        }
+        if (version == null) {
+            version = "";
+        }
+        return version;
+    }
+
+    /**
+     * Writes the given object to the given stream
+     */
+    public static void write(Writer out, Object object) throws IOException {
+        if (object instanceof String) {
+            out.write((String) object);
+        } else if (object instanceof Object[]) {
+            out.write(toArrayString((Object[]) object));
+        } else if (object instanceof Map) {
+            out.write(toMapString((Map) object));
+        } else if (object instanceof Collection) {
+            out.write(toListString((Collection) object));
+        } else if (object instanceof Writable) {
+            Writable writable = (Writable) object;
+            writable.writeTo(out);
+        } else if (object instanceof InputStream || object instanceof Reader) {
+            // Copy stream to stream
+            Reader reader;
+            if (object instanceof InputStream) {
+                reader = new InputStreamReader((InputStream) object);
+            } else {
+                reader = (Reader) object;
+            }
+            char[] chars = new char[8192];
+            int i;
+            while ((i = reader.read(chars)) != -1) {
+                out.write(chars, 0, i);
+            }
+            reader.close();
+        } else {
+            out.write(toString(object));
+        }
+    }
+
+    public static Iterator asIterator(Object o) {
+        return (Iterator) invokeMethod(o, "iterator", EMPTY_ARGS);
+    }
+
+    protected static String format(Object arguments, boolean verbose) {
+        if (arguments == null) {
+            final NullObject nullObject = NullObject.getNullObject();
+            return (String) nullObject.getMetaClass().invokeMethod(nullObject, "toString", EMPTY_ARGS);
+        }
+        if (arguments.getClass().isArray()) {
+            return format(DefaultTypeTransformation.asCollection(arguments), verbose);
+        }
+        if (arguments instanceof Range) {
+            Range range = (Range) arguments;
+            if (verbose) {
+                return range.inspect();
+            } else {
+                return range.toString();
+            }
+        }
+        if (arguments instanceof List) {
+            List list = (List) arguments;
+            StringBuffer buffer = new StringBuffer("[");
+            boolean first = true;
+            for (Iterator iter = list.iterator(); iter.hasNext();) {
+                if (first) {
+                    first = false;
+                } else {
+                    buffer.append(", ");
+                }
+                buffer.append(format(iter.next(), verbose));
+            }
+            buffer.append("]");
+            return buffer.toString();
+        }
+        if (arguments instanceof Map) {
+            Map map = (Map) arguments;
+            if (map.isEmpty()) {
+                return "[:]";
+            }
+            StringBuffer buffer = new StringBuffer("[");
+            boolean first = true;
+            for (Iterator iter = map.entrySet().iterator(); iter.hasNext();) {
+                if (first) {
+                    first = false;
+                } else {
+                    buffer.append(", ");
+                }
+                Map.Entry entry = (Map.Entry) iter.next();
+                buffer.append(format(entry.getKey(), verbose));
+                buffer.append(":");
+                if (entry.getValue() == map) {
+                    buffer.append("this Map_");
+                } else {
+                    buffer.append(format(entry.getValue(), verbose));
+                }
+            }
+            buffer.append("]");
+            return buffer.toString();
+        }
+        if (arguments instanceof Element) {
+            return DOMUtil.serialize((Element) arguments);
+        }
+        if (arguments instanceof String) {
+            if (verbose) {
+                String arg = ((String) arguments).replaceAll("\\n", "\\\\n");    // line feed
+                arg = arg.replaceAll("\\r", "\\\\r");      // carriage return
+                arg = arg.replaceAll("\\t", "\\\\t");      // tab
+                arg = arg.replaceAll("\\f", "\\\\f");      // form feed
+                arg = arg.replaceAll("\\\"", "\\\\\"");    // double quotation amrk
+                arg = arg.replaceAll("\\\\", "\\\\");      // back slash
+                return "\"" + arg + "\"";
+            } else {
+                return (String) arguments;
+            }
+        }
+        return arguments.toString();
+    }
+
+
+    /**
+     * A helper method to format the arguments types as a comma-separated list
+     */
+    public static String toTypeString(Object[] arguments) {
+        if (arguments == null) {
+            return "null";
+        }
+        StringBuffer argBuf = new StringBuffer();
+        for (int i = 0; i < arguments.length; i++) {
+            if (i > 0) {
+                argBuf.append(", ");
+            }
+            argBuf.append(arguments[i] != null ? arguments[i].getClass().getName() : "null");
+        }
+        return argBuf.toString();
+    }
+
+    /**
+     * A helper method to return the string representation of a map with bracket boundaries "[" and "]".
+     */
+    public static String toMapString(Map arg) {
+        return format(arg, true);
+        /*if (arg == null) {
+            return "null";
+        }
+        if (arg.isEmpty()) {
+            return "[:]";
+        }
+        String sbdry = "[";
+        String ebdry = "]";
+        StringBuffer buffer = new StringBuffer(sbdry);
+        boolean first = true;
+        for (Iterator iter = arg.entrySet().iterator(); iter.hasNext();) {
+            if (first)
+                first = false;
+            else
+                buffer.append(", ");
+            Map.Entry entry = (Map.Entry) iter.next();
+            buffer.append(format(entry.getKey(), true));
+            buffer.append(":");
+            buffer.append(format(entry.getValue(), true));
+        }
+        buffer.append(ebdry);
+        return buffer.toString();*/
+    }
+
+    /**
+     * A helper method to return the string representation of a list with bracket boundaries "[" and "]".
+     */
+    public static String toListString(Collection arg) {
+        if (arg == null) {
+            return "null";
+        }
+        if (arg.isEmpty()) {
+            return "[]";
+        }
+        String sbdry = "[";
+        String ebdry = "]";
+        StringBuffer buffer = new StringBuffer(sbdry);
+        boolean first = true;
+        for (Iterator iter = arg.iterator(); iter.hasNext();) {
+            if (first)
+                first = false;
+            else
+                buffer.append(", ");
+            Object elem = iter.next();
+            buffer.append(format(elem, true));
+        }
+        buffer.append(ebdry);
+        return buffer.toString();
+    }
+
+    /**
+     * A helper method to return the string representation of an arrray of objects
+     * with brace boundaries "{" and "}".
+     */
+    public static String toArrayString(Object[] arguments) {
+        if (arguments == null) {
+            return "null";
+        }
+        String sbdry = "{";
+        String ebdry = "}";
+        StringBuffer argBuf = new StringBuffer(sbdry);
+        for (int i = 0; i < arguments.length; i++) {
+            if (i > 0) {
+                argBuf.append(", ");
+            }
+            argBuf.append(format(arguments[i], true));
+        }
+        argBuf.append(ebdry);
+        return argBuf.toString();
+    }
+
+    public static List createRange(Object from, Object to, boolean inclusive) {
+        try {
+            return ScriptBytecodeAdapter.createRange(from, to, inclusive);
+        } catch (RuntimeException re) {
+            throw re;
+        } catch (Error e) {
+            throw e;
+        } catch (Throwable t) {
+            throw new RuntimeException(t);
+        }
+    }
+
+    public static Object bitwiseNegate(Object value) {
+        if (value instanceof Integer) {
+            Integer number = (Integer) value;
+            return new Integer(~number.intValue());
+        }
+        if (value instanceof Long) {
+            Long number = (Long) value;
+            return new Long(~number.longValue());
+        }
+        if (value instanceof BigInteger) {
+            return ((BigInteger) value).not();
+        }
+        if (value instanceof String) {
+            // value is a regular expression.
+            return DefaultGroovyMethods.bitwiseNegate(value.toString());
+        }
+        if (value instanceof GString) {
+            // value is a regular expression.
+            return DefaultGroovyMethods.bitwiseNegate(value.toString());
+        }
+        if (value instanceof ArrayList) {
+            // value is an list.
+            List newlist = new ArrayList();
+            Iterator it = ((ArrayList) value).iterator();
+            for (; it.hasNext();) {
+                newlist.add(bitwiseNegate(it.next()));
+            }
+            return newlist;
+        }
+        return invokeMethod(value, "bitwiseNegate", EMPTY_OBJECT_ARRAY);
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/InvokerInvocationException.java b/groovy/src/main/org/codehaus/groovy/runtime/InvokerInvocationException.java
new file mode 100644
index 0000000..c300092
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/InvokerInvocationException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.GroovyRuntimeException;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * An exception thrown if a method is called and an exception occurred
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class InvokerInvocationException extends GroovyRuntimeException {
+
+    public InvokerInvocationException(InvocationTargetException e) {
+        super(e.getTargetException());
+    }
+
+    public InvokerInvocationException(Throwable cause) {
+        super(cause);
+    }
+
+    public String getMessage() {
+        Throwable cause = getCause();
+        return (cause==null)?"java.lang.NullPointerException":cause.toString();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/IteratorClosureAdapter.java b/groovy/src/main/org/codehaus/groovy/runtime/IteratorClosureAdapter.java
new file mode 100644
index 0000000..51dbce1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/IteratorClosureAdapter.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+import groovy.lang.MetaClass;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A closure which stores calls in a List so that method calls 
+ * can be iterated over in a 'yield' style way
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class IteratorClosureAdapter extends Closure {
+
+    private final List list = new ArrayList();
+    private MetaClass metaClass = InvokerHelper.getMetaClass(this);
+    
+    public IteratorClosureAdapter(Object delegate) {
+        super(delegate);
+    }
+
+    public MetaClass getMetaClass() {
+        return metaClass;
+    }
+
+    public void setMetaClass(MetaClass metaClass) {
+        this.metaClass = metaClass;
+    }
+    
+    public List asList() {
+        return list;
+    }
+
+    protected Object doCall(Object argument) {
+        list.add(argument);
+        return null;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/MetaClassHelper.java b/groovy/src/main/org/codehaus/groovy/runtime/MetaClassHelper.java
new file mode 100644
index 0000000..1cbdec1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/MetaClassHelper.java
@@ -0,0 +1,853 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+import groovy.lang.GString;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.MetaMethod;
+import org.codehaus.groovy.reflection.*;
+import org.codehaus.groovy.runtime.wrappers.Wrapper;
+
+import java.lang.reflect.*;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author John Wilson
+ * @author Jochen Theodorou
+ */
+public class MetaClassHelper {
+
+    public static final Object[] EMPTY_ARRAY = {};
+    public static final Class[] EMPTY_TYPE_ARRAY = {};
+    public static final Object[] ARRAY_WITH_NULL = {null};
+    protected static final Logger LOG = Logger.getLogger(MetaClassHelper.class.getName());
+    private static final int MAX_ARG_LEN = 12;
+    private static final int VARGS_SHIFT = 28;
+
+    public static boolean accessibleToConstructor(final Class at, final Constructor constructor) {
+        boolean accessible = false;
+        final int modifiers = constructor.getModifiers();
+        if (Modifier.isPublic(modifiers)) {
+            accessible = true;
+        } else if (Modifier.isPrivate(modifiers)) {
+            accessible = at.getName().equals(constructor.getName());
+        } else if (Modifier.isProtected(modifiers)) {
+            Boolean isAccessible = checkCompatiblePackages(at, constructor);
+            if (isAccessible != null) {
+                accessible = isAccessible.booleanValue();
+            } else {
+                boolean flag = false;
+                Class clazz = at;
+                while (!flag && clazz != null) {
+                    if (clazz.equals(constructor.getDeclaringClass())) {
+                        flag = true;
+                        break;
+                    }
+                    if (clazz.equals(Object.class)) {
+                        break;
+                    }
+                    clazz = clazz.getSuperclass();
+                }
+                accessible = flag;
+            }
+        } else {
+            Boolean isAccessible = checkCompatiblePackages(at, constructor);
+            if (isAccessible != null) {
+                accessible = isAccessible.booleanValue();
+            }
+        }
+        return accessible;
+    }
+
+    private static Boolean checkCompatiblePackages(Class at, Constructor constructor) {
+        if (at.getPackage() == null && constructor.getDeclaringClass().getPackage() == null) {
+            return Boolean.TRUE;
+        }
+        if (at.getPackage() == null && constructor.getDeclaringClass().getPackage() != null) {
+            return Boolean.FALSE;
+        }
+        if (at.getPackage() != null && constructor.getDeclaringClass().getPackage() == null) {
+            return Boolean.FALSE;
+        }
+        if (at.getPackage().equals(constructor.getDeclaringClass().getPackage())) {
+            return Boolean.TRUE;
+        }
+        return null;
+    }
+
+    public static Object[] asWrapperArray(Object parameters, Class componentType) {
+        Object[] ret = null;
+        if (componentType == boolean.class) {
+            boolean[] array = (boolean[]) parameters;
+            ret = new Object[array.length];
+            for (int i = 0; i < array.length; i++) {
+                ret[i] = new Boolean(array[i]);
+            }
+        } else if (componentType == char.class) {
+            char[] array = (char[]) parameters;
+            ret = new Object[array.length];
+            for (int i = 0; i < array.length; i++) {
+                ret[i] = new Character(array[i]);
+            }
+        } else if (componentType == byte.class) {
+            byte[] array = (byte[]) parameters;
+            ret = new Object[array.length];
+            for (int i = 0; i < array.length; i++) {
+                ret[i] = new Byte(array[i]);
+            }
+        } else if (componentType == int.class) {
+            int[] array = (int[]) parameters;
+            ret = new Object[array.length];
+            for (int i = 0; i < array.length; i++) {
+                ret[i] = new Integer(array[i]);
+            }
+        } else if (componentType == short.class) {
+            short[] array = (short[]) parameters;
+            ret = new Object[array.length];
+            for (int i = 0; i < array.length; i++) {
+                ret[i] = new Short(array[i]);
+            }
+        } else if (componentType == long.class) {
+            long[] array = (long[]) parameters;
+            ret = new Object[array.length];
+            for (int i = 0; i < array.length; i++) {
+                ret[i] = new Long(array[i]);
+            }
+        } else if (componentType == double.class) {
+            double[] array = (double[]) parameters;
+            ret = new Object[array.length];
+            for (int i = 0; i < array.length; i++) {
+                ret[i] = new Double(array[i]);
+            }
+        } else if (componentType == float.class) {
+            float[] array = (float[]) parameters;
+            ret = new Object[array.length];
+            for (int i = 0; i < array.length; i++) {
+                ret[i] = new Float(array[i]);
+            }
+        }
+
+        return ret;
+    }
+
+
+    /**
+     * @param list          the original list
+     * @param parameterType the resulting array type
+     * @return the constructed array
+     */
+    public static Object asPrimitiveArray(List list, Class parameterType) {
+        Class arrayType = parameterType.getComponentType();
+        Object objArray = Array.newInstance(arrayType, list.size());
+        for (int i = 0; i < list.size(); i++) {
+            Object obj = list.get(i);
+            if (arrayType.isPrimitive()) {
+                if (obj instanceof Integer) {
+                    Array.setInt(objArray, i, ((Integer) obj).intValue());
+                } else if (obj instanceof Double) {
+                    Array.setDouble(objArray, i, ((Double) obj).doubleValue());
+                } else if (obj instanceof Boolean) {
+                    Array.setBoolean(objArray, i, ((Boolean) obj).booleanValue());
+                } else if (obj instanceof Long) {
+                    Array.setLong(objArray, i, ((Long) obj).longValue());
+                } else if (obj instanceof Float) {
+                    Array.setFloat(objArray, i, ((Float) obj).floatValue());
+                } else if (obj instanceof Character) {
+                    Array.setChar(objArray, i, ((Character) obj).charValue());
+                } else if (obj instanceof Byte) {
+                    Array.setByte(objArray, i, ((Byte) obj).byteValue());
+                } else if (obj instanceof Short) {
+                    Array.setShort(objArray, i, ((Short) obj).shortValue());
+                }
+            } else {
+                Array.set(objArray, i, obj);
+            }
+        }
+        return objArray;
+    }
+
+    private static final Class[] PRIMITIVES = {
+            byte.class, Byte.class, short.class, Short.class,
+            int.class, Integer.class, long.class, Long.class,
+            BigInteger.class, float.class, Float.class,
+            double.class, Double.class, BigDecimal.class,
+            Number.class, Object.class
+    };
+    private static final int[][] PRIMITIVE_DISTANCE_TABLE = {
+            //              byte    Byte    short   Short   int     Integer     long    Long    BigInteger  float   Float   double  Double  BigDecimal, Number, Object
+            /* byte*/{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,},
+            /*Byte*/{1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,},
+            /*short*/{14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,},
+            /*Short*/{14, 15, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,},
+            /*int*/{14, 15, 12, 13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,},
+            /*Integer*/{14, 15, 12, 13, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,},
+            /*long*/{14, 15, 12, 13, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,},
+            /*Long*/{14, 15, 12, 13, 10, 11, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9,},
+            /*BigInteger*/{14, 15, 12, 13, 10, 11, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7,},
+            /*float*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 0, 1, 2, 3, 4, 5, 6,},
+            /*Float*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 1, 0, 2, 3, 4, 5, 6,},
+            /*double*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 0, 1, 2, 3, 4,},
+            /*Double*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 1, 0, 2, 3, 4,},
+            /*BigDecimal*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 0, 1, 2,},
+            /*Numer*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 2, 0, 1,},
+            /*Object*/{14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 2, 1, 0,},
+    };
+
+    private static int getPrimitiveIndex(Class c) {
+        for (byte i = 0; i < PRIMITIVES.length; i++) {
+            if (PRIMITIVES[i] == c) return i;
+        }
+        return -1;
+    }
+
+    private static int getPrimitiveDistance(Class from, Class to) {
+        // we know here that from!=to, so a distance of 0 is never valid
+        // get primitive type indexes
+        int fromIndex = getPrimitiveIndex(from);
+        int toIndex = getPrimitiveIndex(to);
+        if (fromIndex == -1 || toIndex == -1) return -1;
+        return PRIMITIVE_DISTANCE_TABLE[toIndex][fromIndex];
+    }
+
+    private static int getMaximumInterfaceDistance(Class c, Class interfaceClass) {
+        if (c == null || c == interfaceClass) return 0;
+        Class[] interfaces = c.getInterfaces();
+        int max = 0;
+        for (int i = 0; i < interfaces.length; i++) {
+            int sub = 1 + getMaximumInterfaceDistance(interfaces[i], interfaceClass);
+            max = Math.max(max, sub);
+        }
+        return Math.max(max, getMaximumInterfaceDistance(c.getSuperclass(), interfaceClass));
+    }
+
+    private static long calculateParameterDistance(Class argument, Class parameter) {
+        /**
+         * note: when shifting with 32 bit, you should only shift on a long. If you do
+         *       that with an int, then i==(i<<32), which means you loose the shift
+         *       information
+         */
+
+        if (parameter == argument) return 0;
+
+        if (parameter.isInterface()) {
+            return getMaximumInterfaceDistance(argument, parameter)<<1;
+        }
+
+        long objectDistance = 0;
+        if (argument != null) {
+            long pd = getPrimitiveDistance(parameter, argument);
+            if (pd != -1) return pd << 32;
+
+            // add one to dist to be sure interfaces are prefered
+            objectDistance += PRIMITIVES.length + 1;
+            Class clazz = ReflectionCache.autoboxType(argument);
+            while (clazz != null) {
+                if (clazz == parameter) break;
+                if (clazz == GString.class && parameter == String.class) {
+                    objectDistance += 2;
+                    break;
+                }
+                clazz = clazz.getSuperclass();
+                objectDistance += 3;
+            }
+        } else {
+            // choose the distance to Object if a parameter is null
+            // this will mean that Object is prefered over a more
+            // specific type
+            // remove one to dist to be sure Object is prefered
+            objectDistance--;
+            Class clazz = parameter;
+            if (clazz.isPrimitive()) {
+                objectDistance += 2;
+            } else {
+                while (clazz != Object.class) {
+                    clazz = clazz.getSuperclass();
+                    objectDistance += 2;
+                }
+            }
+        }
+        return objectDistance << 32;
+    }
+
+    public static long calculateParameterDistance(Class[] arguments, Class[] parameters) {
+        if (parameters.length == 0) return 0;
+
+        long ret = 0;        
+        int noVargsLength = parameters.length-1;
+        
+        // if the number of parameters does not match we have 
+        // a vargs usage
+        //
+        // case A: arguments.length<parameters.length
+        //
+        //         In this case arguments.length is always equal to
+        //         noVargsLength because only the last parameter
+        //         might be a optional vargs parameter
+        //
+        // case B: arguments.lenth>parameters.length
+        //
+        //         In this case all arguments with a index bigger than
+        //         paramMinus1 are part of the vargs, so a 
+        //         distance calculaion needs to be done against 
+        //         parameters[noVargsLength].getComponentType()
+        //
+        // case C: arguments.length==parameters.length && 
+        //         isAssignableFrom( parameters[noVargsLength],
+        //                           arguments[noVargsLength] )
+        //
+        //         In this case we have no vargs, so calculate directly
+        //
+        // case D: arguments.length==parameters.length && 
+        //         !isAssignableFrom( parameters[noVargsLength],
+        //                            arguments[noVargsLength] )
+        //
+        //         In this case we have a vargs case again, we need 
+        //         to calculate arguments[noVargsLength] against
+        //         parameters[noVargsLength].getComponentType
+        
+        
+        
+        // first we calculate all arguments, that are for sure not part
+        // of vargs.  Since the minimum for arguments is noVargsLength
+        // we can safely iterate to this point
+        for (int i = 0; i < noVargsLength; i++) {
+            ret += calculateParameterDistance(arguments[i], parameters[i]);
+        }
+        
+        if (arguments.length==parameters.length) {
+            // case C&D, we use baseType to calculate and set it
+            // to the value we need according to case C and D
+            Class baseType = parameters[noVargsLength]; // case C
+            if (!isAssignableFrom(parameters[noVargsLength],arguments[noVargsLength])) {
+                baseType=baseType.getComponentType(); // case D
+                ret+=2l<<VARGS_SHIFT; // penalty for vargs
+            }
+            ret += calculateParameterDistance(arguments[noVargsLength], baseType);
+        } else if (arguments.length>parameters.length) {
+            // case B
+            // we give our a vargs penalty for each exceeding argument and iterate
+            // by using parameters[noVargsLength].getComponentType()
+            ret += (2+arguments.length-parameters.length)<<VARGS_SHIFT;
+            Class vargsType = parameters[noVargsLength].getComponentType();
+            for (int i = noVargsLength; i < arguments.length; i++) {
+                ret += calculateParameterDistance(arguments[i], vargsType);
+            }
+        } else {
+            // case A
+            // we give a penalty for vargs, since we have no direct
+            // match for the last argument
+            ret+=1l<<VARGS_SHIFT;            
+        }  
+
+        return ret;
+    }
+
+    public static String capitalize(String property) {
+        return property.substring(0, 1).toUpperCase() + property.substring(1, property.length());
+    }
+
+    /**
+     * @param methods the methods to choose from
+     * @return the method with 1 parameter which takes the most general type of
+     *         object (e.g. Object)
+     */
+    public static Object chooseEmptyMethodParams(FastArray methods) {
+        Object vargsMethod = null;
+        final int len = methods.size();
+        final Object[] data = methods.getArray();
+        for (int i = 0; i != len; ++i) {
+            Object method = data[i];
+            final ParameterTypes pt = getParameterTypes(method);
+            CachedClass[] paramTypes = pt.getParameterTypes();
+            int paramLength = paramTypes.length;
+            if (paramLength == 0) {
+                return method;
+            } else if (paramLength == 1 && pt.isVargsMethod(EMPTY_ARRAY)) {
+                vargsMethod = method;
+            }
+        }
+        return vargsMethod;
+    }
+
+    /**
+     * @param methods the methods to choose from
+     * @return the method with 1 parameter which takes the most general type of
+     *         object (e.g. Object) ignoring primitve types
+     */
+    public static Object chooseMostGeneralMethodWith1NullParam(FastArray methods) {
+        // let's look for methods with 1 argument which matches the type of the
+        // arguments
+        CachedClass closestClass = null;
+        CachedClass closestVargsClass = null;
+        Object answer = null;
+        int closestDist = -1;
+        final int len = methods.size();
+        for (int i = 0; i != len; ++i) {
+            final Object[] data = methods.getArray();
+            Object method = data[i];
+            final ParameterTypes pt = getParameterTypes(method);
+            CachedClass[] paramTypes = pt.getParameterTypes();
+            int paramLength = paramTypes.length;
+            if (paramLength == 0 || paramLength > 2) continue;
+
+            CachedClass theType = paramTypes[0];
+            if (theType.isPrimitive) continue;
+
+            if (paramLength == 2) {
+                if (!pt.isVargsMethod(ARRAY_WITH_NULL)) continue;
+                if (closestClass == null) {
+                    closestVargsClass = paramTypes[1];
+                    closestClass = theType;
+                    answer = method;
+                } else if (closestClass.getCachedClass() == theType.getCachedClass()) {
+                    if (closestVargsClass == null) continue;
+                    CachedClass newVargsClass = paramTypes[1];
+                    if (closestVargsClass == null || isAssignableFrom(newVargsClass.getCachedClass(), closestVargsClass.getCachedClass())) {
+                        closestVargsClass = newVargsClass;
+                        answer = method;
+                    }
+                } else if (isAssignableFrom(theType.getCachedClass(), closestClass.getCachedClass())) {
+                    closestVargsClass = paramTypes[1];
+                    closestClass = theType;
+                    answer = method;
+                }
+            } else {
+                if (closestClass == null || isAssignableFrom(theType.getCachedClass(), closestClass.getCachedClass())) {
+                    closestVargsClass = null;
+                    closestClass = theType;
+                    answer = method;
+                    closestDist = -1;
+                } else {
+                    // closestClass and theType are not in a subtype relation, we need
+                    // to check the distance to Object
+                    if (closestDist == -1) closestDist = closestClass.getSuperClassDistance();
+                    int newDist = theType.getSuperClassDistance();
+                    if (newDist < closestDist) {
+                        closestDist = newDist;
+                        closestVargsClass = null;
+                        closestClass = theType;
+                        answer = method;
+                    }
+                }
+            }
+        }
+        return answer;
+    }
+
+    // 
+    private static int calculateSimplifiedClassDistanceToObject(Class clazz) {
+        int objectDistance = 0;
+        while (clazz != null) {
+            clazz = clazz.getSuperclass();
+            objectDistance++;
+        }
+        return objectDistance;
+    }
+
+
+    /**
+     * @param list   a list of MetaMethods
+     * @param method the MetaMethod of interest
+     * @return true if a method of the same matching prototype was found in the
+     *         list
+     */
+    public static boolean containsMatchingMethod(List list, MetaMethod method) {
+        for (Iterator iter = list.iterator(); iter.hasNext();) {
+            MetaMethod aMethod = (MetaMethod) iter.next();
+            CachedClass[] params1 = aMethod.getParameterTypes();
+            CachedClass[] params2 = method.getParameterTypes();
+            if (params1.length == params2.length) {
+                boolean matches = true;
+                for (int i = 0; i < params1.length; i++) {
+                    if (params1[i] != params2[i]) {
+                        matches = false;
+                        break;
+                    }
+                }
+                if (matches) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * param instance array to the type array
+     *
+     * @param args the arguments
+     * @return the types of the arguments
+     */
+    public static Class[] convertToTypeArray(Object[] args) {
+        if (args == null)
+            return null;
+        int s = args.length;
+        Class[] ans = new Class[s];
+        for (int i = 0; i < s; i++) {
+            Object o = args[i];
+            if (o == null) {
+                ans[i] = null;
+            } else if (o instanceof Wrapper) {
+                ans[i] = ((Wrapper) o).getType();
+            } else {
+                ans[i] = o.getClass();
+            }
+        }
+        return ans;
+    }
+
+    public static Object doConstructorInvoke(CachedConstructor constructor, Object[] argumentArray) {
+        final Constructor constr = constructor.cachedConstructor;
+        if (LOG.isLoggable(Level.FINER)) {
+            logMethodCall(constr.getDeclaringClass(), constr.getName(), argumentArray);
+        }
+        argumentArray = constructor.coerceArgumentsToClasses(argumentArray);
+        try {
+            return constr.newInstance(argumentArray);
+        } catch (InvocationTargetException e) {
+            throw new InvokerInvocationException(e);
+        } catch (IllegalArgumentException e) {
+            throw createExceptionText("failed to invoke constructor: ", constr, argumentArray, e, false);
+        } catch (IllegalAccessException e) {
+            throw createExceptionText("could not access constructor: ", constr, argumentArray, e, false);
+        } catch (Exception e) {
+            throw createExceptionText("failed to invoke constructor: ", constr, argumentArray, e, true);
+        }
+    }
+
+    private static GroovyRuntimeException createExceptionText(String init, Constructor constructor, Object[] argumentArray, Throwable e, boolean setReason) {
+        throw new GroovyRuntimeException(
+                init
+                        + constructor
+                        + " with arguments: "
+                        + InvokerHelper.toString(argumentArray)
+                        + " reason: "
+                        + e,
+                setReason ? e : null);
+    }
+
+    public static Object makeCommonArray(Object[] arguments, int offset, Class fallback) {
+        // arguments.leght>0 && !=null
+        Class baseClass = null;
+        for (int i = offset; i < arguments.length; i++) {
+            if (arguments[i] == null) continue;
+            Class argClass = arguments[i].getClass();
+            if (baseClass == null) {
+                baseClass = argClass;
+            } else {
+                for (; baseClass != Object.class; baseClass = baseClass.getSuperclass()) {
+                    if (baseClass.isAssignableFrom(argClass)) break;
+                }
+            }
+        }
+        if (baseClass == null) {
+            // all arguments were null
+            baseClass = fallback;
+        }
+        Object result = makeArray(null, baseClass, arguments.length - offset);
+        System.arraycopy(arguments, offset, result, 0, arguments.length - offset);
+        return result;
+    }
+
+    public static Object makeArray(Object obj, Class secondary, int length) {
+        Class baseClass = secondary;
+        if (obj != null) {
+            baseClass = obj.getClass();
+        }
+        /*if (GString.class.isAssignableFrom(baseClass)) {
+              baseClass = GString.class;
+          }*/
+        return Array.newInstance(baseClass, length);
+    }
+
+    private static GroovyRuntimeException createExceptionText(String init, MetaMethod method, Object object, Object[] args, Throwable reason, boolean setReason) {
+        return new GroovyRuntimeException(
+                init
+                        + method
+                        + " on: "
+                        + object
+                        + " with arguments: "
+                        + InvokerHelper.toString(args)
+                        + " reason: "
+                        + reason,
+                setReason ? reason : null);
+    }
+
+    public static Object doMethodInvoke(Object object, MetaMethod method, Object[] argumentArray) {
+        argumentArray = method.getParamTypes().coerceArgumentsToClasses(argumentArray);
+        try {
+            return method.invoke(object, argumentArray);
+        } catch (IllegalArgumentException e) {
+            //TODO: test if this is ok with new MOP, should be changed!
+            // we don't want the exception being unwrapped if it is a IllegalArgumentException
+            // but in the case it is for example a IllegalThreadStateException, we want the unwrapping
+            // from the runtime
+            //Note: the reason we want unwrapping sometimes and sometimes not is that the method
+            // invokation tries to invoke the method with and then reacts with type transformation
+            // if the invokation failed here. This is ok for IllegalArgumentException, but it is
+            // possible that a Reflector will be used to execute the call and then an Exception from inside
+            // the method is not wrapped in a InvocationTargetException and we will end here.
+            boolean setReason = e.getClass() != IllegalArgumentException.class;
+            throw createExceptionText("failed to invoke method: ", method, object, argumentArray, e, setReason);
+        } catch (RuntimeException e) {
+            throw e;
+        } catch (Exception e) {
+            throw createExceptionText("failed to invoke method: ", method, object, argumentArray, e, true);
+        }
+    }
+
+    protected static String getClassName(Object object) {
+        if (object == null) return null;
+        return (object instanceof Class) ? ((Class) object).getName() : object.getClass().getName();
+    }
+
+    /**
+     * Returns a callable object for the given method name on the object.
+     * The object acts like a Closure in that it can be called, like a closure
+     * and passed around - though really its a method pointer, not a closure per se.
+     *
+     * @param object the object containing the method
+     * @param methodName the method of interest
+     * @return the resulting closure-like method pointer
+     */
+    public static Closure getMethodPointer(Object object, String methodName) {
+        return new MethodClosure(object, methodName);
+    }
+
+    public static ParameterTypes getParameterTypes(Object methodOrConstructor) {
+        if (methodOrConstructor instanceof ParameterTypes) {
+            return (ParameterTypes) methodOrConstructor;
+        }
+        if (methodOrConstructor instanceof MetaMethod) {
+            return ((MetaMethod) methodOrConstructor).getParamTypes();
+        }
+        if (methodOrConstructor instanceof Method) {
+            Method method = (Method) methodOrConstructor;
+            return CachedMethod.find(method);
+        }
+        if (methodOrConstructor instanceof Constructor) {
+            Constructor constructor = (Constructor) methodOrConstructor;
+            return CachedConstructor.find(constructor);
+        }
+        throw new IllegalArgumentException("Must be a Method or Constructor");
+    }
+
+    public static boolean isAssignableFrom(Class classToTransformTo, Class classToTransformFrom) {
+        if (classToTransformFrom == null) return true;
+        classToTransformTo = ReflectionCache.autoboxType(classToTransformTo);
+        classToTransformFrom = ReflectionCache.autoboxType(classToTransformFrom);
+
+        if (classToTransformTo == classToTransformFrom) {
+            return true;
+        }
+        // note: there is not coercion for boolean and char. Range matters, precision doesn't
+        else if (classToTransformTo == Integer.class) {
+            if (classToTransformFrom == Integer.class
+                    || classToTransformFrom == Short.class
+                    || classToTransformFrom == Byte.class
+                    || classToTransformFrom == BigInteger.class)
+                return true;
+        } else if (classToTransformTo == Double.class) {
+            if (classToTransformFrom == Double.class
+                    || classToTransformFrom == Integer.class
+                    || classToTransformFrom == Long.class
+                    || classToTransformFrom == Short.class
+                    || classToTransformFrom == Byte.class
+                    || classToTransformFrom == Float.class
+                    || classToTransformFrom == BigDecimal.class
+                    || classToTransformFrom == BigInteger.class)
+                return true;
+        } else if (classToTransformTo == BigDecimal.class) {
+            if (classToTransformFrom == Double.class
+                    || classToTransformFrom == Integer.class
+                    || classToTransformFrom == Long.class
+                    || classToTransformFrom == Short.class
+                    || classToTransformFrom == Byte.class
+                    || classToTransformFrom == Float.class
+                    || classToTransformFrom == BigDecimal.class
+                    || classToTransformFrom == BigInteger.class)
+                return true;
+        } else if (classToTransformTo == BigInteger.class) {
+            if (classToTransformFrom == Integer.class
+                    || classToTransformFrom == Long.class
+                    || classToTransformFrom == Short.class
+                    || classToTransformFrom == Byte.class
+                    || classToTransformFrom == BigInteger.class)
+                return true;
+        } else if (classToTransformTo == Long.class) {
+            if (classToTransformFrom == Long.class
+                    || classToTransformFrom == Integer.class
+                    || classToTransformFrom == Short.class
+                    || classToTransformFrom == Byte.class)
+                return true;
+        } else if (classToTransformTo == Float.class) {
+            if (classToTransformFrom == Float.class
+                    || classToTransformFrom == Integer.class
+                    || classToTransformFrom == Long.class
+                    || classToTransformFrom == Short.class
+                    || classToTransformFrom == Byte.class)
+                return true;
+        } else if (classToTransformTo == Short.class) {
+            if (classToTransformFrom == Short.class
+                    || classToTransformFrom == Byte.class)
+                return true;
+        } else if (classToTransformTo == String.class) {
+            if (classToTransformFrom == String.class ||
+                    GString.class.isAssignableFrom(classToTransformFrom)) {
+                return true;
+            }
+        }
+
+        return ReflectionCache.isAssignableFrom(classToTransformTo, classToTransformFrom);
+    }
+
+    public static boolean isGenericSetMethod(MetaMethod method) {
+        return (method.getName().equals("set"))
+                && method.getParameterTypes().length == 2;
+    }
+
+    protected static boolean isSuperclass(Class claszz, Class superclass) {
+        while (claszz != null) {
+            if (claszz == superclass) return true;
+            claszz = claszz.getSuperclass();
+        }
+        return false;
+    }
+    
+    public static boolean parametersAreCompatible(Class[] arguments, Class[] parameters) {
+        if (arguments.length != parameters.length) return false;
+        for (int i = 0; i < arguments.length; i++) {
+            if (!isAssignableFrom(parameters[i], arguments[i])) return false;
+        }
+        return true;
+    }
+
+    public static boolean isValidMethod(ParameterTypes pt, Class[] arguments, boolean includeCoerce) {
+        if (arguments == null) {
+            return true;
+        }
+        int size = arguments.length;
+
+        CachedClass[] paramTypes = pt.getParameterTypes();
+        if ((size >= paramTypes.length || size == paramTypes.length - 1)
+                && paramTypes.length > 0
+                && paramTypes[(paramTypes.length - 1)].isArray) {
+            // first check normal number of parameters
+            for (int i = 0; i < paramTypes.length - 1; i++) {
+                if (isAssignableFrom(paramTypes[i].getCachedClass(), arguments[i])) continue;
+                return false;
+            }
+            // check varged
+            Class clazz = paramTypes[paramTypes.length - 1].getCachedClass().getComponentType();
+            for (int i = paramTypes.length; i < size; i++) {
+                if (isAssignableFrom(clazz, arguments[i])) continue;
+                return false;
+            }
+            return true;
+        } else if (paramTypes.length == size) {
+            // lets check the parameter types match
+            for (int i = 0; i < size; i++) {
+                if (isAssignableFrom(paramTypes[i].getCachedClass(), arguments[i])) continue;
+                return false;
+            }
+            return true;
+        } else if (paramTypes.length == 1 && size == 0) {
+            return true;
+        }
+        return false;
+
+    }
+
+    public static boolean isValidMethod(Object method, Class[] arguments, boolean includeCoerce) {
+        return isValidMethod(getParameterTypes(method), arguments, includeCoerce);
+    }
+
+    public static void logMethodCall(Object object, String methodName, Object[] arguments) {
+        String className = getClassName(object);
+        String logname = "methodCalls." + className + "." + methodName;
+        Logger objLog = Logger.getLogger(logname);
+        if (!objLog.isLoggable(Level.FINER)) return;
+        StringBuffer msg = new StringBuffer(methodName);
+        msg.append("(");
+        if (arguments != null) {
+            for (int i = 0; i < arguments.length;) {
+                msg.append(normalizedValue(arguments[i]));
+                if (++i < arguments.length) {
+                    msg.append(",");
+                }
+            }
+        }
+        msg.append(")");
+        objLog.logp(Level.FINER, className, msg.toString(), "called from MetaClass.invokeMethod");
+    }
+
+    protected static String normalizedValue(Object argument) {
+        String value;
+        try {
+            value = argument.toString();
+            if (value.length() > MAX_ARG_LEN) {
+                value = value.substring(0, MAX_ARG_LEN - 2) + "..";
+            }
+            if (argument instanceof String) {
+                value = "\'" + value + "\'";
+            }
+        } catch (Exception e) {
+            value = shortName(argument);
+        }
+        return value;
+    }
+
+    protected static String shortName(Object object) {
+        if (object == null || object.getClass() == null) return "unknownClass";
+        String name = getClassName(object);
+        if (name == null) return "unknownClassName"; // *very* defensive...
+        int lastDotPos = name.lastIndexOf('.');
+        if (lastDotPos < 0 || lastDotPos >= name.length() - 1) return name;
+        return name.substring(lastDotPos + 1);
+    }
+
+    public static Class[] wrap(Class[] classes) {
+        Class[] wrappedArguments = new Class[classes.length];
+        for (int i = 0; i < wrappedArguments.length; i++) {
+            Class c = classes[i];
+            if (c == null) continue;
+            if (c.isPrimitive()) {
+                if (c == Integer.TYPE) {
+                    c = Integer.class;
+                } else if (c == Byte.TYPE) {
+                    c = Byte.class;
+                } else if (c == Long.TYPE) {
+                    c = Long.class;
+                } else if (c == Double.TYPE) {
+                    c = Double.class;
+                } else if (c == Float.TYPE) {
+                    c = Float.class;
+                }
+            } else if (isSuperclass(c, GString.class)) {
+                c = String.class;
+            }
+            wrappedArguments[i] = c;
+        }
+        return wrappedArguments;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/MethodClosure.java b/groovy/src/main/org/codehaus/groovy/runtime/MethodClosure.java
new file mode 100644
index 0000000..75c1c50
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/MethodClosure.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+
+/**
+ * Represents a method on an object using a closure which can be invoked
+ * at any time
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MethodClosure extends Closure {
+
+    private String method;
+    
+    public MethodClosure(Object owner, String method) {
+        super(owner);
+        this.method = method;
+
+        final Class clazz = owner.getClass()==Class.class?(Class) owner:owner.getClass();
+        
+        maximumNumberOfParameters = 0;
+        parameterTypes = new Class [0];
+
+        Method[] methods = (Method[]) AccessController.doPrivileged(new  PrivilegedAction() {
+            public Object run() {
+                return clazz.getMethods();
+            }
+        });
+        for (int j = 0; j < methods.length; j++) {
+            if (method.equals(methods[j].getName()) && methods[j].getParameterTypes().length > maximumNumberOfParameters) {
+                Class[] pt = methods[j].getParameterTypes();
+                maximumNumberOfParameters = pt.length;
+                parameterTypes = pt;
+            }
+        }        
+        methods = (Method[]) AccessController.doPrivileged(new  PrivilegedAction() {
+            public Object run() {
+                return clazz.getDeclaredMethods();
+            }
+        });
+        for (int j = 0; j < methods.length; j++) {
+            if (method.equals(methods[j].getName()) && methods[j].getParameterTypes().length > maximumNumberOfParameters) {
+                Class[] pt = methods[j].getParameterTypes();
+                maximumNumberOfParameters = pt.length;
+                parameterTypes = pt;
+            }
+        }
+
+    }
+    
+    public String getMethod() {
+        return method;
+    }
+
+    protected Object doCall(Object arguments) {
+        return InvokerHelper.invokeMethod(getDelegate(), method, arguments);
+    }
+    
+    public Object getProperty(String property) {
+        if ("method".equals(property)) {
+            return getMethod();
+        } else  return super.getProperty(property);        
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/MethodKey.java b/groovy/src/main/org/codehaus/groovy/runtime/MethodKey.java
new file mode 100644
index 0000000..ab6389a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/MethodKey.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * An abstract base class for a key used for comparators and Map keys to lookup a method by
+ * name and parameter types
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class MethodKey {
+
+    private int hash;
+    private String name;
+    private Class sender;
+    private boolean isCallToSuper;
+    
+    public MethodKey(Class sender, String name, boolean isCallToSuper) {
+        this.sender = sender;
+        this.name = name;
+        this.isCallToSuper = isCallToSuper;
+    }
+
+    /**
+     * Creates an immutable copy that we can cache. 
+     */
+    public MethodKey createCopy() {
+        int size = getParameterCount();
+        Class[] paramTypes = new Class[size];
+        for (int i = 0; i < size; i++) {
+            paramTypes[i] = getParameterType(i);
+        }
+        return new DefaultMethodKey(sender, name, paramTypes, isCallToSuper);
+    }
+
+    public boolean equals(Object that) {
+        if (this == that) {
+            return true;
+        }
+        else if (that instanceof MethodKey) {
+            return equals((MethodKey) that);
+        }
+        return false;
+    }
+
+    public boolean equals(MethodKey that) {
+      int size;
+      if (sender!=that.sender) return false;
+      if (isCallToSuper!=that.isCallToSuper) return false;
+      if (!name.equals(that.name)) return false;
+      if ((size = getParameterCount()) != that.getParameterCount()) return false;
+      
+      for (int i = 0; i < size; i++) {
+          if (getParameterType(i) != that.getParameterType(i)) {
+              return false;
+          }
+      }
+      return true;
+    }
+
+    public int hashCode() {
+        if (hash == 0) {
+            hash = createHashCode();
+            if (hash == 0) {
+                hash = 0xcafebabe;
+            }
+        }
+        return hash;
+    }
+
+    public String toString() {
+        return super.toString() + "[name:" + name + "; params:" + getParamterTypes();
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public List getParamterTypes() {
+        int size = getParameterCount();
+        if (size <= 0) {
+            return Collections.EMPTY_LIST;
+        }
+        List params = new ArrayList(size);
+        for (int i = 0; i < size; i++) {
+            params.add(getParameterType(i));
+        }
+        return params;
+    }
+
+    public abstract int getParameterCount();
+    public abstract Class getParameterType(int index);
+
+    protected int createHashCode() {
+        int answer = name.hashCode();
+        int size = getParameterCount();
+
+        /** @todo we should use the real Josh Bloch algorithm here */
+
+        // can't remember the exact Josh Bloch algorithm and I've not got the book handy
+        // but its something like this IIRC
+        for (int i = 0; i < size; i++) {
+            answer *= 37;
+            answer += 1 + getParameterType(i).hashCode();
+        }
+        answer *= 37;
+        answer += isCallToSuper?1:0;
+        answer *= 37;
+        answer += 1 + sender.hashCode();
+        return answer;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/NullObject.java b/groovy/src/main/org/codehaus/groovy/runtime/NullObject.java
new file mode 100644
index 0000000..6c1ce71
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/NullObject.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import java.util.Collections;
+import java.util.Iterator;
+
+import groovy.lang.GroovyObjectSupport;
+
+public class NullObject extends GroovyObjectSupport {
+    private static final NullObject INSTANCE = new NullObject();
+
+    /**
+     * private constructor
+     */
+    private NullObject() {
+    }
+
+    /**
+     * get the NullObject reference
+     *
+     * @return the null object
+     */
+    public static NullObject getNullObject() {
+        return INSTANCE;
+    }
+
+    /**
+     * Since this is implemented as a singleton, we should avoid the
+     * use of the clone method
+     */
+    public Object clone() throws CloneNotSupportedException {
+        throw new CloneNotSupportedException();
+    }
+
+    /**
+     * Tries to get a property on null, which will always fail
+     *
+     * @param property - the property to get
+     * @return a NPE
+     */
+    public Object getProperty(String property) {
+        throw new NullPointerException("Cannot get property " + property + "() on null object");
+    }
+
+    /**
+     * Tries to set a property on null, which will always fail
+     *
+     * @param property - the proprty to set
+     * @param newValue - the new value of the property
+     */
+    public void setProperty(String property, Object newValue) {
+        throw new NullPointerException("Cannot set property " + property + "() on null object");
+    }
+
+    /**
+     * Tries to invoke a method on null, which will always fail
+     *
+     * @param name the name of the method to invoke
+     * @param args - arguments to the method
+     * @return a NPE
+     */
+    public Object invokeMethod(String name, Object args) {
+        throw new NullPointerException("Cannot invoke method " + name + "() on null object");
+    }
+
+    /**
+     * null is only equal to null
+     *
+     * @param to - the reference object with which to compare
+     * @return - true if this object is the same as the to argument
+     */
+    public boolean equals(Object to) {
+        return to == null;
+    }
+
+    /**
+     * iterator() method to be able to iterate on null.
+     * Note: this part is from Invoker
+     *
+     * @return an iterator for an empty list
+     */
+    public Iterator iterator() {
+        return Collections.EMPTY_LIST.iterator();
+    }
+
+    /**
+     * Allows to add a String to null.
+     * The result is concatenated String of the result of calling
+     * toString() on this object and the String in the parameter.
+     *
+     * @param s - the String to concatenate
+     * @return the concatenated string
+     */
+    public Object plus(String s) {
+        return getMetaClass().invokeMethod(this, "toString", new Object[]{}) + s;
+    }
+
+    /**
+     * The method "is" is used to test for equal references.
+     * This method will return true only if the given parameter
+     * is null
+     *
+     * @param other - the object to test
+     * @return true if other is null
+     */
+    public boolean is(Object other) {
+        return other == null;
+    }
+
+    /**
+     * Type conversion method for null.
+     *
+     * @param c - the class to convert to
+     * @return always null
+     */
+    public Object asType(Class c) {
+        return null;
+    }
+
+    public String toString() {
+        return "null";
+    }
+
+    public int hashCode() {
+        throw new NullPointerException("hashCode() not allowed on null");
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/ReflectionMethodInvoker.java b/groovy/src/main/org/codehaus/groovy/runtime/ReflectionMethodInvoker.java
new file mode 100644
index 0000000..010a856
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/ReflectionMethodInvoker.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * Utility class to call methods through reflection, and falls through using the <code>Invoker</code> to call the method if it fails.
+ * The class is particularly useful for Groovy classes implementing <code>GroovyIntercpetable</code>,
+ * since it is not possible to call any method from this class,
+ * because it is intercepted by the <code>invokeMethod()</code> method.
+ *
+ * @author Guillaume Laforge
+ */
+public class ReflectionMethodInvoker {
+
+    /**
+     * Invoke a method through reflection.
+     * Falls through to using the Invoker to call the method in case the reflection call fails..
+     *
+     * @param object the object on which to invoke a method
+     * @param methodName the name of the method to invoke
+     * @param parameters the parameters of the method call
+     * @return the result of the method call
+     */
+    public static Object invoke(Object object, String methodName, Object[] parameters) {
+        try {
+            Class[] classTypes = new Class[parameters.length];
+            for (int i = 0; i < classTypes.length; i++) {
+                classTypes[i] = parameters[i].getClass();
+            }
+            Method method = object.getClass().getMethod(methodName, classTypes);
+            return method.invoke(object, parameters);
+        } catch (Throwable t) {
+            return InvokerHelper.invokeMethod(object, methodName,  parameters);
+        }
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/Reflector.java b/groovy/src/main/org/codehaus/groovy/runtime/Reflector.java
new file mode 100644
index 0000000..90419c6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/Reflector.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.MissingMethodException;
+import org.codehaus.groovy.reflection.CachedMethod;
+
+
+/**
+ * Provides as alternative to reflection using bytecode generation.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision: 4247 $
+ */
+public class Reflector {
+
+    public Object invoke(CachedMethod method, Object object, Object[] arguments) {
+        return noSuchMethod(method, object, arguments);
+    }
+
+    protected Object noSuchMethod(CachedMethod method, Object object, Object[] arguments) {
+        throw new MissingMethodException(method.getName(), method.getDeclaringClass(), arguments, false);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/RegexSupport.java b/groovy/src/main/org/codehaus/groovy/runtime/RegexSupport.java
new file mode 100644
index 0000000..adcaf6d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/RegexSupport.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import java.util.regex.Matcher;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: sam
+ */
+public class RegexSupport {
+
+    private static final ThreadLocal CURRENT_MATCHER = new ThreadLocal();
+
+    public static Matcher getLastMatcher() {
+        return (Matcher) CURRENT_MATCHER.get();
+    }
+
+    public static void setLastMatcher(Matcher matcher) {
+        CURRENT_MATCHER.set(matcher);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java b/groovy/src/main/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
new file mode 100644
index 0000000..cad8d16
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/ScriptBytecodeAdapter.java
@@ -0,0 +1,806 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.*;
+import org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack;
+import org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+import org.codehaus.groovy.runtime.wrappers.GroovyObjectWrapper;
+import org.codehaus.groovy.runtime.wrappers.PojoWrapper;
+import org.codehaus.groovy.runtime.wrappers.Wrapper;
+
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A static helper class to interface bytecode and runtime
+ *
+ * @author Jochen Theodorou
+ * @version $Revision$
+ */
+public class ScriptBytecodeAdapter {
+    public static final Object[] EMPTY_ARGS = {};
+    private static final Integer ZERO = new Integer(0);
+    private static final Integer MINUS_ONE = new Integer(-1);
+    private static final Integer ONE = new Integer(1);
+
+    //  --------------------------------------------------------
+    //                   exception handling
+    //  --------------------------------------------------------
+    private static Throwable unwrap(GroovyRuntimeException gre) {
+        if (gre instanceof MissingPropertyExceptionNoStack) {
+            MissingPropertyExceptionNoStack noStack = (MissingPropertyExceptionNoStack) gre;
+            return new MissingPropertyException(noStack.getProperty(), noStack.getType());
+        }
+
+        if (gre instanceof MissingMethodExceptionNoStack) {
+            MissingMethodExceptionNoStack noStack = (MissingMethodExceptionNoStack) gre;
+            return new MissingMethodException(noStack.getMethod(), noStack.getType(), noStack.getArguments(), noStack.isStatic());
+        }
+
+        Throwable th = gre;
+        if (th.getCause() != null && th.getCause() != gre) th = th.getCause();
+        if (th != gre && (th instanceof GroovyRuntimeException)) return unwrap((GroovyRuntimeException) th);
+        return th;
+    }
+
+    //  --------------------------------------------------------
+    //                       methods for this
+    //  --------------------------------------------------------
+    public static Object invokeMethodOnCurrentN(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
+        Object result = null;
+        try {
+            try {
+                // if it's a pure interceptable object (even intercepting toString(), clone(), ...)
+                if (receiver instanceof GroovyInterceptable) {
+                    result = receiver.invokeMethod(messageName, messageArguments);
+                }
+                //else if there's a statically typed method or a GDK method
+                else {
+                    result = receiver.getMetaClass().invokeMethod(senderClass, receiver, messageName, messageArguments, false, true);
+                }
+            } catch (MissingMethodException e) {
+                if (receiver.getClass() == e.getType() && e.getMethod().equals(messageName)) {
+                    // in case there's nothing else, invoke the object's own invokeMethod()
+                    result = receiver.invokeMethod(messageName, messageArguments);
+                } else {
+                    throw e;
+                }
+            }
+        } catch (GroovyRuntimeException t) {
+            throw unwrap(t);
+        }
+        return result;
+    }
+
+    public static Object invokeMethodOnCurrentNSafe(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
+        return invokeMethodOnCurrentN(senderClass, receiver, messageName, messageArguments);
+    }
+
+    public static Object invokeMethodOnCurrentNSpreadSafe(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
+        if (!isSpreadable(receiver))
+            return invokeMethodOnCurrentN(senderClass, receiver, messageName, messageArguments);
+
+        List answer = new ArrayList();
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            answer.add(invokeMethodNSafe(senderClass, it.next(), messageName, messageArguments));
+        }
+        return answer;
+    }
+
+    public static Object invokeMethodOnCurrent0(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        return invokeMethodOnCurrentN(senderClass, receiver, messageName, EMPTY_ARGS);
+    }
+
+    public static Object invokeMethodOnCurrent0Safe(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
+        return invokeMethodOnCurrentNSafe(senderClass, receiver, messageName, EMPTY_ARGS);
+    }
+
+    public static Object invokeMethodOnCurrent0SpreadSafe(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
+        return invokeMethodOnCurrentNSpreadSafe(senderClass, receiver, messageName, EMPTY_ARGS);
+    }
+
+    //  --------------------------------------------------------
+    //                       methods for super
+    //  --------------------------------------------------------
+    public static Object invokeMethodOnSuperN(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
+        MetaClass metaClass = receiver.getMetaClass();
+        // ignore interception and missing method fallback
+        Object result = null;
+        try {
+            result = metaClass.invokeMethod(senderClass, receiver, messageName, messageArguments, true, true);
+        } catch (GroovyRuntimeException t) {
+            throw unwrap(t);
+        }
+        return result;
+    }
+
+    public static Object invokeMethodOnSuperNSafe(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
+        return invokeMethodOnSuperN(senderClass, receiver, messageName, messageArguments);
+    }
+
+    public static Object invokeMethodOnSuperNSpreadSafe(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
+        if (!isSpreadable(receiver))
+            return invokeMethodOnSuperN(senderClass, receiver, messageName, messageArguments);
+
+        List answer = new ArrayList();
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            answer.add(invokeMethodNSafe(senderClass, it.next(), messageName, messageArguments));
+        }
+        return answer;
+    }
+
+    public static Object invokeMethodOnSuper0(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        return invokeMethodOnSuperN(senderClass, receiver, messageName, EMPTY_ARGS);
+    }
+
+    public static Object invokeMethodOnSuper0Safe(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
+        return invokeMethodOnSuperNSafe(senderClass, receiver, messageName, EMPTY_ARGS);
+    }
+
+    public static Object invokeMethodOnSuper0SpreadSafe(Class senderClass, GroovyObject receiver, String messageName, Object[] messageArguments) throws Throwable {
+        return invokeMethodOnSuperNSpreadSafe(senderClass, receiver, messageName, EMPTY_ARGS);
+    }
+
+    //  --------------------------------------------------------
+    //              normal method invocation
+    //  --------------------------------------------------------       
+    public static Object invokeMethodN(Class senderClass, Object receiver, String messageName, Object[] messageArguments) throws Throwable {
+        try {
+            return InvokerHelper.invokeMethod(receiver, messageName, messageArguments);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static Object invokeMethodNSafe(Class senderClass, Object receiver, String messageName, Object[] messageArguments) throws Throwable {
+        if (receiver == null) return null;
+        return invokeMethodN(senderClass, receiver, messageName, messageArguments);
+    }
+
+    public static Object invokeMethodNSpreadSafe(Class senderClass, Object receiver, String messageName, Object[] messageArguments) throws Throwable {
+        if (receiver == null) return null;
+        if (!isSpreadable(receiver))
+            return invokeMethodN(senderClass, receiver, messageName, messageArguments);
+
+        List answer = new ArrayList();
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            answer.add(invokeMethodNSafe(senderClass, it.next(), messageName, messageArguments));
+        }
+        return answer;
+    }
+
+    private static Object[] getBoxedItems(Object receiver) {
+        return DefaultTypeTransformation.primitiveArrayToList(receiver).toArray();
+    }
+
+    public static Object invokeMethod0(Class senderClass, Object receiver, String messageName) throws Throwable {
+        return invokeMethodN(senderClass, receiver, messageName, EMPTY_ARGS);
+    }
+
+    public static Object invokeMethod0Safe(Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (receiver == null) return null;
+        return invokeMethodNSafe(senderClass, receiver, messageName, EMPTY_ARGS);
+    }
+
+    public static Object invokeMethod0SpreadSafe(Class senderClass, Object receiver, String messageName) throws Throwable {
+        return invokeMethodNSpreadSafe(senderClass, receiver, messageName, EMPTY_ARGS);
+    }
+
+    //  --------------------------------------------------------
+    //                static normal method invocation
+    //  --------------------------------------------------------       
+    public static Object invokeStaticMethodN(Class senderClass, Class receiver, String messageName, Object[] messageArguments) throws Throwable {
+        try {
+            return InvokerHelper.invokeStaticMethod(receiver, messageName, messageArguments);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static Object invokeStaticMethod0(Class senderClass, Class receiver, String messageName) throws Throwable {
+        return invokeStaticMethodN(senderClass, receiver, messageName, EMPTY_ARGS);
+    }
+
+    //  --------------------------------------------------------
+    //              normal constructor invocation (via new)
+    //  --------------------------------------------------------       
+    public static Object invokeNewN(Class senderClass, Class receiver, Object arguments) throws Throwable {
+        try {
+            return InvokerHelper.invokeConstructorOf(receiver, arguments);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static Object invokeNew0(Class senderClass, Class receiver) throws Throwable {
+        return invokeNewN(senderClass, receiver, EMPTY_ARGS);
+    }
+
+    //  --------------------------------------------------------
+    //       special constructor invocation (via this/super)
+    //  --------------------------------------------------------       
+
+    public static int selectConstructorAndTransformArguments(Object[] arguments, int numberOfConstructors, Class which) {
+        MetaClass metaClass = GroovySystem.getMetaClassRegistry().getMetaClass(which);
+        return metaClass.selectConstructorAndTransformArguments(numberOfConstructors, arguments);
+    }
+
+    //  --------------------------------------------------------
+    //              field handling super: get
+    //  --------------------------------------------------------       
+
+    public static Object getFieldOnSuper(Class senderClass, Object receiver, String messageName) throws Throwable {
+        try {
+            if (receiver instanceof Class) {
+                return InvokerHelper.getAttribute(receiver, messageName);
+            } else {
+                MetaClass mc = ((GroovyObject) receiver).getMetaClass();
+                return mc.getAttribute(senderClass, receiver, messageName, true);
+            }
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static Object getFieldOnSuperSafe(Class senderClass, Object receiver, String messageName) throws Throwable {
+        return getFieldOnSuper(senderClass, receiver, messageName);
+    }
+
+    public static Object getFieldOnSuperSpreadSafe(Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (!isSpreadable(receiver))
+            return getFieldOnSuper(senderClass, receiver, messageName);
+
+        List answer = new ArrayList();
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            answer.add(getFieldOnSuper(senderClass, it.next(), messageName));
+        }
+        return answer;
+    }
+
+    //  --------------------------------------------------------
+    //              field handling super: set
+    //  --------------------------------------------------------       
+
+    public static void setFieldOnSuper(Object messageArgument, Class senderClass, Object receiver, String messageName) throws Throwable {
+        try {
+            if (receiver instanceof Class) {
+                InvokerHelper.setAttribute(receiver, messageName, messageArgument);
+            } else {
+                MetaClass mc = ((GroovyObject) receiver).getMetaClass();
+                mc.setAttribute(senderClass, receiver, messageName, messageArgument, true, true);
+            }
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static void setFieldOnSuperSafe(Object messageArgument, Class senderClass, Object receiver, String messageName) throws Throwable {
+        setFieldOnSuper(messageArgument, senderClass, receiver, messageName);
+    }
+
+    public static void setFieldOnSuperSpreadSafe(Object messageArgument, Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (!isSpreadable(receiver)) {
+            setFieldOnSuper(messageArgument, senderClass, receiver, messageName);
+            return;
+        }
+
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            setFieldOnSuper(messageArgument, senderClass, it.next(), messageName);
+        }
+    }
+
+    //  --------------------------------------------------------
+    //              normal field handling : get
+    //  --------------------------------------------------------       
+
+    public static Object getField(Class senderClass, Object receiver, String messageName) throws Throwable {
+        try {
+            return InvokerHelper.getAttribute(receiver, messageName);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static Object getFieldSafe(Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (receiver == null) return null;
+        return getField(senderClass, receiver, messageName);
+    }
+
+    public static Object getFieldSpreadSafe(Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (receiver == null) return null;
+        if (!isSpreadable(receiver))
+            return getField(senderClass, receiver, messageName);
+
+        List list = (List) receiver;
+        List answer = new ArrayList();
+        for (Iterator it = list.iterator(); it.hasNext();) {
+            answer.add(getFieldSafe(senderClass, it.next(), messageName));
+        }
+        return answer;
+    }
+
+    //  --------------------------------------------------------
+    //              normal field handling : set
+    //  --------------------------------------------------------       
+
+    public static void setField(Object messageArgument, Class senderClass, Object receiver, String messageName) throws Throwable {
+        try {
+            InvokerHelper.setAttribute(receiver, messageName, messageArgument);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static void setFieldSafe(Object messageArgument, Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (receiver == null) return;
+        setField(messageArgument, senderClass, receiver, messageName);
+    }
+
+    public static void setFieldSpreadSafe(Object messageArgument, Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (receiver == null) return;
+        if (!isSpreadable(receiver)) {
+            setField(messageArgument, senderClass, receiver, messageName);
+            return;
+        }
+
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            setFieldSafe(messageArgument, senderClass, it.next(), messageName);
+        }
+    }
+
+    //  --------------------------------------------------------
+    //              normal GroovyObject field handling : get
+    //  --------------------------------------------------------       
+
+    public static Object getGroovyObjectField(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        return receiver.getMetaClass().getAttribute(receiver, messageName);
+    }
+
+    public static Object getGroovyObjectFieldSafe(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        if (receiver == null) return null;
+        return receiver.getMetaClass().getAttribute(receiver, messageName);
+    }
+
+    public static Object getGroovyObjectFieldSpreadSafe(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        if (receiver == null) return null;
+        if (!isSpreadable(receiver)) return getGroovyObjectField(senderClass, receiver, messageName);
+
+        List answer = new ArrayList();
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            answer.add(getFieldSafe(senderClass, it.next(), messageName));
+        }
+        return answer;
+    }
+
+    //  --------------------------------------------------------
+    //              normal field handling : set
+    //  --------------------------------------------------------       
+
+    public static void setGroovyObjectField(Object messageArgument, Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        receiver.getMetaClass().setAttribute(receiver, messageName, messageArgument);
+    }
+
+    public static void setGroovyObjectFieldSafe(Object messageArgument, Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        if (receiver == null) return;
+        receiver.getMetaClass().setAttribute(receiver, messageName, messageArgument);
+    }
+
+    public static void setGroovyObjectFieldSpreadSafe(Object messageArgument, Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        if (receiver == null) return;
+        if (!isSpreadable(receiver)) {
+            setGroovyObjectField(messageArgument, senderClass, receiver, messageName);
+            return;
+        }
+
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            setFieldSafe(messageArgument, senderClass, it.next(), messageName);
+        }
+    }
+
+    //  --------------------------------------------------------
+    //              Property handling super: get
+    //  --------------------------------------------------------       
+
+    public static Object getPropertyOnSuper(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        return invokeMethodOnSuperN(senderClass, receiver, "getProperty", new Object[]{messageName});
+    }
+
+    public static Object getPropertyOnSuperSafe(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        return getPropertyOnSuper(senderClass, receiver, messageName);
+    }
+
+    public static Object getPropertyOnSuperSpreadSafe(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        if (!isSpreadable(receiver))
+            return getPropertyOnSuper(senderClass, receiver, messageName);
+
+        List answer = new ArrayList();
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            answer.add(getPropertySafe(senderClass, it.next(), messageName));
+        }
+        return answer;
+    }
+
+    //  --------------------------------------------------------
+    //              Property handling super: set
+    //  --------------------------------------------------------       
+
+    public static void setPropertyOnSuper(Object messageArgument, Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        try {
+            InvokerHelper.setAttribute(receiver, messageName, messageArgument);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static void setPropertyOnSuperSafe(Object messageArgument, Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        setPropertyOnSuper(messageArgument, senderClass, receiver, messageName);
+    }
+
+    public static void setPropertyOnSuperSpreadSafe(Object messageArgument, Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        if (!isSpreadable(receiver)) {
+            setPropertyOnSuper(messageArgument, senderClass, receiver, messageName);
+            return;
+        }
+
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            setPropertySafe(messageArgument, senderClass, it.next(), messageName);
+        }
+    }
+
+    //  --------------------------------------------------------
+    //              normal Property handling : get
+    //  --------------------------------------------------------       
+
+    public static Object getProperty(Class senderClass, Object receiver, String messageName) throws Throwable {
+        try {
+            return InvokerHelper.getProperty(receiver, messageName);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static Object getPropertySafe(Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (receiver == null) return null;
+        return getProperty(senderClass, receiver, messageName);
+    }
+
+    public static Object getPropertySpreadSafe(Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (receiver == null) return null;
+        if (!isSpreadable(receiver))
+            return getProperty(senderClass, receiver, messageName);
+
+        List answer = new ArrayList();
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            answer.add(getPropertySafe(senderClass, it.next(), messageName));
+        }
+        return answer;
+    }
+
+    //  --------------------------------------------------------
+    //              normal Property handling : set
+    //  --------------------------------------------------------       
+
+    public static void setProperty(Object messageArgument, Class senderClass, Object receiver, String messageName) throws Throwable {
+        try {
+            InvokerHelper.setProperty(receiver, messageName, messageArgument);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static void setPropertySafe(Object messageArgument, Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (receiver == null) return;
+        setProperty(messageArgument, senderClass, receiver, messageName);
+    }
+
+    public static void setPropertySpreadSafe(Object messageArgument, Class senderClass, Object receiver, String messageName) throws Throwable {
+        if (receiver == null) return;
+        if (!isSpreadable(receiver)) {
+            setProperty(messageArgument, senderClass, receiver, messageName);
+            return;
+        }
+
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            setPropertySafe(messageArgument, senderClass, it.next(), messageName);
+        }
+    }
+
+    //  --------------------------------------------------------
+    //              normal GroovyObject Property handling : get
+    //  --------------------------------------------------------       
+
+    public static Object getGroovyObjectProperty(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        return receiver.getProperty(messageName);
+    }
+
+    public static Object getGroovyObjectPropertySafe(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        if (receiver == null) return null;
+        return getGroovyObjectProperty(senderClass, receiver, messageName);
+    }
+
+    public static Object getGroovyObjectPropertySpreadSafe(Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        if (receiver == null) return null;
+        if (!isSpreadable(receiver)) return getGroovyObjectProperty(senderClass, receiver, messageName);
+
+        List answer = new ArrayList();
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            answer.add(getPropertySafe(senderClass, it.next(), messageName));
+        }
+        return answer;
+    }
+
+    //  --------------------------------------------------------
+    //              normal GroovyObject Property handling : set
+    //  --------------------------------------------------------       
+
+    public static void setGroovyObjectProperty(Object messageArgument, Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        receiver.setProperty(messageName, messageArgument);
+    }
+
+    public static void setGroovyObjectPropertySafe(Object messageArgument, Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        if (receiver == null) return;
+        receiver.setProperty(messageName, messageArgument);
+    }
+
+    public static void setGroovyObjectPropertySpreadSafe(Object messageArgument, Class senderClass, GroovyObject receiver, String messageName) throws Throwable {
+        if (receiver == null) return;
+        if (!isSpreadable(receiver)) {
+            setProperty(messageArgument, senderClass, receiver, messageName);
+            return;
+        }
+
+        for (Iterator it = InvokerHelper.asIterator(receiver); it.hasNext();) {
+            setPropertySafe(messageArgument, senderClass, it.next(), messageName);
+        }
+    }
+
+    //  **********************************************************************************
+    //  **********************************************************************************
+    //  **************          methods not covered by the new MOP          **************
+    //  **********************************************************************************
+    //  **********************************************************************************
+
+    //  --------------------------------------------------------
+    //                     Closures
+    //  --------------------------------------------------------           
+
+    /**
+     * Returns the method pointer for the given object name
+     *
+     * @param object the object containing the method
+     * @param methodName the name of the method of interest
+     * @return the resulting Closure
+     */
+    public static Closure getMethodPointer(Object object, String methodName) {
+        return InvokerHelper.getMethodPointer(object, methodName);
+    }
+
+    // TODO: set sender class
+    public static Object invokeClosure(Object closure, Object[] arguments) throws Throwable {
+        return invokeMethodN(closure.getClass(), closure, "call", arguments);
+    }
+
+    //  --------------------------------------------------------
+    //                     type conversion
+    //  --------------------------------------------------------           
+
+    /**
+     * Provides a hook for type coercion of the given object to the required type
+     *
+     * @param type   of object to convert the given object to
+     * @param object the object to be converted
+     * @return the original object or a new converted value
+     * @throws Throwable if the coercion fails
+     */
+    public static Object asType(Object object, Class type) throws Throwable {
+        if (object == null) object = NullObject.getNullObject();
+        return invokeMethodN(object.getClass(), object, "asType", new Object[]{type});
+    }
+
+    /**
+     * Provides a hook for type casting of the given object to the required type
+     *
+     * @param type   of object to convert the given object to
+     * @param object the object to be converted
+     * @return the original object or a new converted value
+     * @throws Throwable if the type casting fails
+     */
+    public static Object castToType(Object object, Class type) throws Throwable {
+        try {
+            return DefaultTypeTransformation.castToType(object, type);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static Tuple createTuple(Object[] array) {
+        return new Tuple(array);
+    }
+
+    public static List createList(Object[] values) {
+        return InvokerHelper.createList(values);
+    }
+
+    public static Wrapper createPojoWrapper(Object val, Class clazz) {
+        return new PojoWrapper(val, clazz);
+    }
+
+    public static Wrapper createGroovyObjectWrapper(GroovyObject val, Class clazz) {
+        return new GroovyObjectWrapper(val, clazz);
+    }
+
+    public static Map createMap(Object[] values) {
+        return InvokerHelper.createMap(values);
+    }
+
+
+    //TODO: refactor
+    public static List createRange(Object from, Object to, boolean inclusive) throws Throwable {
+        if (!inclusive) {
+            if (compareEqual(from, to)) {
+                return new EmptyRange((Comparable) from);
+            }
+            if (compareGreaterThan(from, to)) {
+                to = invokeMethod0(ScriptBytecodeAdapter.class, to, "next");
+            } else {
+                to = invokeMethod0(ScriptBytecodeAdapter.class, to, "previous");
+            }
+        }
+        if (from instanceof Integer && to instanceof Integer) {
+            return new IntRange(DefaultTypeTransformation.intUnbox(from), DefaultTypeTransformation.intUnbox(to));
+        } else {
+            return new ObjectRange((Comparable) from, (Comparable) to);
+        }
+    }
+
+    //assert
+    public static void assertFailed(Object expression, Object message) {
+        InvokerHelper.assertFailed(expression, message);
+    }
+
+    //isCase
+    //TODO: set sender class
+    public static boolean isCase(Object switchValue, Object caseExpression) throws Throwable {
+        if (caseExpression == null) {
+            return switchValue == null;
+        }
+        return DefaultTypeTransformation.castToBoolean(invokeMethodN(caseExpression.getClass(), caseExpression, "isCase", new Object[]{switchValue}));
+    }
+
+    //compare
+    public static boolean compareIdentical(Object left, Object right) {
+        return left == right;
+    }
+
+    public static boolean compareEqual(Object left, Object right) {
+        return DefaultTypeTransformation.compareEqual(left, right);
+    }
+
+    public static boolean compareNotEqual(Object left, Object right) {
+        return !compareEqual(left, right);
+    }
+
+    public static Integer compareTo(Object left, Object right) {
+        int answer = DefaultTypeTransformation.compareTo(left, right);
+        if (answer == 0) {
+            return ZERO;
+        } else {
+            return answer > 0 ? ONE : MINUS_ONE;
+        }
+    }
+
+    public static boolean compareLessThan(Object left, Object right) {
+        return compareTo(left, right).intValue() < 0;
+    }
+
+    public static boolean compareLessThanEqual(Object left, Object right) {
+        return compareTo(left, right).intValue() <= 0;
+    }
+
+    public static boolean compareGreaterThan(Object left, Object right) {
+        return compareTo(left, right).intValue() > 0;
+    }
+
+    public static boolean compareGreaterThanEqual(Object left, Object right) {
+        return compareTo(left, right).intValue() >= 0;
+    }
+
+    //regexpr
+    public static Pattern regexPattern(Object regex) {
+        return DefaultGroovyMethods.bitwiseNegate(regex.toString());
+    }
+
+    public static Matcher findRegex(Object left, Object right) throws Throwable {
+        try {
+            return InvokerHelper.findRegex(left, right);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static boolean matchRegex(Object left, Object right) {
+        return InvokerHelper.matchRegex(left, right);
+    }
+
+
+    //spread expressions
+    public static Object[] despreadList(Object[] args, Object[] spreads, int[] positions) {
+        List ret = new ArrayList();
+        int argsPos = 0;
+        int spreadPos = 0;
+        for (int pos = 0; pos < positions.length; pos++) {
+            for (; argsPos < positions[pos]; argsPos++) {
+                ret.add(args[argsPos]);
+            }
+            Object value = spreads[spreadPos];
+            if (value == null) {
+                ret.add(null);
+            } else if (value instanceof List) {
+                ret.addAll((List) value);
+            } else if (value.getClass().isArray()) {
+                ret.addAll(DefaultTypeTransformation.primitiveArrayToList(value));
+            } else {
+                throw new IllegalArgumentException("cannot spread the type " + value.getClass().getName() + " with value " + value);
+            }
+            spreadPos++;
+        }
+        for (; argsPos < args.length; argsPos++) {
+            ret.add(args[argsPos]);
+        }
+        return ret.toArray();
+    }
+
+    public static Object spreadMap(Object value) {
+        return InvokerHelper.spreadMap(value);
+    }
+
+    public static Object unaryMinus(Object value) throws Throwable {
+        try {
+            return InvokerHelper.unaryMinus(value);
+        } catch (GroovyRuntimeException gre) {
+            throw unwrap(gre);
+        }
+    }
+
+    public static Object unaryPlus(Object value) {
+        return InvokerHelper.unaryPlus(value);
+    }
+
+    public static Object bitwiseNegate(Object value) {
+        return InvokerHelper.bitwiseNegate(value);
+    }
+
+    public static MetaClass initMetaClass(Object object) {
+        return InvokerHelper.getMetaClass(object);
+    }
+
+    private static MetaClass getMetaClassObjectNotNull(Object object) {
+        if (!(object instanceof GroovyObject)) {
+            return initMetaClass(object);
+        } else {
+            return ((GroovyObject) object).getMetaClass();
+        }
+    }
+
+    private static boolean isSpreadable (Object receiver) {
+        return (receiver instanceof Collection)
+          || (receiver instanceof Iterator)
+          || receiver.getClass().isArray();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/ScriptReference.java b/groovy/src/main/org/codehaus/groovy/runtime/ScriptReference.java
new file mode 100644
index 0000000..3506b08
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/ScriptReference.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Reference;
+import groovy.lang.Script;
+
+/**
+ * Represents a reference to a variable in a script
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ScriptReference extends Reference {
+
+    private Script script;
+    private String variable;
+
+    public ScriptReference(Script script, String variable) {
+        this.script = script;
+        this.variable = variable;
+    }
+
+    public Object get() {
+        return script.getBinding().getVariable(variable);
+    }
+
+    public void set(Object value) {
+        script.getBinding().setVariable(variable, value);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/ScriptTestAdapter.java b/groovy/src/main/org/codehaus/groovy/runtime/ScriptTestAdapter.java
new file mode 100644
index 0000000..88843bd
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/ScriptTestAdapter.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+/**
+ * An adapter to make any Groovy Script class an instance of a JUnit Test
+ *
+ * @version $Revision$
+ */
+public class ScriptTestAdapter implements Test {
+    private Class scriptClass;
+    private String[] arguments;
+
+    public ScriptTestAdapter(Class scriptClass, String[] arguments) {
+        this.scriptClass = scriptClass;
+        this.arguments = arguments;
+    }
+
+    public int countTestCases() {
+        return 1;
+    }
+
+    public void run(TestResult result) {
+        try {
+            result.startTest(this);
+
+            // lets run the script
+            InvokerHelper.runScript(scriptClass, arguments);
+            result.endTest(this);
+        }
+        catch (Exception e) {
+            result.addError(this, e);
+        }
+    }
+
+    public String toString() {
+        return "TestCase for script: " + scriptClass.getName();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/StackTraceUtils.java b/groovy/src/main/org/codehaus/groovy/runtime/StackTraceUtils.java
new file mode 100644
index 0000000..427d9dc
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/StackTraceUtils.java
@@ -0,0 +1,155 @@
+/* Copyright 2004-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT c;pWARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.runtime;

+

+import java.io.PrintWriter;

+import java.util.ArrayList;

+import java.util.Enumeration;

+import java.util.List;

+import java.util.logging.Level;

+import java.util.logging.LogManager;

+import java.util.logging.Logger;

+

+/**

+ * Originally was grails.utils.GrailsUtils, removed some grails specific stuff.

+ * Utility methods removing internal lines from stack traces

+ *

+ * @author Graeme Rocher

+ * @since 0.2

+ *

+ * @version $Revision: 5544 $

+ * First Created: 02-Jun-2006

+ * Last Updated: $Date: 2007-09-21 13:53:07 -0500 (Fri, 21 Sep 2007) $

+ */

+public class StackTraceUtils {

+

+    public static final String STACK_LOG_NAME = "StackTrace";

+    private static final Logger STACK_LOG;

+    // set log to consume traces by default, end user can override later

+    static {

+        outer: do {

+            Enumeration existingLogs = LogManager.getLogManager().getLoggerNames();

+            while (existingLogs.hasMoreElements()) {

+                if (STACK_LOG_NAME.equals(existingLogs.nextElement())) {

+                    STACK_LOG = Logger.getLogger(STACK_LOG_NAME);

+                    break outer;

+                }

+            }

+            STACK_LOG = Logger.getLogger(STACK_LOG_NAME);

+            STACK_LOG.setUseParentHandlers(false);

+        } while (false);

+    }

+

+    private static final String[] GROOVY_PACKAGES =

+            System.getProperty("groovy.sanitized.stacktraces",

+                "groovy.," +

+                "org.codehaus.groovy.," +

+                "java.," +

+                "javax.," +

+                "sun.," +

+                "gjdk.groovy.,"

+            ).split("(\\s|,)+");

+

+    /**

+     * <p>Remove all apparently groovy-internal trace entries from the exception instance<p>

+     * <p>This modifies the original instance and returns it, it does not clone</p>

+     * @param t

+     * @return The exception passed in, after cleaning the stack trace

+     */

+    public static Throwable sanitize(Throwable t) {

+        // Note that this getBoolean access may well be synced...

+        if (!Boolean.getBoolean("groovy.full.stacktrace")) {

+            StackTraceElement[] trace = t.getStackTrace();

+            List newTrace = new ArrayList();

+            for (int i = 0; i < trace.length; i++) {

+                StackTraceElement stackTraceElement = trace[i];

+                if (isApplicationClass(stackTraceElement.getClassName())) {

+                    newTrace.add( stackTraceElement);

+                }

+            }

+            // We don't want to lose anything, so log it

+  	        STACK_LOG.log(Level.WARNING, "Sanitizing stacktrace:", t);

+

+

+            StackTraceElement[] clean = new StackTraceElement[newTrace.size()];

+            newTrace.toArray(clean);

+            t.setStackTrace(clean);

+        }

+        return t;

+    }

+

+    public static void printSanitizedStackTrace(Throwable t, PrintWriter p) {

+        t = StackTraceUtils.sanitize(t);

+

+        StackTraceElement[] trace = t.getStackTrace();

+        for (int i = 0; i < trace.length; i++) {

+            StackTraceElement stackTraceElement = trace[i];

+            p.println(  "at "+stackTraceElement.getClassName()

+                        +"("+stackTraceElement.getMethodName()

+                        +":"+stackTraceElement.getLineNumber()+")");

+        }

+    }

+

+    public static void printSanitizedStackTrace(Throwable t) {

+        printSanitizedStackTrace(t, new PrintWriter(System.err));

+    }

+

+    public static boolean isApplicationClass(String className) {

+        for (int i = 0; i < GROOVY_PACKAGES.length; i++) {

+            String groovyPackage = GROOVY_PACKAGES[i];

+            if (className.startsWith(groovyPackage)) {

+                return false;

+            }

+        }

+        return true;

+    }

+

+    /**

+     * <p>Extracts the root cause of the exception, no matter how nested it is</p>

+     * @param t

+     * @return The deepest cause of the exception that can be found

+     */

+    public static Throwable extractRootCause(Throwable t) {

+        Throwable result = t;

+        while (result.getCause() != null) {

+            result = result.getCause();

+        }

+        return result;

+    }

+

+    /**

+     * <p>Get the root cause of an exception and sanitize it for display to the user</p>

+     * <p>This will MODIFY the stacktrace of the root cause exception object and return it</p>

+     * @param t

+     * @return The root cause exception instance, with its stace trace modified to filter out groovy runtime classes

+     */

+    public static Throwable sanitizeRootCause(Throwable t) {

+        return StackTraceUtils.sanitize(StackTraceUtils.extractRootCause(t));

+    }

+

+    /**

+     * <p>Sanitize the exception and ALL nested causes</p>

+     * <p>This will MODIFY the stacktrace of the exception instance and all its causes irreversibly</p>

+     * @param t

+     * @return The root cause exception instances, with stack trace modified to filter out groovy runtime classes

+     */

+    public static Throwable deepSanitize(Throwable t) {

+        Throwable current = t;

+        while (current.getCause() != null) {

+            current = StackTraceUtils.sanitize(current.getCause());

+        }

+        return StackTraceUtils.sanitize(t);

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/runtime/StringBufferWriter.java b/groovy/src/main/org/codehaus/groovy/runtime/StringBufferWriter.java
new file mode 100644
index 0000000..1ec9bdb
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/StringBufferWriter.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * This class codes around a silly limiation of StringWriter which doesn't allow a StringBuffer
+ * to be passed in as a constructor for some bizzare reason.
+ * So we replicate the behaviour of StringWriter here but allow a StringBuffer to be passed in.
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class StringBufferWriter extends Writer {
+
+    private StringBuffer buffer;
+
+    /**
+     * Create a new string writer which will append the text to the given StringBuffer
+     */
+    public StringBufferWriter(StringBuffer buffer) {
+        this.buffer = buffer;
+    }
+
+    /**
+     * Write a single character.
+     */
+    public void write(int c) {
+        buffer.append((char) c);
+    }
+
+    /**
+     * Write a portion of an array of characters.
+     *
+     * @param text Array of characters
+     * @param offset Offset from which to start writing characters
+     * @param length Number of characters to write
+     */
+    public void write(char text[], int offset, int length) {
+        if ((offset < 0) || (offset > text.length) || (length < 0) || ((offset + length) > text.length) || ((offset + length) < 0)) {
+            throw new IndexOutOfBoundsException();
+        }
+        else if (length == 0) {
+            return;
+        }
+        buffer.append(text, offset, length);
+    }
+
+    /**
+     * Write a string.
+     */
+    public void write(String text) {
+        buffer.append(text);
+    }
+
+    /**
+     * Write a portion of a string.
+     *
+     * @param text the text to be written
+     * @param offset offset from which to start writing characters
+     * @param length Number of characters to write
+     */
+    public void write(String text, int offset, int length) {
+        buffer.append(text.substring(offset, offset + length));
+    }
+
+    /**
+     * Return the buffer's current value as a string.
+     */
+    public String toString() {
+        return buffer.toString();
+    }
+
+    /**
+     * Flush the stream.
+     */
+    public void flush() {
+    }
+
+    /**
+     * Closing a <tt>StringWriter</tt> has no effect. The methods in this
+     * class can be called after the stream has been closed without generating
+     * an <tt>IOException</tt>.
+     */
+    public void close() throws IOException {
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/TimeCategory.java b/groovy/src/main/org/codehaus/groovy/runtime/TimeCategory.java
new file mode 100644
index 0000000..142c623
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/TimeCategory.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime;
+
+import groovy.time.BaseDuration;
+import groovy.time.DatumDependentDuration;
+import groovy.time.Duration;
+import groovy.time.TimeDuration;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+public class TimeCategory {
+    /*
+     * Methods to allow Date Duration arithmetic
+     */
+
+    public static Date plus(final Date date, final BaseDuration duration) {
+        return duration.plus(date);
+    }
+
+    public static Date minus(final Date date, final BaseDuration duration) {
+        final Calendar cal = Calendar.getInstance();
+
+        cal.setTime(date);
+        cal.add(Calendar.YEAR, -duration.getYears());
+        cal.add(Calendar.MONTH, -duration.getMonths());
+        cal.add(Calendar.DAY_OF_YEAR, -duration.getDays());
+        cal.add(Calendar.HOUR_OF_DAY, -duration.getHours());
+        cal.add(Calendar.MINUTE, -duration.getMinutes());
+        cal.add(Calendar.SECOND, -duration.getSeconds());
+        cal.add(Calendar.MILLISECOND, -duration.getMillis());
+
+        return cal.getTime();
+    }
+
+    public static TimeZone getTimeZone(Date self) {
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(self);
+        return calendar.getTimeZone();
+    }
+
+    public static Duration getDaylightSavingsOffset(Date self) {
+        TimeZone timeZone = getTimeZone(self);
+        int millis = (timeZone.useDaylightTime() && timeZone.inDaylightTime(self))
+                ? timeZone.getDSTSavings() : 0;
+        return new TimeDuration(0, 0, 0, millis);
+    }
+
+    public static Duration getDaylightSavingsOffset(BaseDuration self) {
+        return getDaylightSavingsOffset(new Date(self.toMilliseconds() + 1));
+    }
+
+    public static Duration getRelativeDaylightSavingsOffset(Date self, Date other) {
+        Duration d1 = getDaylightSavingsOffset(self);
+        Duration d2 = getDaylightSavingsOffset(other);
+        return new TimeDuration(0, 0, 0, (int) (d2.toMilliseconds() - d1.toMilliseconds()));
+    }
+
+    public static TimeDuration minus(final Date lhs, final Date rhs) {
+        long milliseconds = lhs.getTime() - rhs.getTime();
+        long days = milliseconds / (24 * 60 * 60 * 1000);
+        milliseconds -= days * 24 * 60 * 60 * 1000;
+        int hours = (int) (milliseconds / (60 * 60 * 1000));
+        milliseconds -= hours * 60 * 60 * 1000;
+        int minutes = (int) (milliseconds / (60 * 1000));
+        milliseconds -= minutes * 60 * 1000;
+        int seconds = (int) (milliseconds / 1000);
+        milliseconds -= seconds * 1000;
+
+        return new TimeDuration((int) days, hours, minutes, seconds, (int) milliseconds);
+    }
+
+    /*
+    * Methods on Integer to implement 1.month, 4.years etc.
+    */
+
+    public static DatumDependentDuration getMonths(final Integer self) {
+        return new DatumDependentDuration(0, self.intValue(), 0, 0, 0, 0, 0);
+    }
+
+    public static DatumDependentDuration getMonth(final Integer self) {
+        return getMonths(self);
+    }
+
+    public static DatumDependentDuration getYears(final Integer self) {
+        return new DatumDependentDuration(self.intValue(), 0, 0, 0, 0, 0, 0);
+    }
+
+    public static DatumDependentDuration getYear(final Integer self) {
+        return getYears(self);
+    }
+
+    /*
+    * Methods on Integer to implement 1.week, 4.days etc.
+    */
+
+    public static Duration getWeeks(final Integer self) {
+        return new Duration(self.intValue() * 7, 0, 0, 0, 0);
+    }
+
+    public static Duration getWeek(final Integer self) {
+        return getWeeks(self);
+    }
+
+    public static Duration getDays(final Integer self) {
+        return new Duration(self.intValue(), 0, 0, 0, 0);
+    }
+
+    public static Duration getDay(final Integer self) {
+        return getDays(self);
+    }
+
+    public static TimeDuration getHours(final Integer self) {
+        return new TimeDuration(0, self.intValue(), 0, 0, 0);
+    }
+
+    public static TimeDuration getHour(final Integer self) {
+        return getHours(self);
+    }
+
+    public static TimeDuration getMinutes(final Integer self) {
+        return new TimeDuration(0, 0, self.intValue(), 0, 0);
+    }
+
+    public static TimeDuration getMinute(final Integer self) {
+        return getMinutes(self);
+    }
+
+    public static TimeDuration getSeconds(final Integer self) {
+        return new TimeDuration(0, 0, 0, self.intValue(), 0);
+    }
+
+    public static TimeDuration getSecond(final Integer self) {
+        return getSeconds(self);
+    }
+
+    public static TimeDuration getMilliseconds(final Integer self) {
+        return new TimeDuration(0, 0, 0, 0, self.intValue());
+    }
+
+    public static TimeDuration getMillisecond(final Integer self) {
+        return getMilliseconds(self);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/WritableFile.java b/groovy/src/main/org/codehaus/groovy/runtime/WritableFile.java
new file mode 100644
index 0000000..2b225a2
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/WritableFile.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Writable;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+
+/**
+ * A Writable File.
+ *
+ * @author John Wilson
+ *
+ */
+public class WritableFile extends File implements Writable {
+    private final String encoding;
+
+    public WritableFile(final File delegate) {
+        this(delegate, null);
+    }
+
+    public WritableFile(final File delegate, final String encoding) {
+        super(delegate.toURI());
+        this.encoding = encoding;
+    }
+
+    public Writer writeTo(final Writer out) throws IOException {
+        final Reader reader =
+            (this.encoding == null)
+                ? DefaultGroovyMethods.newReader(this)
+                : DefaultGroovyMethods.newReader(this, this.encoding);
+
+        try {
+            int c = reader.read();
+
+            while (c != -1) {
+                out.write(c);
+                c = reader.read();
+            }
+        }
+        finally {
+            reader.close();
+        }
+        return out;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
new file mode 100644
index 0000000..e4e0536
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ClosureMetaClass.java
@@ -0,0 +1,654 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.runtime.metaclass;

+

+import groovy.lang.*;

+import org.codehaus.groovy.reflection.CachedClass;

+import org.codehaus.groovy.reflection.CachedField;

+import org.codehaus.groovy.reflection.CachedMethod;

+import org.codehaus.groovy.reflection.FastArray;

+import org.codehaus.groovy.runtime.InvokerHelper;

+import org.codehaus.groovy.runtime.MetaClassHelper;

+import org.codehaus.groovy.runtime.wrappers.Wrapper;

+

+import java.lang.reflect.Constructor;

+import java.lang.reflect.Method;

+import java.util.*;

+import java.util.logging.Level;

+

+/**

+ * A Metaclass for closures generated by the Groovy compiler. These classes

+ * have special characteristics this MetaClass uses. One of these is that a

+ * generated Closure has only additional doCall methods, all other methods

+ * are in the Closure class as well. To use this fact this MetaClass uses

+ * a MetaClass for Closure as static field And delegates calls to this

+ * MetaClass if needed. This allows a lean implementation for this MetaClass.

+ * Multiple generated closures will then use the same MetaClass for Closure.

+ * For static dispatching this class uses the MetaClass of Class, again

+ * all isntances of this class will share that MetaClass. The Class MetaClass

+ * is initialized lazy, because most operations do not need this MetaClass.

+ * <p/>

+ * The Closure and Class MetaClasses are not replaceable.

+ * <p/>

+ * This MetaClass is for internal usage only!

+ *

+ * @author Jochen Theodorou

+ * @since 1.1

+ */

+public final class ClosureMetaClass extends MetaClassImpl {

+    private boolean initialized;

+    private final FastArray closureMethods = new FastArray(3);

+    private Map attributes = new HashMap();

+    private MethodChooser chooser;

+    private volatile boolean attributeInitDone = false;

+

+    private static final MetaClassImpl CLOSURE_METACLASS;

+    private static MetaClassImpl classMetaClass;

+    private static final Object[] EMPTY_ARGUMENTS = {};

+    private static final String CLOSURE_CALL_METHOD = "call";

+    private static final String CLOSURE_DO_CALL_METHOD = "doCall";

+    private static final String CLOSURE_CURRY_METHOD = "curry";

+

+    static {

+        CLOSURE_METACLASS = new MetaClassImpl(Closure.class);

+        CLOSURE_METACLASS.initialize();

+    }

+

+    private static synchronized MetaClass getStaticMetaClass() {

+        if (classMetaClass == null) {

+            classMetaClass = new MetaClassImpl(Class.class);

+            classMetaClass.initialize();

+        }

+        return classMetaClass;

+    }

+

+    private static interface MethodChooser {

+        Object chooseMethod(Class[] arguments, boolean coerce);

+    }

+

+    private static class StandardClosureChooser implements MethodChooser {

+        private final MetaMethod doCall0;

+        private final MetaMethod doCall1;

+

+        StandardClosureChooser(MetaMethod m0, MetaMethod m1) {

+            doCall0 = m0;

+            doCall1 = m1;

+        }

+

+        public Object chooseMethod(Class[] arguments, boolean coerce) {

+            if (arguments.length == 0) return doCall0;

+            if (arguments.length == 1) return doCall1;

+            return null;

+        }

+    }

+

+    private static class NormalMethodChooser implements MethodChooser {

+        private final FastArray methods;

+        final Class theClass;

+

+        NormalMethodChooser(Class theClass, FastArray methods) {

+            this.theClass = theClass;

+            this.methods = methods;

+        }

+

+        public Object chooseMethod(Class[] arguments, boolean coerce) {

+            if (arguments.length == 0) {

+                return MetaClassHelper.chooseEmptyMethodParams(methods);

+            } else if (arguments.length == 1 && arguments[0] == null) {

+                return MetaClassHelper.chooseMostGeneralMethodWith1NullParam(methods);

+            } else {

+                List matchingMethods = new ArrayList();

+

+                final int len = methods.size();

+                final Object[] data = methods.getArray();

+                for (int i = 0; i != len; ++i) {

+                    Object method = data[i];

+

+                    // making this false helps find matches

+                    if (MetaClassHelper.isValidMethod(method, arguments, coerce)) {

+                        matchingMethods.add(method);

+                    }

+                }

+                if (matchingMethods.isEmpty()) {

+                    return null;

+                } else if (matchingMethods.size() == 1) {

+                    return matchingMethods.get(0);

+                }

+                return chooseMostSpecificParams(CLOSURE_DO_CALL_METHOD, matchingMethods, arguments);

+            }

+        }

+

+        private Object chooseMostSpecificParams(String name, List matchingMethods, Class[] arguments) {

+            long matchesDistance = -1;

+            LinkedList matches = new LinkedList();

+            for (Iterator iter = matchingMethods.iterator(); iter.hasNext();) {

+                Object method = iter.next();

+                Class[] paramTypes = MetaClassHelper.getParameterTypes(method).getNativeParameterTypes();

+                if (!MetaClassHelper.parametersAreCompatible(arguments, paramTypes)) continue;

+                long dist = MetaClassHelper.calculateParameterDistance(arguments, paramTypes);

+                if (dist == 0) return method;

+                if (matches.isEmpty()) {

+                    matches.add(method);

+                    matchesDistance = dist;

+                } else if (dist < matchesDistance) {

+                    matchesDistance = dist;

+                    matches.clear();

+                    matches.add(method);

+                } else if (dist == matchesDistance) {

+                    matches.add(method);

+                }

+

+            }

+            if (matches.size() == 1) {

+                return matches.getFirst();

+            }

+            if (matches.isEmpty()) {

+                return null;

+            }

+

+            //more than one matching method found --> ambigous!

+            String msg = "Ambiguous method overloading for method ";

+            msg += theClass.getName() + "#" + name;

+            msg += ".\nCannot resolve which method to invoke for ";

+            msg += InvokerHelper.toString(arguments);

+            msg += " due to overlapping prototypes between:";

+            for (Iterator iter = matches.iterator(); iter.hasNext();) {

+                CachedClass[] types = MetaClassHelper.getParameterTypes(iter.next()).getParameterTypes();

+                msg += "\n\t" + InvokerHelper.toString(types);

+            }

+            throw new GroovyRuntimeException(msg);

+        }

+    }

+

+

+    public ClosureMetaClass(MetaClassRegistry registry, Class theClass) {

+        super(registry, theClass);

+    }

+

+    public MetaProperty getMetaProperty(String name) {

+        return CLOSURE_METACLASS.getMetaProperty(name);

+    }

+

+    private void unwrap(Object[] arguments) {

+        for (int i = 0; i != arguments.length; i++) {

+            if (arguments[i] instanceof Wrapper) {

+                arguments[i] = ((Wrapper) arguments[i]).unwrap();

+            }

+        }

+    }

+

+    private MetaMethod pickClosureMethod(Class[] argClasses) {

+        Object answer = chooser.chooseMethod(argClasses, false);

+        return (MetaMethod) answer;

+    }

+

+    private MetaMethod getDelegateMethod(Closure closure, Object delegate, String methodName, Class[] argClasses) {

+        if (delegate == closure || delegate == null) return null;

+        MetaClass delegateMetaClass;

+        if (delegate instanceof Class) {

+            delegateMetaClass = registry.getMetaClass((Class)delegate);

+            return delegateMetaClass.getStaticMetaMethod(methodName, argClasses);

+        }

+        else {

+            delegateMetaClass = lookupObjectMetaClass(delegate);

+            return delegateMetaClass.pickMethod(methodName, argClasses);

+        }

+    }

+

+    public Object invokeMethod(Class sender, Object object, String methodName, Object[] originalArguments, boolean isCallToSuper, boolean fromInsideClass) {

+        checkInitalised();

+        if (object == null) {

+            throw new NullPointerException("Cannot invoke method: " + methodName + " on null object");

+        }

+        if (LOG.isLoggable(Level.FINER)) {

+            MetaClassHelper.logMethodCall(object, methodName, originalArguments);

+        }

+

+        final Object[] arguments = makeArguments(originalArguments, methodName); 

+        final Class[] argClasses = MetaClassHelper.convertToTypeArray(arguments);

+        unwrap(arguments);

+

+        MetaMethod method;

+        final Closure closure = (Closure) object;

+

+        if (CLOSURE_DO_CALL_METHOD.equals(methodName) || CLOSURE_CALL_METHOD.equals(methodName)) {

+            method = pickClosureMethod(argClasses);

+            if (method==null && arguments.length==1 && arguments[0] instanceof List) {

+                Object[] newArguments = ((List) arguments[0]).toArray();

+                Class[] newArgClasses = MetaClassHelper.convertToTypeArray(newArguments);

+                method = pickClosureMethod(newArgClasses);

+                if (method!=null) {

+                    method = new TransformMetaMethod(method) {

+                        public Object invoke(Object object, Object[] arguments) {

+                            Object firstArgument = arguments[0];

+                            List list = (List) firstArgument;

+                            arguments = list.toArray();

+                            return super.invoke(object, arguments);

+                        }

+                    };

+                }

+            }

+            if (method==null) throw new MissingMethodException(methodName, theClass, arguments, false);

+        } else if (CLOSURE_CURRY_METHOD.equals(methodName)) {

+            return closure.curry(arguments);

+        } else {

+            method = CLOSURE_METACLASS.pickMethod(methodName, argClasses);

+        }

+ 

+        if (method != null) return MetaClassHelper.doMethodInvoke(object, method, arguments);

+

+        MissingMethodException last = null;

+        Object callObject = object;

+        if (method == null) {

+            final Object owner = closure.getOwner();

+            final Object delegate = closure.getDelegate();

+            final Object thisObject = closure.getThisObject();

+            final int resolveStrategy = closure.getResolveStrategy();

+            boolean invokeOnDelegate = false;

+            boolean invokeOnOwner = false;

+            boolean ownerFirst = true;

+

+            switch (resolveStrategy) {

+                case Closure.TO_SELF:

+                    break;

+                case Closure.DELEGATE_ONLY:

+                    method = getDelegateMethod(closure, delegate, methodName, argClasses);

+                    callObject = delegate;

+                    if (method == null) {

+                        invokeOnDelegate = delegate != closure && (delegate instanceof GroovyObject);

+                    }

+                    break;

+                case Closure.OWNER_ONLY:

+                    method = getDelegateMethod(closure, owner, methodName, argClasses);

+                    callObject = owner;

+                    if (method == null) {

+                        invokeOnOwner = owner != closure && (owner instanceof GroovyObject);

+                    }

+

+                    break;

+                case Closure.DELEGATE_FIRST:

+                    method = getDelegateMethod(closure, delegate, methodName, argClasses);

+                    callObject = delegate;

+                    if (method == null) {

+                        method = getDelegateMethod(closure, owner, methodName, argClasses);

+                        callObject = owner;

+                    }

+                    if (method == null) {

+                        invokeOnDelegate = delegate != closure && (delegate instanceof GroovyObject);

+                        invokeOnOwner = owner != closure && (owner instanceof GroovyObject);

+                        ownerFirst = false;

+                    }

+                    break;

+                default: // owner first

+                    // owner first means we start with the outer most owner that is not a generated closure

+                    // this owner is equal to the this object, so we check that one first.                    

+                    method = getDelegateMethod(closure, thisObject, methodName, argClasses);

+                    callObject = thisObject;

+                    if (method == null) {

+                        //try finding a delegate that has that method... we start from 

+                        // outside building a stack and try each delegate

+                        LinkedList list = new LinkedList();

+                        for (Object current = closure; current!=thisObject; ) {

+                            Closure currentClosure = (Closure) current; 

+                            if (currentClosure.getDelegate()!=null) list.add(current);

+                            current=currentClosure.getOwner();

+                        }

+

+                        while (!list.isEmpty() && method==null) {

+                            Closure closureWithDelegate = (Closure) list.removeLast();

+                            Object currentDelegate = closureWithDelegate.getDelegate();

+                            method = getDelegateMethod(closureWithDelegate,currentDelegate,methodName,argClasses);

+                            callObject = currentDelegate;

+                        }

+                    }

+                    if (method == null) {

+                        invokeOnDelegate = delegate != closure && (delegate instanceof GroovyObject);

+                        invokeOnOwner = owner != closure && (owner instanceof GroovyObject);

+                    }

+            }

+            if (method == null && (invokeOnOwner || invokeOnDelegate)) {

+                try {

+                    if (ownerFirst) {

+                        return invokeOnDelegationObjects(invokeOnOwner, owner, invokeOnDelegate, delegate, methodName, arguments);

+                    } else {

+                        return invokeOnDelegationObjects(invokeOnDelegate, delegate, invokeOnOwner, owner, methodName, arguments);

+                    }

+                } catch (MissingMethodException mme) {

+                    last = mme;

+                }

+            }

+        }

+

+        if (method != null) {

+            return MetaClassHelper.doMethodInvoke(callObject, method, arguments);

+        } else {

+            // if no method was found, try to find a closure defined as a field of the class and run it

+            Object value = null;

+            try {

+                value = this.getProperty(object, methodName);

+            } catch (MissingPropertyException mpe) {

+                // ignore

+            }

+            if (value instanceof Closure) {  // This test ensures that value != this If you ever change this ensure that value != this

+                Closure cl = (Closure) value;

+                MetaClass delegateMetaClass = cl.getMetaClass();

+                return delegateMetaClass.invokeMethod(cl.getClass(), closure, CLOSURE_DO_CALL_METHOD, originalArguments, false, fromInsideClass);

+            }

+        }

+

+        if (last != null) throw last;

+        throw new MissingMethodException(methodName, theClass, arguments, false);

+    }

+

+    private Object[] makeArguments(Object[] arguments, String methodName) {

+        if (arguments == null) return EMPTY_ARGUMENTS;

+        if (CLOSURE_CALL_METHOD.equals(methodName)) {

+            if (arguments.length==1 && arguments[0] instanceof Object[]) {

+                return (Object[]) arguments[0];

+            }

+        }

+        return arguments;

+    }

+

+    private Object invokeOnDelegationObjects(

+            boolean invoke1, Object o1,

+            boolean invoke2, Object o2,

+            String methodName, Object[] args) {

+        MissingMethodException first = null;

+        if (invoke1) {

+            GroovyObject go = (GroovyObject) o1;

+            try {

+                return go.invokeMethod(methodName, args);

+            } catch (MissingMethodException mme) {

+                first = mme;

+            }

+        }

+        if (invoke2) {

+            GroovyObject go = (GroovyObject) o2;

+            try {

+                return go.invokeMethod(methodName, args);

+            } catch (MissingMethodException mme) {

+                if (first == null) first = mme;

+            }

+        }

+        throw first;

+    }

+

+    private synchronized void initAttributes() {

+        if (!attributes.isEmpty()) return;

+        attributes.put("!", null); // just a dummy for later

+        CachedField[] fieldArray = theCachedClass.getFields();

+        for (int i = 0; i < fieldArray.length; i++) {

+            MetaFieldProperty mfp = MetaFieldProperty.create(fieldArray[i]);

+            attributes.put(fieldArray[i].getName(), mfp);

+        }

+        attributeInitDone = !attributes.isEmpty();

+    }

+

+    public synchronized void initialize() {

+        if (!isInitialized()) {

+            CachedMethod[] methodArray = theCachedClass.getMethods();

+            synchronized (theCachedClass) {

+                for (int i = 0; i < methodArray.length; i++) {

+                    final CachedMethod cachedMethod = methodArray[i];

+                    Method reflectionMethod = cachedMethod.cachedMethod;

+                    if (!reflectionMethod.getName().equals(CLOSURE_DO_CALL_METHOD)) continue;

+                    MetaMethod method = cachedMethod.getReflectionMetaMethod();

+                    closureMethods.add(method);

+                }

+            }

+            assignMethodChooser();

+

+            initialized = true;

+        }

+    }

+

+    private void assignMethodChooser() {

+        if (closureMethods.size() == 1) {

+            final MetaMethod doCall = (MetaMethod) closureMethods.get(0);

+            final CachedClass[] c = doCall.getParameterTypes();

+            int length = c.length;

+            if (length == 0) {

+                // no arg method

+                chooser = new MethodChooser() {

+                    public Object chooseMethod(Class[] arguments, boolean coerce) {

+                        if (arguments.length == 0) return doCall;

+                        return null;

+                    }

+                };

+            } else {

+                if (length == 1 && c[0].getCachedClass() == Object.class) {

+                    // Object fits all, so simple dispatch rule here

+                    chooser = new MethodChooser() {

+                        public Object chooseMethod(Class[] arguments, boolean coerce) {

+                            // <2, because foo() is same as foo(null)

+                            if (arguments.length < 2) return doCall;

+                            return null;

+                        }

+                    };

+                } else {

+                    boolean allObject = true;

+                    for (int i = 0; i < c.length - 1; i++) {

+                        if (c[i].getCachedClass() != Object.class) {

+                            allObject = false;

+                            break;

+                        }

+                    }

+                    if (allObject && c[c.length - 1].getCachedClass() == Object.class) {

+                        // all arguments are object, so test only if argument number is correct

+                        chooser = new MethodChooser() {

+                            public Object chooseMethod(Class[] arguments, boolean coerce) {

+                                if (arguments.length == c.length) return doCall;

+                                return null;

+                            }

+                        };

+                    } else {

+                        if (allObject && c[c.length - 1].getCachedClass() == Object[].class) {

+                            // all arguments are Object but last, which is a vargs argument, that

+                            // will fit all, so jsut test if the number of argument is equal or

+                            // more than the parameters we have.

+                            final int minimumLength = c.length - 2;

+                            chooser = new MethodChooser() {

+                                public Object chooseMethod(Class[] arguments, boolean coerce) {

+                                    if (arguments.length > minimumLength) return doCall;

+                                    return null;

+                                }

+                            };

+                        } else {

+                            // general case for single method

+                            chooser = new MethodChooser() {

+                                public Object chooseMethod(Class[] arguments, boolean coerce) {

+                                    if (MetaClassHelper.isValidMethod(doCall, arguments, coerce)) {

+                                        return doCall;

+                                    }

+                                    return null;

+                                }

+                            };

+                        }

+                    }

+                }

+            }

+        } else if (closureMethods.size() == 2) {

+            MetaMethod m0 = null, m1 = null;

+            for (int i = 0; i != closureMethods.size(); ++i) {

+                MetaMethod m = (MetaMethod) closureMethods.get(i);

+                CachedClass[] c = m.getParameterTypes();

+                if (c.length == 0) {

+                    m0 = m;

+                } else {

+                    if (c.length == 1 && c[0].getCachedClass() == Object.class) {

+                        m1 = m;

+                    }

+                }

+            }

+            if (m0 != null && m1 != null) {

+                // standard closure (2 methods because "it" is with default null)

+                chooser = new StandardClosureChooser(m0, m1);

+            }

+        }

+        if (chooser == null) {

+            // standard chooser for cases if it is not a single method and if it is

+            // not the standard closure.

+            chooser = new NormalMethodChooser(theClass, closureMethods);

+        }

+    }

+

+    private void generateReflector() {

+//        if (GroovySystem.isUseReflection())

+//          return;

+//

+//        reflector = ((MetaClassRegistryImpl) registry).loadReflector(theClass, closureMethods);

+//        if (reflector == null) {

+//            throw new RuntimeException("Should have a reflector for " + theClass.getName());

+//        }

+//        // lets set the reflector on all the methods

+//        for (Iterator iter = closureMethods.iterator(); iter.hasNext();) {

+//            StdMetaMethod metaMethod = (StdMetaMethod) iter.next();

+//            metaMethod.setReflector(reflector);

+//        }

+    }

+

+    private MetaClass lookupObjectMetaClass(Object object) {

+        if (object instanceof GroovyObject) {

+            GroovyObject go = (GroovyObject) object;

+            return go.getMetaClass();

+        }

+        Class ownerClass = object.getClass();

+        if (ownerClass == Class.class) ownerClass = (Class) object;

+        MetaClass metaClass = registry.getMetaClass(ownerClass);

+        return metaClass;

+    }

+

+    public List getMethods() {

+        List answer = CLOSURE_METACLASS.getMetaMethods();

+        answer.addAll(closureMethods.toList());

+        return answer;

+    }

+

+    public List getMetaMethods() {

+        return CLOSURE_METACLASS.getMetaMethods();

+    }

+

+    public List getProperties() {

+        return CLOSURE_METACLASS.getProperties();

+    }

+

+    public MetaMethod pickMethod(String name, Class[] argTypes) {

+        if (argTypes == null) argTypes = EMPTY_CLASS_ARRAY;

+        if (name.equals(CLOSURE_CALL_METHOD) || name.equals(CLOSURE_DO_CALL_METHOD)) {

+            return pickClosureMethod(argTypes);

+        }

+        return CLOSURE_METACLASS.getMetaMethod(name, argTypes);

+    }

+

+    public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {

+        return null;

+    }

+

+    protected boolean isInitialized() {

+        return initialized;

+    }

+

+    public MetaMethod getStaticMetaMethod(String name, Object[] args) {

+        return CLOSURE_METACLASS.getStaticMetaMethod(name, args);

+    }

+

+    public MetaMethod getStaticMetaMethod(String name, Class[] argTypes) {

+        return CLOSURE_METACLASS.getStaticMetaMethod(name, argTypes);

+    }

+

+    public Object getProperty(Class sender, Object object, String name, boolean useSuper, boolean fromInsideClass) {

+        if (object instanceof Class) {

+            return getStaticMetaClass().getProperty(sender, object, name, useSuper, fromInsideClass);

+        } else {

+            return CLOSURE_METACLASS.getProperty(sender, object, name, useSuper, fromInsideClass);

+        }

+    }

+

+    public Object getAttribute(Class sender, Object object, String attribute, boolean useSuper, boolean fromInsideClass) {

+        if (object instanceof Class) {

+            return getStaticMetaClass().getAttribute(sender, object, attribute, useSuper);

+        } else {

+            if (!attributeInitDone) initAttributes();

+            MetaFieldProperty mfp = (MetaFieldProperty) attributes.get(attribute);

+            if (mfp == null) {

+                return CLOSURE_METACLASS.getAttribute(sender, object, attribute, useSuper);

+            } else {

+                return mfp.getProperty(object);

+            }

+        }

+    }

+

+    public void setAttribute(Class sender, Object object, String attribute,

+                             Object newValue, boolean useSuper, boolean fromInsideClass) {

+        if (object instanceof Class) {

+            getStaticMetaClass().setAttribute(sender, object, attribute, newValue, useSuper, fromInsideClass);

+        } else {

+            if (!attributeInitDone) initAttributes();

+            MetaFieldProperty mfp = (MetaFieldProperty) attributes.get(attribute);

+            if (mfp == null) {

+                CLOSURE_METACLASS.setAttribute(sender, object, attribute, newValue, useSuper, fromInsideClass);

+            } else {

+                mfp.setProperty(object, newValue);

+            }

+        }

+    }

+

+    public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {

+        return getStaticMetaClass().invokeMethod(Class.class, object, methodName, arguments, false, false);

+    }

+

+    public void setProperty(Class sender, Object object, String name, Object newValue, boolean useSuper, boolean fromInsideClass) {

+        if (object instanceof Class) {

+            getStaticMetaClass().setProperty(sender, object, name, newValue, useSuper, fromInsideClass);

+        } else {

+            CLOSURE_METACLASS.setProperty(sender, object, name, newValue, useSuper, fromInsideClass);

+        }

+    }

+

+    public MetaMethod getMethodWithoutCaching(Class sender, String methodName, Class[] arguments, boolean isCallToSuper) {

+        throw new UnsupportedOperationException();

+    }

+

+    public void setProperties(Object bean, Map map) {

+        throw new UnsupportedOperationException();

+    }

+

+    private Object invokeConstructor(Class at, Object[] arguments) {

+        throw new UnsupportedOperationException();

+    }

+

+    public void addMetaBeanProperty(MetaBeanProperty mp) {

+        throw new UnsupportedOperationException();

+    }

+

+    public void addMetaMethod(MetaMethod method) {

+        throw new UnsupportedOperationException();

+    }

+

+    public void addNewInstanceMethod(Method method) {

+        throw new UnsupportedOperationException();

+    }

+

+    public void addNewStaticMethod(Method method) {

+        throw new UnsupportedOperationException();

+    }

+

+    public Constructor retrieveConstructor(Class[] arguments) {

+        throw new UnsupportedOperationException();

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ClosureMetaMethod.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ClosureMetaMethod.java
new file mode 100644
index 0000000..5e8341b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ClosureMetaMethod.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import groovy.lang.Closure;
+import groovy.lang.ClosureInvokingMethod;
+import groovy.lang.MetaMethod;
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.ParameterTypes;
+import org.codehaus.groovy.reflection.ReflectionCache;
+
+import java.lang.reflect.Modifier;
+
+/**
+ *
+ * A MetaMethod that accepts a closure in the constructor which is invoked when the MetaMethod is called.
+ * The delegate of the closure is set to the instance that the MetaMethod is invoked on when called.
+ *
+ * @author Graeme Rocher
+ * @since 1.1
+ */
+public class ClosureMetaMethod extends MetaMethod implements ClosureInvokingMethod {
+
+	private final Closure callable;
+    private final String name;
+    private final ParameterTypes pt;
+    private final CachedClass declaringClass;
+
+    public ClosureMetaMethod(String name, Closure c) {
+		this(name, c.getOwner().getClass(), c);
+	}
+
+    public ClosureMetaMethod(String name, Class declaringClass,Closure c) {
+		this.name = name;
+        callable = c;
+        pt = new ParameterTypes(c.getParameterTypes());
+        this.declaringClass = ReflectionCache.getCachedClass(declaringClass);
+    }
+
+
+    public int getModifiers() {
+        return Modifier.PUBLIC;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Class getReturnType() {
+        return Object.class;
+    }
+
+	public CachedClass getDeclaringClass() {
+		return declaringClass;
+	}
+
+    public ParameterTypes getParamTypes() {
+        return pt;
+    }
+
+	public Object invoke(final Object object, final Object[] arguments) {
+		Closure cloned = (Closure) callable.clone();
+		cloned.setDelegate(object);
+
+		return cloned.call(arguments);
+	}
+
+  /**
+     * Retrieves the closure that is invoked by this MetaMethod
+     *
+     * @return The closure
+     */
+    public Closure getClosure() {
+		return callable;
+	}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ClosureStaticMetaMethod.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ClosureStaticMetaMethod.java
new file mode 100644
index 0000000..86ee05a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ClosureStaticMetaMethod.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import groovy.lang.Closure;
+import groovy.lang.ClosureInvokingMethod;
+import groovy.lang.MetaMethod;
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.ParameterTypes;
+import org.codehaus.groovy.reflection.ReflectionCache;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * This class represents a MetaMethod that is a closure that pretends to be a static method.
+ * It is used by ExpandoMetaClass to allow addition of static methods defined as closures
+ *
+ * @author Graeme Rocher
+ * @since 01.1
+ */
+public class ClosureStaticMetaMethod extends MetaMethod implements ClosureInvokingMethod {
+
+	private final Closure callable;
+	private final CachedClass declaringClass;
+    private final ParameterTypes pt;
+    private final String name;
+
+    /**
+     *
+     * @param name The name of the MetaMethod
+     * @param declaringClass The class which declared the MetaMethod
+     * @param c The closure that this ClosureMetaMethod will invoke when called
+     */
+    public ClosureStaticMetaMethod(String name, Class declaringClass, Closure c) {
+        pt = new ParameterTypes(c.getParameterTypes());
+        this.callable = c;
+		this.declaringClass = ReflectionCache.getCachedClass(declaringClass);
+        this.name = name;
+	}
+
+	public Object invoke(Object object, Object[] arguments) {
+		Closure cloned = (Closure) callable.clone();
+		cloned.setDelegate(object);
+		return cloned.call(arguments);
+	}
+
+    public int getModifiers() {
+        return Modifier.PUBLIC | Modifier.STATIC;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public Class getReturnType() {
+        return Object.class;
+    }
+
+    public CachedClass getDeclaringClass() {
+		return this.declaringClass;
+	}
+
+    public ParameterTypes getParamTypes() {
+        return pt;
+    }
+
+    /**
+     * Retrieves the closure that is invoked by this MetaMethod
+     *
+     * @return The closure
+     */
+    public Closure getClosure() {
+		return this.callable;
+	}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ConcurrentReaderHashMap.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ConcurrentReaderHashMap.java
new file mode 100644
index 0000000..2187ebb
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ConcurrentReaderHashMap.java
@@ -0,0 +1,1282 @@
+/*

+  File: ConcurrentReaderHashMap

+

+  Written by Doug Lea. Adapted and released, under explicit

+  permission, from JDK1.2 HashMap.java and Hashtable.java which

+  carries the following copyright:

+

+     * Copyright 1997 by Sun Microsystems, Inc.,

+     * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.

+     * All rights reserved.

+     *

+     * This software is the confidential and proprietary information

+     * of Sun Microsystems, Inc. ("Confidential Information").  You

+     * shall not disclose such Confidential Information and shall use

+     * it only in accordance with the terms of the license agreement

+     * you entered into with Sun.

+

+  History:

+  Date       Who                What

+  28oct1999  dl               Created

+  14dec1999  dl               jmm snapshot

+  19apr2000  dl               use barrierLock

+  12jan2001  dl               public release

+  17nov2001  dl               Minor tunings

+  20may2002  dl               BarrierLock can now be serialized.

+  09dec2002  dl               Fix interference checks.

+  23jun2004  dl               Avoid bad array sizings in view toArray methods

+  02jul2007  blackdrag        adaption of package name to Groovy project

+*/

+

+package org.codehaus.groovy.runtime.metaclass;

+

+import java.util.Map;

+import java.util.AbstractMap;

+import java.util.AbstractSet;

+import java.util.AbstractCollection;

+import java.util.Collection;

+import java.util.Set;

+import java.util.ArrayList;

+import java.util.Iterator;

+import java.util.Enumeration;

+import java.util.NoSuchElementException;

+

+import java.io.Serializable;

+import java.io.IOException;

+

+

+/**

+ * A version of Hashtable that supports mostly-concurrent reading, but

+ * exclusive writing.  Because reads are not limited to periods

+ * without writes, a concurrent reader policy is weaker than a classic

+ * reader/writer policy, but is generally faster and allows more

+ * concurrency. This class is a good choice especially for tables that

+ * are mainly created by one thread during the start-up phase of a

+ * program, and from then on, are mainly read (with perhaps occasional

+ * additions or removals) in many threads.  If you also need concurrency

+ * among writes, consider instead using ConcurrentHashMap.

+ * <p>

+ *

+ * Successful retrievals using get(key) and containsKey(key) usually

+ * run without locking. Unsuccessful ones (i.e., when the key is not

+ * present) do involve brief synchronization (locking).  Also, the

+ * size and isEmpty methods are always synchronized.

+ *

+ * <p> Because retrieval operations can ordinarily overlap with

+ * writing operations (i.e., put, remove, and their derivatives),

+ * retrievals can only be guaranteed to return the results of the most

+ * recently <em>completed</em> operations holding upon their

+ * onset. Retrieval operations may or may not return results

+ * reflecting in-progress writing operations.  However, the retrieval

+ * operations do always return consistent results -- either those

+ * holding before any single modification or after it, but never a

+ * nonsense result.  For aggregate operations such as putAll and

+ * clear, concurrent reads may reflect insertion or removal of only

+ * some entries. In those rare contexts in which you use a hash table

+ * to synchronize operations across threads (for example, to prevent

+ * reads until after clears), you should either encase operations

+ * in synchronized blocks, or instead use java.util.Hashtable.

+ *

+ * <p>

+ *

+ * This class also supports optional guaranteed

+ * exclusive reads, simply by surrounding a call within a synchronized

+ * block, as in <br> 

+ * <code>ConcurrentReaderHashMap t; ... Object v; <br>

+ * synchronized(t) { v = t.get(k); } </code> <br>

+ *

+ * But this is not usually necessary in practice. For

+ * example, it is generally inefficient to write:

+ *

+ * <pre>

+ *   ConcurrentReaderHashMap t; ...            // Inefficient version

+ *   Object key; ...

+ *   Object value; ...

+ *   synchronized(t) { 

+ *     if (!t.containsKey(key))

+ *       t.put(key, value);

+ *       // other code if not previously present

+ *     }

+ *     else {

+ *       // other code if it was previously present

+ *     }

+ *   }

+ *</pre>

+ * Instead, if the values are intended to be the same in each case, just take advantage of the fact that put returns

+ * null if the key was not previously present:

+ * <pre>

+ *   ConcurrentReaderHashMap t; ...                // Use this instead

+ *   Object key; ...

+ *   Object value; ...

+ *   Object oldValue = t.put(key, value);

+ *   if (oldValue == null) {

+ *     // other code if not previously present

+ *   }

+ *   else {

+ *     // other code if it was previously present

+ *   }

+ *</pre>

+ * <p>

+ *

+ * Iterators and Enumerations (i.e., those returned by

+ * keySet().iterator(), entrySet().iterator(), values().iterator(),

+ * keys(), and elements()) return elements reflecting the state of the

+ * hash table at some point at or since the creation of the

+ * iterator/enumeration.  They will return at most one instance of

+ * each element (via next()/nextElement()), but might or might not

+ * reflect puts and removes that have been processed since they were

+ * created.  They do <em>not</em> throw ConcurrentModificationException.

+ * However, these iterators are designed to be used by only one

+ * thread at a time. Sharing an iterator across multiple threads may

+ * lead to unpredictable results if the table is being concurrently

+ * modified.  Again, you can ensure interference-free iteration by

+ * enclosing the iteration in a synchronized block.  <p>

+ *

+ * This class may be used as a direct replacement for any use of

+ * java.util.Hashtable that does not depend on readers being blocked

+ * during updates. Like Hashtable but unlike java.util.HashMap,

+ * this class does NOT allow <tt>null</tt> to be used as a key or

+ * value.  This class is also typically faster than ConcurrentHashMap

+ * when there is usually only one thread updating the table, but 

+ * possibly many retrieving values from it.

+ * <p>

+ *

+ * Implementation note: A slightly faster implementation of

+ * this class will be possible once planned Java Memory Model

+ * revisions are in place.

+ *

+ * <p>[<a href="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>]

+ **/

+public class ConcurrentReaderHashMap 

+  extends AbstractMap 

+  implements Map, Cloneable, Serializable {

+

+

+  /*

+    The basic strategy is an optimistic-style scheme based on

+    the guarantee that the hash table and its lists are always

+    kept in a consistent enough state to be read without locking:

+

+    * Read operations first proceed without locking, by traversing the

+       apparently correct list of the apparently correct bin. If an

+       entry is found, but not invalidated (value field null), it is

+       returned. If not found, operations must recheck (after a memory

+       barrier) to make sure they are using both the right list and

+       the right table (which can change under resizes). If

+       invalidated, reads must acquire main update lock to wait out

+       the update, and then re-traverse.

+

+    * All list additions are at the front of each bin, making it easy

+       to check changes, and also fast to traverse.  Entry next

+       pointers are never assigned. Remove() builds new nodes when

+       necessary to preserve this.

+

+    * Remove() (also clear()) invalidates removed nodes to alert read

+       operations that they must wait out the full modifications.

+ 

+  */

+

+  /** A Serializable class for barrier lock **/

+  protected static class BarrierLock implements java.io.Serializable { }

+

+  /**

+   * Lock used only for its memory effects.

+   **/

+  protected final BarrierLock barrierLock = new BarrierLock();

+

+  /**

+   * field written to only to guarantee lock ordering.

+   **/

+

+  protected transient Object lastWrite;

+

+  /**

+   * Force a memory synchronization that will cause

+   * all readers to see table. Call only when already

+   * holding main synch lock.

+   **/

+  protected final void recordModification(Object x) { 

+    synchronized(barrierLock) {

+      lastWrite = x;

+    }

+  }

+

+  /**

+   * Get ref to table; the reference and the cells it

+   * accesses will be at least as fresh as from last

+   * use of barrierLock

+   **/

+  protected final Entry[] getTableForReading() { 

+    synchronized(barrierLock) {

+      return table; 

+    }

+  }

+

+

+  /**

+   * The default initial number of table slots for this table (32).

+   * Used when not otherwise specified in constructor.

+   **/

+  public static final int DEFAULT_INITIAL_CAPACITY = 32; 

+

+

+  /**

+   * The minimum capacity, used if a lower value is implicitly specified

+   * by either of the constructors with arguments.  

+   * MUST be a power of two.

+   */

+  private static final int MINIMUM_CAPACITY = 4;

+  

+  /**

+   * The maximum capacity, used if a higher value is implicitly specified

+   * by either of the constructors with arguments.

+   * MUST be a power of two <= 1<<30.

+   */

+  private static final int MAXIMUM_CAPACITY = 1 << 30;

+  

+  /**

+   * The default load factor for this table (1.0).

+   * Used when not otherwise specified in constructor.

+   **/

+

+  public static final float DEFAULT_LOAD_FACTOR = 0.75f; 

+

+

+  /**

+   * The hash table data.

+   */

+  protected transient Entry[] table;

+

+  /**

+   * The total number of mappings in the hash table.

+   */

+  protected transient int count;

+

+  /**

+   * The table is rehashed when its size exceeds this threshold.  (The

+   * value of this field is always (int)(capacity * loadFactor).)

+   *

+   * @serial

+   */

+  protected int threshold;

+

+  /**

+   * The load factor for the hash table.

+   *

+   * @serial

+   */

+  protected float loadFactor;

+

+  /**

+   * Returns the appropriate capacity (power of two) for the specified 

+   * initial capacity argument.

+   */

+  private int p2capacity(int initialCapacity) {

+    int cap = initialCapacity;

+    

+    // Compute the appropriate capacity

+    int result;

+    if (cap > MAXIMUM_CAPACITY || cap < 0) {

+      result = MAXIMUM_CAPACITY;

+    } else {

+      result = MINIMUM_CAPACITY;

+      while (result < cap)

+        result <<= 1;

+    }

+    return result;

+  }

+

+  /**

+   * Return hash code for Object x. Since we are using power-of-two

+   * tables, it is worth the effort to improve hashcode via

+   * the same multiplicative scheme as used in IdentityHashMap.

+   */

+  private static int hash(Object x) {

+    int h = x.hashCode();

+    // Multiply by 127 (quickly, via shifts), and mix in some high

+    // bits to help guard against bunching of codes that are

+    // consecutive or equally spaced.

+    return ((h << 7) - h + (h >>> 9) + (h >>> 17));

+  }

+

+  /** 

+   * Check for equality of non-null references x and y. 

+   **/

+  protected boolean eq(Object x, Object y) {

+    return x == y || x.equals(y);

+  }

+

+  /**

+   * Constructs a new, empty map with the specified initial 

+   * capacity and the specified load factor. 

+   *

+   * @param initialCapacity the initial capacity

+   *  The actual initial capacity is rounded to the nearest power of two.

+   * @param loadFactor  the load factor of the ConcurrentReaderHashMap

+   * @throws IllegalArgumentException  if the initial maximum number 

+   *               of elements is less

+   *               than zero, or if the load factor is nonpositive.

+   */

+  public ConcurrentReaderHashMap(int initialCapacity, float loadFactor) {

+    if (loadFactor <= 0)

+      throw new IllegalArgumentException("Illegal Load factor: "+

+                                         loadFactor);

+    this.loadFactor = loadFactor;

+

+    int cap = p2capacity(initialCapacity);

+

+    table = new Entry[cap];

+    threshold = (int)(cap * loadFactor);

+  }

+

+  /**

+   * Constructs a new, empty map with the specified initial 

+   * capacity and default load factor.

+   *

+   * @param   initialCapacity   the initial capacity of the 

+   *                            ConcurrentReaderHashMap.

+   * @throws    IllegalArgumentException if the initial maximum number 

+   *              of elements is less

+   *              than zero.

+   */

+  public ConcurrentReaderHashMap(int initialCapacity) {

+    this(initialCapacity, DEFAULT_LOAD_FACTOR);

+  }

+

+  /**

+   * Constructs a new, empty map with a default initial capacity

+   * and load factor.

+   */

+  public ConcurrentReaderHashMap() {

+    this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);

+  }

+

+  /**

+   * Constructs a new map with the same mappings as the given map.  The

+   * map is created with a capacity of twice the number of mappings in

+   * the given map or 16 (whichever is greater), and a default load factor.

+   */

+  public ConcurrentReaderHashMap(Map t) {

+        this(Math.max((int) (t.size() / DEFAULT_LOAD_FACTOR) + 1, 16),

+             DEFAULT_LOAD_FACTOR);

+    putAll(t);

+  }

+

+  /**

+   * Returns the number of key-value mappings in this map.

+   *

+   * @return the number of key-value mappings in this map.

+   */

+  public synchronized int size() {

+    return count;

+  }

+

+  /**

+   * Returns <tt>true</tt> if this map contains no key-value mappings.

+   *

+   * @return <tt>true</tt> if this map contains no key-value mappings.

+   */

+  public synchronized boolean isEmpty() {

+    return count == 0;

+  }

+  

+

+

+  /**

+   * Returns the value to which the specified key is mapped in this table.

+   *

+   * @param   key   a key in the table.

+   * @return  the value to which the key is mapped in this table;

+   *          <code>null</code> if the key is not mapped to any value in

+   *          this table.

+   * @exception  NullPointerException  if the key is

+   *               <code>null</code>.

+   * @see     #put(Object, Object)

+   */

+  public Object get(Object key) {

+

+    // throw null pointer exception if key null

+    int hash = hash(key);

+

+    /* 

+       Start off at the apparently correct bin.  If entry is found, we

+       need to check after a barrier anyway.  If not found, we need a

+       barrier to check if we are actually in right bin. So either

+       way, we encounter only one barrier unless we need to retry.

+       And we only need to fully synchronize if there have been

+       concurrent modifications.

+    */

+

+    Entry[] tab = table;

+    int index = hash & (tab.length - 1);

+    Entry first = tab[index];

+    Entry e = first;

+

+    for (;;) {

+      if (e == null) {

+

+        // If key apparently not there, check to

+        // make sure this was a valid read

+

+        Entry[] reread = getTableForReading();

+        if (tab == reread && first == tab[index])

+          return null;

+        else {

+          // Wrong list -- must restart traversal at new first

+          tab = reread;

+          e = first = tab[index = hash & (tab.length-1)];

+        }

+

+      }

+

+      else if (e.hash == hash && eq(key, e.key)) {

+        Object value = e.value;

+        if (value != null) 

+          return value;

+

+        // Entry was invalidated during deletion. But it could

+        // have been re-inserted, so we must retraverse.

+        // To avoid useless contention, get lock to wait out modifications

+        // before retraversing.

+

+        synchronized(this) {

+          tab = table;

+        }

+        e = first = tab[index = hash & (tab.length-1)];

+

+      }

+      else

+        e = e.next;

+    }

+  }

+

+

+  /**

+   * Tests if the specified object is a key in this table.

+   * 

+   * @param   key   possible key.

+   * @return  <code>true</code> if and only if the specified object 

+   *          is a key in this table, as determined by the 

+   *          <tt>equals</tt> method; <code>false</code> otherwise.

+   * @exception  NullPointerException  if the key is

+   *               <code>null</code>.

+   * @see     #contains(Object)

+   */

+  public boolean containsKey(Object key) {

+    return get(key) != null;

+  }

+

+  /**

+   * Maps the specified <code>key</code> to the specified 

+   * <code>value</code> in this table. Neither the key nor the 

+   * value can be <code>null</code>. <p>

+   *

+   * The value can be retrieved by calling the <code>get</code> method 

+   * with a key that is equal to the original key. 

+   *

+   * @param      key     the table key.

+   * @param      value   the value.

+   * @return     the previous value of the specified key in this table,

+   *             or <code>null</code> if it did not have one.

+   * @exception  NullPointerException  if the key or value is

+   *               <code>null</code>.

+   * @see     Object#equals(Object)

+   * @see     #get(Object)

+   */

+  public Object put(Object key, Object value) {

+    if (value == null) 

+      throw new NullPointerException();

+    

+    int hash = hash(key);

+    Entry[] tab = table;

+    int index = hash & (tab.length-1);

+    Entry first = tab[index];

+    Entry e;

+

+    for (e = first; e != null; e = e.next)

+      if (e.hash == hash && eq(key, e.key))

+        break;

+

+    synchronized(this) {

+      if (tab == table) {

+        if (e == null) {

+          //  make sure we are adding to correct list

+          if (first == tab[index]) {

+            //  Add to front of list

+            Entry newEntry = new Entry(hash, key, value, first);

+            tab[index] = newEntry;

+            if (++count >= threshold) rehash();

+            else recordModification(newEntry);

+            return null;

+          }

+        }

+        else {

+          Object oldValue = e.value; 

+          if (first == tab[index] && oldValue != null) {

+            e.value = value;

+            return oldValue;

+          }

+        }

+      }

+      

+      // retry if wrong list or lost race against concurrent remove

+      return sput(key, value, hash);

+    }

+  }

+

+

+  /**

+   * Continuation of put(), called only when synch lock is

+   * held and interference has been detected.

+   **/

+  protected Object sput(Object key, Object value, int hash) { 

+

+    Entry[] tab = table;

+    int index = hash & (tab.length-1);

+    Entry first = tab[index];

+    Entry e = first;

+

+    for (;;) {

+      if (e == null) {

+        Entry newEntry = new Entry(hash, key, value, first);

+        tab[index] = newEntry;

+        if (++count >= threshold) rehash();

+        else recordModification(newEntry);

+        return null;

+      }

+      else if (e.hash == hash && eq(key, e.key)) {

+        Object oldValue = e.value; 

+        e.value = value;

+        return oldValue;

+      }

+      else

+        e = e.next;

+    }

+  }

+

+

+  /**

+   * Rehashes the contents of this map into a new table

+   * with a larger capacity. This method is called automatically when the

+   * number of keys in this map exceeds its capacity and load factor.

+   */

+  protected void rehash() { 

+    Entry[] oldTable = table;

+    int oldCapacity = oldTable.length;

+    if (oldCapacity >= MAXIMUM_CAPACITY) {

+      threshold = Integer.MAX_VALUE; // avoid retriggering

+      return;

+    }

+

+    int newCapacity = oldCapacity << 1;

+    int mask = newCapacity - 1;

+    threshold = (int)(newCapacity * loadFactor);

+

+    Entry[] newTable = new Entry[newCapacity];

+    /*

+     * Reclassify nodes in each list to new Map.  Because we are

+     * using power-of-two expansion, the elements from each bin

+     * must either stay at same index, or move to

+     * oldCapacity+index. We also eliminate unnecessary node

+     * creation by catching cases where old nodes can be reused

+     * because their next fields won't change. Statistically, at

+     * the default threshhold, only about one-sixth of them need

+     * cloning. (The nodes they replace will be garbage

+     * collectable as soon as they are no longer referenced by any

+     * reader thread that may be in the midst of traversing table

+     * right now.)

+     */

+    

+    for (int i = 0; i < oldCapacity ; i++) {

+      // We need to guarantee that any existing reads of old Map can

+      //  proceed. So we cannot yet null out each bin.  

+      Entry e = oldTable[i];

+      

+      if (e != null) {

+        int idx = e.hash & mask;

+        Entry next = e.next;

+        

+        //  Single node on list

+        if (next == null) 

+          newTable[idx] = e;

+        

+        else {    

+          // Reuse trailing consecutive sequence of all same bit

+          Entry lastRun = e;

+          int lastIdx = idx;

+          for (Entry last = next; last != null; last = last.next) {

+            int k = last.hash & mask;

+            if (k != lastIdx) {

+              lastIdx = k;

+              lastRun = last;

+            }

+          }

+          newTable[lastIdx] = lastRun;

+          

+          // Clone all remaining nodes

+          for (Entry p = e; p != lastRun; p = p.next) {

+            int k = p.hash & mask;

+            newTable[k] = new Entry(p.hash, p.key, 

+                                    p.value, newTable[k]);

+          }

+        }

+      }

+    }

+

+    table = newTable;

+    recordModification(newTable);

+  }

+

+  /**

+   * Removes the key (and its corresponding value) from this 

+   * table. This method does nothing if the key is not in the table.

+   *

+   * @param   key   the key that needs to be removed.

+   * @return  the value to which the key had been mapped in this table,

+   *          or <code>null</code> if the key did not have a mapping.

+   * @exception  NullPointerException  if the key is

+   *               <code>null</code>.

+   */

+  public Object remove(Object key) {

+    /*

+      Find the entry, then 

+        1. Set value field to null, to force get() to retry

+        2. Rebuild the list without this entry.

+           All entries following removed node can stay in list, but

+           all preceeding ones need to be cloned.  Traversals rely

+           on this strategy to ensure that elements will not be

+          repeated during iteration.

+    */

+          

+

+    int hash = hash(key);

+    Entry[] tab = table;

+    int index = hash & (tab.length-1);

+    Entry first = tab[index];

+    Entry e = first;

+      

+    for (e = first; e != null; e = e.next) 

+      if (e.hash == hash && eq(key, e.key)) 

+        break;

+

+

+    synchronized(this) {

+      if (tab == table) {

+        if (e == null) {

+          if (first == tab[index])

+            return null;

+        }

+        else {

+          Object oldValue = e.value;

+          if (first == tab[index] && oldValue != null) {

+            e.value = null;

+            count--;

+            

+            Entry head = e.next;

+            for (Entry p = first; p != e; p = p.next) 

+              head = new Entry(p.hash, p.key, p.value, head);

+            

+            tab[index] = head;

+            recordModification(head);

+            return oldValue;

+          }

+        }

+      }

+    

+      // Wrong list or interference

+      return sremove(key, hash);

+    }

+  }

+

+  /**

+   * Continuation of remove(), called only when synch lock is

+   * held and interference has been detected.

+   **/

+  protected Object sremove(Object key, int hash) {

+    Entry[] tab = table;

+    int index = hash & (tab.length-1);

+    Entry first = tab[index];

+      

+    for (Entry e = first; e != null; e = e.next) {

+      if (e.hash == hash && eq(key, e.key)) {

+        Object oldValue = e.value;

+        e.value = null;

+        count--;

+        Entry head = e.next;

+        for (Entry p = first; p != e; p = p.next) 

+          head = new Entry(p.hash, p.key, p.value, head);

+        

+        tab[index] = head;

+        recordModification(head);

+        return oldValue;

+      }

+    }

+    return null;

+  }

+

+

+  /**

+   * Returns <tt>true</tt> if this map maps one or more keys to the

+   * specified value. Note: This method requires a full internal

+   * traversal of the hash table, and so is much slower than

+   * method <tt>containsKey</tt>.

+   *

+   * @param value value whose presence in this map is to be tested.

+   * @return <tt>true</tt> if this map maps one or more keys to the

+   * specified value.  

+   * @exception  NullPointerException  if the value is <code>null</code>.

+   */

+  public boolean containsValue(Object value) {

+    if (value == null) throw new NullPointerException();

+

+    Entry tab[] = getTableForReading();

+    

+    for (int i = 0 ; i < tab.length; ++i) {

+      for (Entry e = tab[i] ; e != null ; e = e.next) 

+        if (value.equals(e.value))

+          return true;

+    }

+

+    return false;

+  }

+

+  /**

+   * Tests if some key maps into the specified value in this table.

+   * This operation is more expensive than the <code>containsKey</code>

+   * method.<p>

+   *

+   * Note that this method is identical in functionality to containsValue,

+   * (which is part of the Map interface in the collections framework).

+   * 

+   * @param      value   a value to search for.

+   * @return     <code>true</code> if and only if some key maps to the

+   *             <code>value</code> argument in this table as 

+   *             determined by the <tt>equals</tt> method;

+   *             <code>false</code> otherwise.

+   * @exception  NullPointerException  if the value is <code>null</code>.

+   * @see        #containsKey(Object)

+   * @see        #containsValue(Object)

+   * @see      Map

+   */

+  public boolean contains(Object value) {

+    return containsValue(value);

+  }

+

+

+  /**

+   * Copies all of the mappings from the specified map to this one.

+   * 

+   * These mappings replace any mappings that this map had for any of the

+   * keys currently in the specified Map.

+   *

+   * @param t Mappings to be stored in this map.

+   */

+  public synchronized void putAll(Map t) {

+    int n = t.size();

+    if (n == 0)

+      return;

+

+    // Expand enough to hold at least n elements without resizing.

+    // We can only resize table by factor of two at a time.

+    // It is faster to rehash with fewer elements, so do it now.

+    while (n >= threshold)

+      rehash();

+

+    for (Iterator it = t.entrySet().iterator(); it.hasNext();) {

+      Map.Entry entry = (Map.Entry) it.next();

+      Object key = entry.getKey();

+      Object value = entry.getValue();

+      put(key, value);

+    }

+  }

+

+

+  /**

+   * Removes all mappings from this map.

+   */

+  public synchronized void clear() {

+    Entry tab[] = table;

+    for (int i = 0; i < tab.length ; ++i) { 

+

+      // must invalidate all to force concurrent get's to wait and then retry

+      for (Entry e = tab[i]; e != null; e = e.next) 

+        e.value = null; 

+

+      tab[i] = null;

+    }

+    count = 0;

+    recordModification(tab);

+  }

+

+  /**

+   * Returns a shallow copy of this 

+   * <tt>ConcurrentReaderHashMap</tt> instance: the keys and

+   * values themselves are not cloned.

+   *

+   * @return a shallow copy of this map.

+   */

+  public synchronized Object clone() {

+    try { 

+      ConcurrentReaderHashMap t = (ConcurrentReaderHashMap)super.clone();

+

+      t.keySet = null;

+      t.entrySet = null;

+      t.values = null;

+

+      Entry[] tab = table;

+      t.table = new Entry[tab.length];

+      Entry[] ttab = t.table;

+

+      for (int i = 0; i < tab.length; ++i) {

+        Entry first = null;

+        for (Entry e = tab[i]; e != null; e = e.next) 

+          first = new Entry(e.hash, e.key, e.value, first);

+        ttab[i] = first;

+      }

+

+      return t;

+    } 

+    catch (CloneNotSupportedException e) { 

+      // this shouldn't happen, since we are Cloneable

+      throw new InternalError();

+    }

+  }

+

+  // Views

+

+  protected transient Set keySet = null;

+  protected transient Set entrySet = null;

+  protected transient Collection values = null;

+

+  /**

+   * Returns a set view of the keys contained in this map.  The set is

+   * backed by the map, so changes to the map are reflected in the set, and

+   * vice-versa.  The set supports element removal, which removes the

+   * corresponding mapping from this map, via the <tt>Iterator.remove</tt>,

+   * <tt>Set.remove</tt>, <tt>removeAll</tt>, <tt>retainAll</tt>, and

+   * <tt>clear</tt> operations.  It does not support the <tt>add</tt> or

+   * <tt>addAll</tt> operations.

+   *

+   * @return a set view of the keys contained in this map.

+   */

+  public Set keySet() {

+    Set ks = keySet;

+    return (ks != null)? ks : (keySet = new KeySet());

+  }

+  

+  private class KeySet extends AbstractSet {

+    public Iterator iterator() {

+      return new KeyIterator();

+    }

+    public int size() {

+      return ConcurrentReaderHashMap.this.size();

+    }

+    public boolean contains(Object o) {

+      return ConcurrentReaderHashMap.this.containsKey(o);

+    }

+    public boolean remove(Object o) {

+      return ConcurrentReaderHashMap.this.remove(o) != null;

+    }

+    public void clear() {

+      ConcurrentReaderHashMap.this.clear();

+    }

+    public Object[] toArray() {

+      Collection c = new ArrayList();

+      for (Iterator i = iterator(); i.hasNext(); )

+          c.add(i.next());

+      return c.toArray();

+    }

+    public Object[] toArray(Object[] a) {

+      Collection c = new ArrayList();

+      for (Iterator i = iterator(); i.hasNext(); )

+          c.add(i.next());

+      return c.toArray(a);

+    }

+  }

+

+  /**

+   * Returns a collection view of the values contained in this map.  The

+   * collection is backed by the map, so changes to the map are reflected in

+   * the collection, and vice-versa.  The collection supports element

+   * removal, which removes the corresponding mapping from this map, via the

+   * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,

+   * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.

+   * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.

+   *

+   * @return a collection view of the values contained in this map.

+   */

+  public Collection values() {

+    Collection vs = values;

+    return (vs != null)? vs : (values = new Values());

+  }

+  

+  private class Values extends AbstractCollection {

+    public Iterator iterator() {

+      return new ValueIterator();

+    }

+    public int size() {

+      return ConcurrentReaderHashMap.this.size();

+    }

+    public boolean contains(Object o) {

+      return ConcurrentReaderHashMap.this.containsValue(o);

+    }

+    public void clear() {

+      ConcurrentReaderHashMap.this.clear();

+    }

+    public Object[] toArray() {

+      Collection c = new ArrayList();

+      for (Iterator i = iterator(); i.hasNext(); )

+          c.add(i.next());

+      return c.toArray();

+    }

+    public Object[] toArray(Object[] a) {

+      Collection c = new ArrayList();

+      for (Iterator i = iterator(); i.hasNext(); )

+          c.add(i.next());

+      return c.toArray(a);

+    }

+  }

+

+  /**

+   * Returns a collection view of the mappings contained in this map.  Each

+   * element in the returned collection is a <tt>Map.Entry</tt>.  The

+   * collection is backed by the map, so changes to the map are reflected in

+   * the collection, and vice-versa.  The collection supports element

+   * removal, which removes the corresponding mapping from the map, via the

+   * <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,

+   * <tt>removeAll</tt>, <tt>retainAll</tt>, and <tt>clear</tt> operations.

+   * It does not support the <tt>add</tt> or <tt>addAll</tt> operations.

+   *

+   * @return a collection view of the mappings contained in this map.

+   */

+  public Set entrySet() {

+    Set es = entrySet;

+    return (es != null) ? es : (entrySet = new EntrySet());

+  }

+

+  private class EntrySet extends AbstractSet {

+    public Iterator iterator() {

+      return new HashIterator();

+    }

+    public boolean contains(Object o) {

+      if (!(o instanceof Map.Entry))

+        return false;

+      Map.Entry entry = (Map.Entry)o;

+      Object v = ConcurrentReaderHashMap.this.get(entry.getKey());

+      return v != null && v.equals(entry.getValue());

+    }

+    public boolean remove(Object o) {

+      if (!(o instanceof Map.Entry))

+        return false;

+      return ConcurrentReaderHashMap.this.findAndRemoveEntry((Map.Entry)o);

+    }

+    public int size() {

+      return ConcurrentReaderHashMap.this.size();

+    }

+    public void clear() {

+      ConcurrentReaderHashMap.this.clear();

+    }

+    public Object[] toArray() {

+      Collection c = new ArrayList();

+      for (Iterator i = iterator(); i.hasNext(); )

+          c.add(i.next());

+      return c.toArray();

+    }

+    public Object[] toArray(Object[] a) {

+      Collection c = new ArrayList();

+      for (Iterator i = iterator(); i.hasNext(); )

+          c.add(i.next());

+      return c.toArray(a);

+    }

+  }

+

+  /**

+   * Helper method for entrySet.remove

+   **/

+  protected synchronized boolean findAndRemoveEntry(Map.Entry entry) {

+    Object key = entry.getKey();

+    Object v = get(key);

+    if (v != null && v.equals(entry.getValue())) {

+      remove(key);

+      return true;

+    }

+    else

+      return false;

+  }

+

+  /**

+   * Returns an enumeration of the keys in this table.

+   *

+   * @return  an enumeration of the keys in this table.

+   * @see     Enumeration

+   * @see     #elements()

+   * @see   #keySet()

+   * @see   Map

+   */

+  public Enumeration keys() {

+    return new KeyIterator();

+  }

+

+  /**

+   * Returns an enumeration of the values in this table.

+   * Use the Enumeration methods on the returned object to fetch the elements

+   * sequentially.

+   *

+   * @return  an enumeration of the values in this table.

+   * @see     java.util.Enumeration

+   * @see     #keys()

+   * @see   #values()

+   * @see   Map

+   */

+  public Enumeration elements() {

+    return new ValueIterator();

+  }

+

+

+  /**

+   * ConcurrentReaderHashMap collision list entry.

+   */

+  protected static class Entry implements Map.Entry {

+

+    /* 

+       The use of volatile for value field ensures that

+       we can detect status changes without synchronization.

+       The other fields are never changed, and are

+       marked as final. 

+    */

+    protected final int hash;

+    protected final Object key;

+    protected final Entry next;

+    protected volatile Object value;

+

+    Entry(int hash, Object key, Object value, Entry next) {

+      this.hash = hash;

+      this.key = key;

+      this.next = next;

+      this.value = value;

+    }

+

+    // Map.Entry Ops 

+

+    public Object getKey() {

+      return key;

+    }

+

+    /**

+     * Get the value.  Note: In an entrySet or entrySet.iterator,

+     * unless the set or iterator is used under synchronization of the

+     * table as a whole (or you can otherwise guarantee lack of

+     * concurrent modification), <tt>getValue</tt> <em>might</em>

+     * return null, reflecting the fact that the entry has been

+     * concurrently removed. However, there are no assurances that

+     * concurrent removals will be reflected using this method.

+     * 

+     * @return     the current value, or null if the entry has been 

+     * detectably removed.

+     **/

+    public Object getValue() {

+      return value; 

+    }

+

+    /**

+     * Set the value of this entry.  Note: In an entrySet or

+     * entrySet.iterator), unless the set or iterator is used under

+     * synchronization of the table as a whole (or you can otherwise

+     * guarantee lack of concurrent modification), <tt>setValue</tt>

+     * is not strictly guaranteed to actually replace the value field

+     * obtained via the <tt>get</tt> operation of the underlying hash

+     * table in multithreaded applications.  If iterator-wide

+     * synchronization is not used, and any other concurrent

+     * <tt>put</tt> or <tt>remove</tt> operations occur, sometimes

+     * even to <em>other</em> entries, then this change is not

+     * guaranteed to be reflected in the hash table. (It might, or it

+     * might not. There are no assurances either way.)

+     *

+     * @param      value   the new value.

+     * @return     the previous value, or null if entry has been detectably

+     * removed.

+     * @exception  NullPointerException  if the value is <code>null</code>.

+     * 

+     **/

+    public Object setValue(Object value) {

+      if (value == null)

+        throw new NullPointerException();

+      Object oldValue = this.value;

+      this.value = value;

+      return oldValue;

+    }

+

+    public boolean equals(Object o) {

+      if (!(o instanceof Map.Entry))

+        return false;

+      Map.Entry e = (Map.Entry)o;

+      return (key.equals(e.getKey()) && value.equals(e.getValue()));

+    }

+    

+    public int hashCode() {

+      return  key.hashCode() ^ value.hashCode();

+    }

+    

+    public String toString() {

+      return key + "=" + value;

+    }

+

+  }

+

+  protected class HashIterator implements Iterator, Enumeration {

+    protected final Entry[] tab;           // snapshot of table

+    protected int index;                   // current slot 

+    protected Entry entry = null;          // current node of slot

+    protected Object currentKey;           // key for current node

+    protected Object currentValue;         // value for current node

+    protected Entry lastReturned = null;   // last node returned by next

+

+    protected HashIterator() {

+      tab = ConcurrentReaderHashMap.this.getTableForReading();

+      index = tab.length - 1;

+    }

+

+    public boolean hasMoreElements() { return hasNext(); }

+    public Object nextElement() { return next(); }

+

+

+    public boolean hasNext() {

+

+      /*

+        currentkey and currentValue are set here to ensure that next()

+        returns normally if hasNext() returns true. This avoids

+        surprises especially when final element is removed during

+        traversal -- instead, we just ignore the removal during

+        current traversal.  

+      */

+

+      for (;;) {

+        if (entry != null) {

+          Object v = entry.value;

+          if (v != null) {

+            currentKey = entry.key;

+            currentValue = v;

+            return true;

+          }

+          else

+            entry = entry.next;

+        }

+

+        while (entry == null && index >= 0)

+          entry = tab[index--];

+

+        if (entry == null) {

+          currentKey = currentValue = null;

+          return false;

+        }

+      }

+    }

+

+    protected Object returnValueOfNext() { return entry; }

+

+    public Object next() {

+      if (currentKey == null && !hasNext())

+        throw new NoSuchElementException();

+

+      Object result = returnValueOfNext();

+      lastReturned = entry;

+      currentKey = currentValue = null;

+      entry = entry.next;

+      return result;

+    }

+

+    public void remove() {

+      if (lastReturned == null)

+        throw new IllegalStateException();

+      ConcurrentReaderHashMap.this.remove(lastReturned.key);

+      lastReturned = null;

+    }

+

+  }

+

+

+  protected class KeyIterator extends HashIterator {

+    protected Object returnValueOfNext() { return currentKey; }

+  }

+  

+  protected class ValueIterator extends HashIterator {

+    protected Object returnValueOfNext() { return currentValue; }

+  }

+  

+

+

+  /**

+   * Save the state of the <tt>ConcurrentReaderHashMap</tt> 

+   * instance to a stream (i.e.,

+   * serialize it).

+   *

+   * @serialData The <i>capacity</i> of the 

+   * ConcurrentReaderHashMap (the length of the

+   * bucket array) is emitted (int), followed  by the

+   * <i>size</i> of the ConcurrentReaderHashMap (the number of key-value

+   * mappings), followed by the key (Object) and value (Object)

+   * for each key-value mapping represented by the ConcurrentReaderHashMap

+   * The key-value mappings are emitted in no particular order.

+   */

+  private synchronized void writeObject(java.io.ObjectOutputStream s)

+    throws IOException  {

+    // Write out the threshold, loadfactor, and any hidden stuff

+    s.defaultWriteObject();

+    

+    // Write out number of buckets

+    s.writeInt(table.length);

+    

+    // Write out size (number of Mappings)

+    s.writeInt(count);

+    

+    // Write out keys and values (alternating)

+    for (int index = table.length-1; index >= 0; index--) {

+      Entry entry = table[index];

+      

+      while (entry != null) {

+        s.writeObject(entry.key);

+        s.writeObject(entry.value);

+        entry = entry.next;

+      }

+    }

+  }

+

+  /**

+   * Reconstitute the <tt>ConcurrentReaderHashMap</tt> 

+   * instance from a stream (i.e.,

+   * deserialize it).

+   */

+  private synchronized void readObject(java.io.ObjectInputStream s)

+    throws IOException, ClassNotFoundException  {

+    // Read in the threshold, loadfactor, and any hidden stuff

+    s.defaultReadObject();

+

+    // Read in number of buckets and allocate the bucket array;

+    int numBuckets = s.readInt();

+    table = new Entry[numBuckets];

+    

+    // Read in size (number of Mappings)

+    int size = s.readInt();

+    

+    // Read the keys and values, and put the mappings in the table

+    for (int i=0; i<size; i++) {

+      Object key = s.readObject();

+      Object value = s.readObject();

+      put(key, value);

+    }

+  }

+  

+  /** 

+   * Return the number of slots in this table 

+   **/

+  public synchronized int capacity() {

+    return table.length;

+  }

+

+  /** 

+   * Return the load factor 

+   **/

+  public float loadFactor() {

+    return loadFactor;

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MemoryAwareConcurrentReadMap.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MemoryAwareConcurrentReadMap.java
new file mode 100644
index 0000000..0311712
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MemoryAwareConcurrentReadMap.java
@@ -0,0 +1,752 @@
+/*

+  File: ConcurrentReaderHashMap

+

+  Written by Doug Lea. Adapted and released, under explicit

+  permission, from JDK1.2 HashMap.java and Hashtable.java which

+  carries the following copyright:

+

+ * Copyright 1997 by Sun Microsystems, Inc.,

+ * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.

+ * All rights reserved.

+ *

+ * This software is the confidential and proprietary information

+ * of Sun Microsystems, Inc. ("Confidential Information").  You

+ * shall not disclose such Confidential Information and shall use

+ * it only in accordance with the terms of the license agreement

+ * you entered into with Sun.

+

+  History:

+  Date       Who                What

+  28oct1999  dl               Created

+  14dec1999  dl               jmm snapshot

+  19apr2000  dl               use barrierLock

+  12jan2001  dl               public release

+  17nov2001  dl               Minor tunings

+  20may2002  dl               BarrierLock can now be serialized.

+  09dec2002  dl               Fix interference checks.

+  23jun2004  dl               Avoid bad array sizings in view toArray methods

+  02jul2007  blackdrag        adaption of package name to Groovy project

+ */

+

+package org.codehaus.groovy.runtime.metaclass;

+

+

+import java.lang.ref.ReferenceQueue;

+import java.lang.ref.SoftReference;

+

+

+/**

+ * This Map is astripped down version of ConcurrentReaderHashMap with

+ * small modifications here and there.

+ * It is no full Map, it does have put/get/remove, but no iterators.

+ * This map is intended to hold values and keys as SoftReference. If

+ * one of value or key are removed, so will be complete entry. This map

+ * will not use the equals method to compare keys, think of it as a

+ * IdentityHashMap with features of concurrency and memory aware caching.

+ * As  ConcurrentReaderHashMap also does this implementation prefere read

+ * operations and tries not to lock if possible. SoftReferenced values

+ * are only removed from the map if the map goes into a synchronization

+ * block on this. This may affect reads, but only in rare cases.

+ **/

+public class MemoryAwareConcurrentReadMap {

+

+

+    /*

+    The basic strategy is an optimistic-style scheme based on

+    the guarantee that the hash table and its lists are always

+    kept in a consistent enough state to be read without locking:

+

+     * Read operations first proceed without locking, by traversing the

+       apparently correct list of the apparently correct bin. If an

+       entry is found, but not invalidated (value field null), it is

+       returned. If not found, operations must recheck (after a memory

+       barrier) to make sure they are using both the right list and

+       the right table (which can change under resizes). If

+       invalidated, reads must acquire main update lock to wait out

+       the update, and then re-traverse.

+

+     * All list additions are at the front of each bin, making it easy

+       to check changes, and also fast to traverse.  Entry next

+       pointers are never assigned. Remove() builds new nodes when

+       necessary to preserve this.

+

+     * Remove() (also clear()) invalidates removed nodes to alert read

+       operations that they must wait out the full modifications.

+

+     */

+

+    /** A Serializable class for barrier lock **/

+    protected static class BarrierLock implements java.io.Serializable { }

+

+    /**

+     * Lock used only for its memory effects.

+     **/

+    protected final BarrierLock barrierLock = new BarrierLock();

+

+    /**

+     * field written to only to guarantee lock ordering.

+     **/

+    protected transient Object lastWrite;

+

+    /**

+     * Force a memory synchronization that will cause

+     * all readers to see table. Call only when already

+     * holding main synch lock.

+     **/

+    protected final void recordModification(Object x) { 

+        synchronized(barrierLock) {

+            lastWrite = x;

+        }

+    }

+

+    /**

+     * Get ref to table; the reference and the cells it

+     * accesses will be at least as fresh as from last

+     * use of barrierLock

+     **/

+    protected final Entry[] getTableForReading() { 

+        synchronized(barrierLock) {

+            return table; 

+        }

+    }

+

+

+    /**

+     * The default initial number of table slots for this table (32).

+     * Used when not otherwise specified in constructor.

+     **/

+    public static final int DEFAULT_INITIAL_CAPACITY = 32; 

+

+

+    /**

+     * The minimum capacity, used if a lower value is implicitly specified

+     * by either of the constructors with arguments.  

+     * MUST be a power of two.

+     */

+    private static final int MINIMUM_CAPACITY = 4;

+

+    /**

+     * The maximum capacity, used if a higher value is implicitly specified

+     * by either of the constructors with arguments.

+     * MUST be a power of two <= 1<<30.

+     */

+    private static final int MAXIMUM_CAPACITY = 1 << 30;

+

+    /**

+     * The default load factor for this table (1.0).

+     * Used when not otherwise specified in constructor.

+     **/

+

+    public static final float DEFAULT_LOAD_FACTOR = 0.75f; 

+

+

+    /**

+     * The hash table data.

+     */

+    protected transient Entry[] table;

+

+    /**

+     * The total number of mappings in the hash table.

+     */

+    protected transient int count;

+

+    /**

+     * The table is rehashed when its size exceeds this threshold.  (The

+     * value of this field is always (int)(capacity * loadFactor).)

+     *

+     * @serial

+     */

+    protected int threshold;

+

+    /**

+     * The load factor for the hash table.

+     *

+     * @serial

+     */

+    protected float loadFactor;

+

+    

+    private ReferenceQueue queue;

+    

+    /**

+     * Returns the appropriate capacity (power of two) for the specified 

+     * initial capacity argument.

+     */

+    private int p2capacity(int initialCapacity) {

+        int cap = initialCapacity;

+

+        // Compute the appropriate capacity

+        int result;

+        if (cap > MAXIMUM_CAPACITY || cap < 0) {

+            result = MAXIMUM_CAPACITY;

+        } else {

+            result = MINIMUM_CAPACITY;

+            while (result < cap)

+                result <<= 1;

+        }

+        return result;

+    }

+

+    /**

+     * Return hash code for Object x. Since we are using power-of-two

+     * tables, it is worth the effort to improve hashcode via

+     * the same multiplicative scheme as used in IdentityHashMap.

+     */

+    private static int hash(Object x) {

+        int h = x.hashCode();

+        // Multiply by 127 (quickly, via shifts), and mix in some high

+        // bits to help guard against bunching of codes that are

+        // consecutive or equally spaced.

+        return ((h << 7) - h + (h >>> 9) + (h >>> 17));

+    }

+

+    /** 

+     * Check for referential equality, null allowed 

+     **/

+    protected boolean eq(Object x, Object y) {

+        return x == y;

+    }

+

+    /**

+     * Constructs a new, empty map with the specified initial 

+     * capacity and the specified load factor. 

+     *

+     * @param initialCapacity the initial capacity

+     *  The actual initial capacity is rounded to the nearest power of two.

+     * @param loadFactor  the load factor of the ConcurrentReaderHashMap

+     * @throws IllegalArgumentException  if the initial maximum number 

+     *               of elements is less

+     *               than zero, or if the load factor is nonpositive.

+     */

+    public MemoryAwareConcurrentReadMap(int initialCapacity, float loadFactor) {

+        if (loadFactor <= 0)

+            throw new IllegalArgumentException("Illegal Load factor: "+

+                    loadFactor);

+        this.loadFactor = loadFactor;

+

+        int cap = p2capacity(initialCapacity);

+

+        table = new Entry[cap];

+        threshold = (int)(cap * loadFactor);

+        

+        queue = new ReferenceQueue();

+    }

+

+    /**

+     * Constructs a new, empty map with the specified initial 

+     * capacity and default load factor.

+     *

+     * @param   initialCapacity   the initial capacity of the 

+     *                            ConcurrentReaderHashMap.

+     * @throws    IllegalArgumentException if the initial maximum number 

+     *              of elements is less

+     *              than zero.

+     */

+    public MemoryAwareConcurrentReadMap(int initialCapacity) {

+        this(initialCapacity, DEFAULT_LOAD_FACTOR);

+    }

+

+    /**

+     * Constructs a new, empty map with a default initial capacity

+     * and load factor.

+     */

+    public MemoryAwareConcurrentReadMap() {

+        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);

+    }

+

+    /**

+     * Returns the number of key-value mappings in this map.

+     *

+     * @return the number of key-value mappings in this map.

+     */

+    public synchronized int size() {

+        return count;

+    }

+

+    /**

+     * Returns <tt>true</tt> if this map contains no key-value mappings.

+     *

+     * @return <tt>true</tt> if this map contains no key-value mappings.

+     */

+    public synchronized boolean isEmpty() {

+        return count == 0;

+    }

+

+    /**

+     * Returns the value to which the specified key is mapped in this table.

+     *

+     * @param   key   a key in the table.

+     * @return  the value to which the key is mapped in this table;

+     *          <code>null</code> if the key is not mapped to any value in

+     *          this table.

+     * @exception  NullPointerException  if the key is

+     *               <code>null</code>.

+     * @see     #put(Object, Object)

+     */

+    public Object get(Object key) {

+

+        // throw null pointer exception if key null

+        int hash = hash(key);

+

+        /* 

+       Start off at the apparently correct bin.  If entry is found, we

+       need to check after a barrier anyway.  If not found, we need a

+       barrier to check if we are actually in right bin. So either

+       way, we encounter only one barrier unless we need to retry.

+       And we only need to fully synchronize if there have been

+       concurrent modifications.

+         */

+

+        Entry[] tab = table;

+        int index = hash & (tab.length - 1);

+        Entry first = tab[index];

+        Entry e = first;

+

+        for (;;) {

+            if (e == null) {

+

+                // If key apparently not there, check to

+                // make sure this was a valid read

+

+                Entry[] reread = getTableForReading();

+                if (tab == reread && first == tab[index])

+                    return null;

+                else {

+                    // Wrong list -- must restart traversal at new first

+                    tab = reread;

+                    e = first = tab[index = hash & (tab.length-1)];

+                }

+                continue;

+            }

+            

+            Object eKey = e.getKey();

+            Object eValue = e.getValue();

+

+            if (e.hash == hash && eq(key, eKey)) {

+                if (e.value != DUMMY_REF) return eValue;

+

+                // Entry was invalidated during deletion. But it could

+                // have been re-inserted, so we must retraverse.

+                // To avoid useless contention, get lock to wait out modifications

+                // before retraversing.

+                

+                

+                synchronized(this) {

+                    if (eKey==null && eValue==null) expungeStaleEntries();

+                    tab = table;

+                }

+                e = first = tab[index = hash & (tab.length-1)];

+

+            } 

+            else 

+                e = e.next;

+        }

+    }

+    

+    /**

+     * Maps the specified <code>key</code> to the specified 

+     * <code>value</code> in this table. Neither the key nor the 

+     * value can be <code>null</code>. <p>

+     *

+     * The value can be retrieved by calling the <code>get</code> method 

+     * with a key that is equal to the original key. 

+     *

+     * @param      key     the table key.

+     * @param      value   the value.

+     * @return     the previous value of the specified key in this table,

+     *             or <code>null</code> if it did not have one.

+     * @exception  NullPointerException  if the key or value is

+     *               <code>null</code>.

+     * @see     #get(Object)

+     */

+    public Object put(Object key, Object value) {

+        if (value == null) 

+            throw new NullPointerException();

+

+        int hash = hash(key);

+        Entry[] tab = table;

+        int index = hash & (tab.length-1);

+        Entry first = tab[index];

+        Entry e;

+

+        for (e = first; e != null; e = e.next)

+            if (e.hash == hash && eq(key, e.getKey()))

+                break;

+

+        synchronized(this) {

+            if (tab == table) {

+                if (e == null) {

+                    //  make sure we are adding to correct list

+                    if (first == tab[index]) {

+                        //  Add to front of list

+                        Entry newEntry = new Entry(hash, key, value, first, queue);

+                        tab[index] = newEntry;

+                        if (++count >= threshold) rehash();

+                        else recordModification(newEntry);

+                        return null;

+                    }

+                }

+                else {

+                    Object oldValue = e.getValue();

+                    if (first == tab[index] && oldValue != null) {

+                        e.setValue(e.value);

+                        return oldValue;

+                    }

+                }

+            }

+

+            // retry if wrong list or lost race against concurrent remove

+            return sput(key, value, hash);

+        }

+    }

+

+

+    /**

+     * Continuation of put(), called only when synch lock is

+     * held and interference has been detected.

+     **/

+    protected Object sput(Object key, Object value, int hash) { 

+        expungeStaleEntries();

+

+        Entry[] tab = table;

+        int index = hash & (tab.length-1);

+        Entry first = tab[index];

+        Entry e = first;

+

+        for (;;) {

+            if (e == null) {

+                Entry newEntry = new Entry(hash, key, value, first, queue);

+                tab[index] = newEntry;

+                if (++count >= threshold) rehash();

+                else recordModification(newEntry);

+                return null;

+            }

+            else if (e.hash == hash && eq(key, e.getKey())) {

+                Object oldValue = e.getValue(); 

+                e.setValue(e.value);

+                return oldValue;

+            }

+            else

+                e = e.next;

+        }

+    }

+

+

+    /**

+     * Rehashes the contents of this map into a new table

+     * with a larger capacity. This method is called automatically when the

+     * number of keys in this map exceeds its capacity and load factor.

+     */

+    protected void rehash() { 

+        Entry[] oldTable = table;

+        int oldCapacity = oldTable.length;

+        if (oldCapacity >= MAXIMUM_CAPACITY) {

+            threshold = Integer.MAX_VALUE; // avoid retriggering

+            return;

+        }

+

+        int newCapacity = oldCapacity << 1;

+        int mask = newCapacity - 1;

+        threshold = (int)(newCapacity * loadFactor);

+

+        Entry[] newTable = new Entry[newCapacity];

+        /*

+         * Reclassify nodes in each list to new Map.  Because we are

+         * using power-of-two expansion, the elements from each bin

+         * must either stay at same index, or move to

+         * oldCapacity+index. We also eliminate unnecessary node

+         * creation by catching cases where old nodes can be reused

+         * because their next fields won't change. Statistically, at

+         * the default threshhold, only about one-sixth of them need

+         * cloning. (The nodes they replace will be garbage

+         * collectable as soon as they are no longer referenced by any

+         * reader thread that may be in the midst of traversing table

+         * right now.)

+         */

+

+        for (int i = 0; i < oldCapacity ; i++) {

+            // We need to guarantee that any existing reads of old Map can

+            //  proceed. So we cannot yet null out each bin.  

+            Entry e = oldTable[i];

+

+            if (e != null) {

+                int idx = e.hash & mask;

+                Entry next = e.next;

+

+                //  Single node on list

+                if (next == null) 

+                    newTable[idx] = e;

+

+                else {    

+                    // Reuse trailing consecutive sequence of all same bit

+                    Entry lastRun = e;

+                    int lastIdx = idx;

+                    for (Entry last = next; last != null; last = last.next) {

+                        int k = last.hash & mask;

+                        if (k != lastIdx) {

+                            lastIdx = k;

+                            lastRun = last;

+                        }

+                    }

+                    newTable[lastIdx] = lastRun;

+

+                    // Clone all remaining nodes

+                    for (Entry p = e; p != lastRun; p = p.next) {

+                        int k = p.hash & mask;

+                        newTable[k] = new Entry(p.hash, p.getKey(), 

+                                p.getValue(), newTable[k], queue);

+                    }

+                }

+            }

+        }

+

+        table = newTable;

+        recordModification(newTable);

+    }

+

+    /**

+     * Removes the key (and its corresponding value) from this 

+     * table. This method does nothing if the key is not in the table.

+     *

+     * @param   key   the key that needs to be removed.

+     * @return  the value to which the key had been mapped in this table,

+     *          or <code>null</code> if the key did not have a mapping.

+     * @exception  NullPointerException  if the key is

+     *               <code>null</code>.

+     */

+    public Object remove(Object key) {

+        /*

+      Find the entry, then 

+        1. Set value field to null, to force get() to retry

+        2. Rebuild the list without this entry.

+           All entries following removed node can stay in list, but

+           all preceeding ones need to be cloned.  Traversals rely

+           on this strategy to ensure that elements will not be

+          repeated during iteration.

+         */

+

+

+        int hash = hash(key);

+        Entry[] tab = table;

+        int index = hash & (tab.length-1);

+        Entry first = tab[index];

+        Entry e = first;

+

+        for (e = first; e != null; e = e.next) 

+            if (e.hash == hash && eq(key, e.getKey())) 

+                break;

+

+

+        synchronized(this) {

+            if (tab == table) {

+                if (e == null) {

+                    if (first == tab[index])

+                        return null;

+                }

+                else {

+                    Object oldValue = e.getValue();

+                    if (first == tab[index] && oldValue != null) {

+                        e.setValue(null);

+                        count--;

+

+                        Entry head = e.next;

+                        for (Entry p = first; p != e; p = p.next) 

+                            head = new Entry(p.hash, p.key, p.value, head, queue);

+

+                        tab[index] = head;

+                        recordModification(head);

+                        return oldValue;

+                    }

+                }

+            }

+

+            // Wrong list or interference

+            return sremove(key, hash);

+        }

+    }

+

+    /**

+     * Continuation of remove(), called only when synch lock is

+     * held and interference has been detected.

+     **/

+    protected Object sremove(Object key, int hash) {

+        expungeStaleEntries();

+        

+        Entry[] tab = table;

+        int index = hash & (tab.length-1);

+        Entry first = tab[index];

+

+        for (Entry e = first; e != null; e = e.next) {

+            if (e.hash == hash && eq(key, e.getKey())) {

+                Object oldValue = e.getValue();

+                e.setValue(null);

+                count--;

+                Entry head = e.next;

+                for (Entry p = first; p != e; p = p.next) 

+                    head = new Entry(p.hash, p.getKey(), p.getValue(), head, queue);

+

+                tab[index] = head;

+                recordModification(head);

+                return oldValue;

+            }

+        }

+        return null;

+    }

+

+    /**

+     * Removes all mappings from this map.

+     */

+    public synchronized void clear() {

+        Entry tab[] = table;

+        for (int i = 0; i < tab.length ; ++i) { 

+

+            // must invalidate all to force concurrent get's to wait and then retry

+            for (Entry e = tab[i]; e != null; e = e.next) 

+                e.setValue(null); 

+

+            tab[i] = null;

+        }

+        count = 0;

+        recordModification(tab);

+    }

+

+    /**

+     * Removes entries from the ReferenceQueue for keys and values

+     * of this map. This method is thought to be called only with

+     * an already existing lock on "this". 

+     * 

+     * The method expects SoftRef instances in the queue. It uses

+     * the entry field to control if the Entry is already removed

+     * map. If the entry is null the removal is skipped. 

+     */

+    private void expungeStaleEntries() {

+        SoftRef ref;

+        Entry[] tab = table;

+        

+        while ((ref=(SoftRef)queue.poll())!=null) {

+            Entry entry = ref.entry;

+            // if entry== null, then it is already deleted

+            // form the map

+            if (entry == null) continue;

+            ref.entry = null;

+            // if neither entry.key nor entry.value == ref then

+            // the entry was reused, but the value has become invalid

+            if (entry.key!=ref && entry.value!=ref) continue;

+            int hash = entry.hash;

+            int index = hash & (tab.length-1);

+            Entry first = tab[index];

+

+            for (Entry e = first; e != null; e = e.next) {

+                if (e==entry) {

+                    entry.key.clear();

+                    entry.setValue(null);

+                    

+                    count--;

+                    

+                    Entry head = e.next;

+                    for (Entry p = first; p != e; p = p.next) 

+                        head = new Entry(p.hash, p.key, p.value, head);

+                    

+                    tab[index] = head;

+                    recordModification(head);

+                    break;

+                }

+            } 

+        }

+    }

+    

+    /**

+     * Reference class used to support get()

+     */

+    private static interface Reference {

+        Object get();

+    }

+    

+    /**

+     * A dummy to replace the SoftReference if needed

+     */

+    private static class DummyRef implements Reference {

+        public Object get() {

+            return null;

+        }

+    }    

+    

+    // constant for DummyRef, no need to keep more than one

+    // it is not critical if more than one is created here

+    private static final Reference DUMMY_REF = new DummyRef();

+    

+    /**

+     * A SoftReference representing a key or value of the map. The

+     * instance keeps a pointer to the entry it is sotring a 

+     * key or value for. This is used to identify the entry we 

+     * need to remove 

+     * @see CopyOfMemoryAwareConcurrentReadMap#expungeStaleEntries() 

+     */

+    private static class SoftRef extends SoftReference implements Reference {

+        private volatile Entry entry;

+        public SoftRef(Entry e, Object v, ReferenceQueue q) {

+            super(v,q);

+            entry = e;

+        }

+        public void clear() {

+            super.clear();

+            entry=null;

+        }

+    }

+

+    /**

+     * ConcurrentReaderHashMap collision list entry.

+     */

+    private static class Entry {

+

+        /* 

+       The use of volatile for value field ensures that

+       we can detect status changes without synchronization.

+       The other fields are never changed, and are

+       marked as final. 

+         */

+        private final int hash;

+        private final SoftRef key;

+        private final Entry next;

+        private volatile Reference value;

+

+        Entry(int hash, Object key, Object value, Entry next, ReferenceQueue queue) {

+            this.hash = hash;

+            this.key = new SoftRef(this,key,queue);

+            this.next = next;

+            this.value = new SoftRef(this,value,queue);

+        }

+

+        Entry(int hash, SoftRef key, Reference value, Entry next) {

+            this.hash = hash;

+            this.key = key;

+            key.entry = this;

+            this.next = next;

+            this.value = DUMMY_REF;

+            this.setValue(value);

+        }

+        

+        // Map.Entry Ops 

+

+        public Object getKey() {

+            return key.get();

+        }

+

+        public Object getValue() {

+            return value.get(); 

+        }

+

+        public Object setValue(Reference value) {

+            Object oldValue = this.value.get();

+            if (value == null || value == DUMMY_REF) {

+                this.value = DUMMY_REF;

+            } else {

+                SoftRef ref = (SoftRef) value;

+                ref.entry = this;

+                this.value = value;

+            }

+            return oldValue;

+        }

+    }

+       

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MetaClassRegistryImpl.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MetaClassRegistryImpl.java
new file mode 100644
index 0000000..ed3ed51
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MetaClassRegistryImpl.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import groovy.lang.*;
+import org.codehaus.groovy.classgen.ReflectorGenerator;
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.CachedMethod;
+import org.codehaus.groovy.reflection.FastArray;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods;
+import org.codehaus.groovy.runtime.Reflector;
+import org.objectweb.asm.ClassWriter;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Modifier;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+
+/**
+ * A registry of MetaClass instances which caches introspection &
+ * reflection information and allows methods to be dynamically added to
+ * existing classes at runtime
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author John Wilson
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @author Graeme Rocher
+ *
+ * @version $Revision$
+ */
+public class MetaClassRegistryImpl implements MetaClassRegistry{
+    private volatile int constantMetaClassCount = 0;
+    private ConcurrentReaderHashMap constantMetaClasses = new ConcurrentReaderHashMap();
+    private MemoryAwareConcurrentReadMap weakMetaClasses = new MemoryAwareConcurrentReadMap();
+    private MemoryAwareConcurrentReadMap loaderMap = new MemoryAwareConcurrentReadMap();
+    private boolean useAccessible;
+    
+    private FastArray instanceMethods = new FastArray();
+    private FastArray staticMethods = new FastArray();
+
+    public static final int LOAD_DEFAULT = 0;
+    public static final int DONT_LOAD_DEFAULT = 1;
+    private static MetaClassRegistry instanceInclude;
+    private static MetaClassRegistry instanceExclude;
+
+    public MetaClassRegistryImpl() {
+        this(LOAD_DEFAULT, true);
+    }
+
+    public MetaClassRegistryImpl(int loadDefault) {
+        this(loadDefault, true);
+    }
+
+    /**
+     * @param useAccessible defines whether or not the {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
+     *                      method will be called to enable access to all methods when using reflection
+     */
+    public MetaClassRegistryImpl(boolean useAccessible) {
+        this(LOAD_DEFAULT, useAccessible);
+    }
+    
+    public MetaClassRegistryImpl(final int loadDefault, final boolean useAccessible) {
+        this.useAccessible = useAccessible;
+
+        if (loadDefault == LOAD_DEFAULT) {
+            HashMap map = new HashMap();
+
+            // lets register the default methods
+            registerMethods(DefaultGroovyMethods.class, true, map);
+            registerMethods(DefaultGroovyStaticMethods.class, false, map);
+
+            for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) {
+                Map.Entry e = (Map.Entry) it.next();
+                CachedClass cls = (CachedClass) e.getKey();
+                ArrayList list = (ArrayList) e.getValue();
+                cls.setNewMopMethods(list);
+            }
+        }
+
+        installMetaClassCreationHandle();
+
+        final MetaClass emcMetaClass = getMetaClassFor(ExpandoMetaClass.class);
+        emcMetaClass.initialize();
+        constantMetaClasses.put(ExpandoMetaClass.class,emcMetaClass);
+        constantMetaClassCount = 1;
+
+
+   }
+
+    /**
+     * Looks for a class called 'groovy.runtime.metaclass.CustomMetaClassCreationHandle' and if it exists uses it as the MetaClassCreationHandle
+     * otherwise uses the default
+     *
+     * @see groovy.lang.MetaClassRegistry.MetaClassCreationHandle
+     */
+    private void installMetaClassCreationHandle() {
+	       try {
+	           final Class customMetaClassHandle = Class.forName("groovy.runtime.metaclass.CustomMetaClassCreationHandle");
+	           final Constructor customMetaClassHandleConstructor = customMetaClassHandle.getConstructor(new Class[]{});
+				 this.metaClassCreationHandle = (MetaClassCreationHandle)customMetaClassHandleConstructor.newInstance(new Object[]{});
+	       } catch (final ClassNotFoundException e) {
+	           this.metaClassCreationHandle = new MetaClassCreationHandle();
+	       } catch (final Exception e) {
+	           throw new GroovyRuntimeException("Could not instantiate custom Metaclass creation handle: "+ e, e);
+	       }
+    }
+    
+    private void registerMethods(final Class theClass, final boolean useInstanceMethods, Map map) {
+        CachedMethod[] methods = ReflectionCache.getCachedClass(theClass).getMethods();
+
+        for (int i = 0; i < methods.length; i++) {
+            CachedMethod method = methods[i];
+            final int mod = method.getModifiers();
+            if (Modifier.isStatic(mod) && Modifier.isPublic(mod)) {
+                CachedClass[] paramTypes = method.getParameterTypes();
+                if (paramTypes.length > 0) {
+                    ArrayList arr = (ArrayList) map.get(paramTypes[0]);
+                    if (arr == null) {
+                        arr = new ArrayList(4);
+                        map.put(paramTypes[0],arr);
+                    }
+                    if (useInstanceMethods) {
+                        final NewInstanceMetaMethod metaMethod = new NewInstanceMetaMethod(method);
+                        arr.add(metaMethod);
+                        instanceMethods.add(metaMethod);
+                    } else {
+                        final NewStaticMetaMethod metaMethod = new NewStaticMetaMethod(method);
+                        arr.add(metaMethod);
+                        staticMethods.add(metaMethod);
+                    }
+                }
+            }
+        }
+    }
+
+    public MetaClass getMetaClass(Class theClass) {
+        MetaClass answer=null;
+        if (constantMetaClassCount!=0) answer = (MetaClass) constantMetaClasses.get(theClass);
+        if (answer!=null) return answer;
+        answer = (MetaClass) weakMetaClasses.get(theClass);
+        if (answer!=null) return answer;
+
+        synchronized (theClass) {
+            answer = (MetaClass) weakMetaClasses.get(theClass);
+            if (answer!=null) return answer;
+            
+            answer = getMetaClassFor(theClass);
+            answer.initialize();
+            if (GroovySystem.isKeepJavaMetaClasses()) {
+                constantMetaClassCount++;
+                constantMetaClasses.put(theClass,answer);
+            } else {
+                weakMetaClasses.put(theClass, answer);
+            }
+            return answer;
+        }
+    }
+
+    public void removeMetaClass(Class theClass) {
+        Object answer=null;
+        if (constantMetaClassCount!=0) answer = constantMetaClasses.remove(theClass);
+        if (answer==null) {
+            weakMetaClasses.remove(theClass);
+        } else {
+            synchronized(theClass) {
+                constantMetaClassCount--;
+            }
+        }
+    }
+
+    /**
+     * Registers a new MetaClass in the registry to customize the type
+     *
+     * @param theClass
+     * @param theMetaClass
+     */
+    public void setMetaClass(Class theClass, MetaClass theMetaClass) {
+        synchronized(theClass) {
+            constantMetaClassCount++;
+            constantMetaClasses.put(theClass, theMetaClass);
+        }
+    }
+
+    public boolean useAccessible() {
+        return useAccessible;
+    }
+
+    /**
+     * create Reflector loader instance if not in map. This method
+     * is only used with a lock on "this" and since loaderMap is not
+     * used anywhere else no sync is needed here
+     */
+    private ReflectorLoader getReflectorLoader(final ClassLoader loader) {
+        ReflectorLoader reflectorLoader = (ReflectorLoader) loaderMap.get(loader);
+        if (reflectorLoader == null) {
+            reflectorLoader = (ReflectorLoader) AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new ReflectorLoader(loader);
+                }
+            }); 
+            loaderMap.put(loader, reflectorLoader);
+        }
+        return reflectorLoader;
+    }
+
+    /**
+     * Find a MetaClass for the class
+     * Use the MetaClass of the superclass of the class to create the MetaClass
+     * 
+     * @param theClass
+     * @return An instance of the MetaClass which will handle this class
+     */
+    private MetaClass getMetaClassFor(final Class theClass) {
+        return metaClassCreationHandle.create(theClass,this);
+    }
+
+    // the following is experimental code, not intended for stable use yet
+    private MetaClassCreationHandle metaClassCreationHandle = new MetaClassCreationHandle();
+    /**
+     * Gets a handle internally used to create MetaClass implementations
+     * WARNING: experimental code, likely to change soon
+     * @return the handle
+     */
+    public MetaClassCreationHandle getMetaClassCreationHandler() {
+        return metaClassCreationHandle;
+    }
+    /**
+     * Sets a handle internally used to create MetaClass implementations.
+     * When replacing the handle with a custom version, you should
+     * resuse the old handle to keep custom logic and to use the
+     * default logic as fallback.
+     * WARNING: experimental code, likely to change soon
+     * @param handle the handle
+     */
+    public void setMetaClassCreationHandle(MetaClassCreationHandle handle) {
+		if(handle == null) throw new IllegalArgumentException("Cannot set MetaClassCreationHandle to null value!");
+        metaClassCreationHandle = handle;
+    }    
+
+    /**
+     * Singleton of MetaClassRegistry. Shall we use threadlocal to store the instance?
+     *
+     * @param includeExtension
+     */
+    public static MetaClassRegistry getInstance(int includeExtension) {
+        if (includeExtension != DONT_LOAD_DEFAULT) {
+            if (instanceInclude == null) {
+                instanceInclude = new MetaClassRegistryImpl();
+            }
+            return instanceInclude;
+        }
+        else {
+            if (instanceExclude == null) {
+                instanceExclude = new MetaClassRegistryImpl(DONT_LOAD_DEFAULT);
+            }
+            return instanceExclude;
+        }
+    }
+
+    public synchronized Reflector loadReflector(final Class theClass, List methods) {
+        final String name = getReflectorName(theClass);
+        ClassLoader loader = (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+        ClassLoader loader = theClass.getClassLoader();
+        if (loader == null) loader = this.getClass().getClassLoader();
+                return loader;
+            }
+        });
+        final ReflectorLoader rloader = getReflectorLoader(loader);
+        Class ref = rloader.getLoadedClass(name);
+        if (ref == null) {
+            /*
+             * Lets generate it && load it.
+             */                        
+            ReflectorGenerator generator = new ReflectorGenerator(methods);
+            ClassWriter cw = new ClassWriter(true);
+            generator.generate(cw, name);
+            final byte[] bytecode = cw.toByteArray();
+            ref = (Class) AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return rloader.defineClass(name, bytecode, getClass().getProtectionDomain());
+        }
+            });
+        }
+        try {
+          return (Reflector) ref.newInstance();
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+    private String getReflectorName(Class theClass) {
+        String className = theClass.getName();
+        String packagePrefix = "gjdk.";
+        String name = packagePrefix + className + "_GroovyReflector";
+        if (theClass.isArray()) {
+               Class clazz = theClass;
+               name = packagePrefix;
+               int level = 0;
+               while (clazz.isArray()) {
+                  clazz = clazz.getComponentType();
+                  level++;
+               }
+            String componentName = clazz.getName();
+            name = packagePrefix + componentName + "_GroovyReflectorArray";
+            if (level>1) name += level;
+        }
+        return name;
+    }
+
+    public FastArray getInstanceMethods() {
+        return instanceMethods;
+    }
+
+    public FastArray getStaticMethods() {
+        return staticMethods;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java
new file mode 100644
index 0000000..31b9f3c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MetaMethodIndex.java
@@ -0,0 +1,509 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import groovy.lang.MetaMethod;
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.FastArray;
+import org.codehaus.groovy.reflection.SingleKeyHashMap;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+
+import java.util.NoSuchElementException;
+
+public class MetaMethodIndex {
+    public SingleKeyHashMap methodHeaders = new SingleKeyHashMap();
+    private CachedClass theCachedClass;
+
+    public static class Header {
+        public Entry head;
+        Class cls;
+        public int clsHashCode31;
+        public Class subclass;
+
+        public Header(Class cls) {
+            this (cls, null);
+        }
+
+        public Header(Class cls, Class subclass) {
+            this.cls = cls;
+            this.subclass = subclass;
+            this.clsHashCode31 = 31 * cls.hashCode();
+        }
+    }
+
+    public static class Entry {
+        public int hash;
+
+        public Entry nextHashEntry, nextClassEntry;
+
+        public String name;
+        public Class cls;
+
+        public Object methods, methodsForSuper, staticMethods;
+
+        public String toString () {
+            return "[" + name + ", " + cls.getName() + "]";
+        }
+    }
+
+    public MetaMethodIndex(CachedClass theCachedClass) {
+        this.theCachedClass = theCachedClass;
+        init(DEFAULT_CAPACITY);
+
+        CachedClass last = null;
+        if (!theCachedClass.isInterface()) {
+            for (CachedClass c = theCachedClass; c != null; c = c.getCachedSuperClass()) {
+              final SingleKeyHashMap.Entry e = methodHeaders.getOrPut(c.getCachedClass());
+              e.value = new Header (c.getCachedClass(), last == null ? null : last.getCachedClass());
+              last = c;
+            }
+        }
+        else {
+            final SingleKeyHashMap.Entry e = methodHeaders.getOrPut(Object.class);
+            e.value = new Header (Object.class, theCachedClass.getCachedClass());
+        }
+    }
+
+    protected Entry table[];
+
+    protected static final int DEFAULT_CAPACITY = 32;
+    protected static final int MINIMUM_CAPACITY = 4;
+    protected static final int MAXIMUM_CAPACITY = 1 << 28;
+
+    protected int size;
+    protected transient int threshold;
+
+    public static int hash(int h) {
+        h += ~(h << 9);
+        h ^= (h >>> 14);
+        h += (h << 4);
+        h ^= (h >>> 10);
+        return h;
+    }
+
+    public int size() {
+        return size;
+    }
+
+    public boolean isEmpty() {
+        return size == 0;
+    }
+
+    public void clear() {
+        Object[] tab = table;
+        for (int i = 0; i < tab.length; i++)
+            tab[i] = null;
+        size = 0;
+    }
+
+    public void init(int initCapacity) {
+        threshold = (initCapacity * 6) / 8;
+        table = new Entry[initCapacity];
+    }
+
+    public void resize(int newLength) {
+        Entry[] oldTable = table;
+        int oldLength = table.length;
+
+        Entry[] newTable = new Entry[newLength];
+
+        for (int j = 0; j < oldLength; j++) {
+
+            for (Entry e = oldTable[j]; e != null;) {
+                Entry next = e.nextHashEntry;
+                int index = e.hash & (newLength - 1);
+
+                e.nextHashEntry = newTable[index];
+                newTable[index] = e;
+
+                e = next;
+            }
+        }
+
+        table = newTable;
+        threshold = (6 * newLength) / 8;
+    }
+
+    public interface EntryIterator {
+        boolean hasNext();
+
+        Entry next();
+    }
+
+
+    public Entry[] getTable() {
+        return table;
+    }
+
+    public EntryIterator getEntrySetIterator() {
+        return new EntryIterator() {
+            Entry next;    // next entry to return
+            int index;        // current slot
+            Entry current;    // current entry
+
+            {
+                Entry[] t = table;
+                int i = t.length;
+                Entry n = null;
+                if (size != 0) { // advance to first entry
+                    while (i > 0 && (n = t[--i]) == null) {
+                    }
+                }
+                next = n;
+                index = i;
+            }
+
+            public boolean hasNext() {
+                return next != null;
+            }
+
+            public Entry next() {
+                return nextEntry();
+            }
+
+            Entry nextEntry() {
+                Entry e = next;
+                if (e == null)
+                    throw new NoSuchElementException();
+
+                Entry n = e.nextHashEntry;
+                Entry[] t = table;
+                int i = index;
+                while (n == null && i > 0)
+                    n = t[--i];
+                index = i;
+                next = n;
+                return current = e;
+            }
+        };
+    }
+
+    public final Entry getMethods(Class cls, String name) {
+        int h = hash(31 * cls.hashCode() + name.hashCode());
+        Entry e = table[h & (table.length - 1)];
+        for (; e != null; e = e.nextHashEntry)
+            if (e.hash == h && cls == e.cls && e.name.equals(name))
+                return e;
+
+        return null;
+    }
+
+    public Entry getOrPutMethods(String name, Header header) {
+        final Class cls = header.cls;
+        int h = hash(header.clsHashCode31 + name.hashCode());
+        final Entry[] t = table;
+        final int index = h & (t.length - 1);
+        Entry e = t[index];
+        for (; e != null; e = e.nextHashEntry)
+            if (e.hash == h && cls == e.cls && e.name.equals(name))
+                return e;
+
+        Entry entry = new Entry();
+        entry.nextHashEntry = t[index];
+        entry.hash = h;
+        entry.name = name;
+        entry.cls = cls;
+        t[index] = entry;
+
+        entry.nextClassEntry = header.head;
+        header.head = entry;
+
+        if (++size == threshold)
+            resize(2 * t.length);
+
+        return entry;
+    }
+
+    public Header getHeader(Class cls) {
+        Header header;
+        final SingleKeyHashMap.Entry head = methodHeaders.getOrPut(cls);
+        if (head.value == null) {
+            head.value = new Header(cls);
+        }
+        header = (Header) head.value;
+        return header;
+    }
+
+    public void copyNonPrivateMethods(Class from, Class to) {
+        copyNonPrivateMethods(getHeader(from), getHeader(to));
+    }
+
+    public void copyNonPrivateMethods(Header from, Header to) {
+        for (Entry e = from.head; e != null; e = e.nextClassEntry)
+            copyNonPrivateMethods(e, to);
+    }
+
+    public void copyAllMethodsToSuper(Header from, Header to) {
+        for (Entry e = from.head; e != null; e = e.nextClassEntry)
+            copyAllMethodsToSuper(e, to);
+    }
+
+    public void copyNonPrivateMethodsFromSuper(Header from) {
+        for (Entry e = from.head; e != null; e = e.nextClassEntry)
+            copyNonPrivateMethodsFromSuper(e);
+    }
+
+    private void copyNonPrivateMethods(Entry from, Header to) {
+        Object oldListOrMethod = from.methods;
+        if (oldListOrMethod instanceof FastArray) {
+            FastArray oldList = (FastArray) oldListOrMethod;
+            Entry e = null;
+            int len1 = oldList.size();
+            Object list[] = oldList.getArray();
+            for (int j = 0; j != len1; ++j) {
+                MetaMethod method = (MetaMethod) list[j];
+                if (method.isPrivate()) continue;
+                if (e == null)
+                    e = getOrPutMethods(from.name, to);
+                e.methods = addMethodToList(e.methods, method);
+            }
+        } else {
+            MetaMethod method = (MetaMethod) oldListOrMethod;
+            if (!method.isPrivate()) {
+                Entry e = getOrPutMethods(from.name, to);
+                e.methods = addMethodToList(e.methods, method);
+            }
+        }
+    }
+
+    private void copyAllMethodsToSuper(Entry from, Header to) {
+        Object oldListOrMethod = from.methods;
+        if (oldListOrMethod instanceof FastArray) {
+            FastArray oldList = (FastArray) oldListOrMethod;
+            Entry e = null;
+            int len1 = oldList.size();
+            Object list[] = oldList.getArray();
+            for (int j = 0; j != len1; ++j) {
+                MetaMethod method = (MetaMethod) list[j];
+                if (e == null)
+                    e = getOrPutMethods(from.name, to);
+                e.methodsForSuper = addMethodToList(e.methodsForSuper, method);
+            }
+        } else {
+            MetaMethod method = (MetaMethod) oldListOrMethod;
+            Entry e = getOrPutMethods(from.name, to);
+            e.methodsForSuper = addMethodToList(e.methodsForSuper, method);
+        }
+    }
+
+    private void copyNonPrivateMethodsFromSuper(Entry e) {
+        Object oldListOrMethod = e.methodsForSuper;
+        if (oldListOrMethod == null)
+          return;
+        
+        if (oldListOrMethod instanceof FastArray) {
+            FastArray oldList = (FastArray) oldListOrMethod;
+            int len1 = oldList.size();
+            Object list[] = oldList.getArray();
+            for (int j = 0; j != len1; ++j) {
+                MetaMethod method = (MetaMethod) list[j];
+                if (method.isPrivate()) continue;
+                e.methods = addMethodToList(e.methods, method);
+            }
+        } else {
+            MetaMethod method = (MetaMethod) oldListOrMethod;
+            if (!method.isPrivate()) {
+                e.methods = addMethodToList(e.methods, method);
+            }
+        }
+    }
+
+    public void copyNonPrivateMethodsDown(Class from, Class to) {
+        copyNonPrivateNonNewMetaMethods(getHeader(from), getHeader(to));
+    }
+
+    public void copyNonPrivateNonNewMetaMethods(Header from, Header to) {
+        for (Entry e = from.head; e != null; e = e.nextClassEntry)
+            copyNonPrivateNonNewMetaMethods(e, to);
+    }
+
+    private void copyNonPrivateNonNewMetaMethods(Entry from, Header to) {
+        Object oldListOrMethod = from.methods;
+        if (oldListOrMethod == null)
+          return;
+        
+        if (oldListOrMethod instanceof FastArray) {
+            FastArray oldList = (FastArray) oldListOrMethod;
+            Entry e = null;
+            int len1 = oldList.size();
+            Object list[] = oldList.getArray();
+            for (int j = 0; j != len1; ++j) {
+                MetaMethod method = (MetaMethod) list[j];
+                if (method instanceof NewMetaMethod || method.isPrivate()) continue;
+                if (e == null)
+                    e = getOrPutMethods(from.name, to);
+                e.methods = addMethodToList(e.methods, method);
+            }
+        } else {
+            MetaMethod method = (MetaMethod) oldListOrMethod;
+            if (method instanceof NewMetaMethod || method.isPrivate()) return;
+            Entry e = getOrPutMethods(from.name, to);
+            e.methods = addMethodToList(e.methods, method);
+        }
+    }
+
+    public Object addMethodToList(Object o, MetaMethod method) {
+        if (o == null) {
+            return method;
+        }
+
+        if (o instanceof MetaMethod) {
+            MetaMethod match = (MetaMethod) o;
+            if (!isMatchingMethod(match, method)) {
+                FastArray list = new FastArray(2);
+                list.add(match);
+                list.add(method);
+                return list;
+            } else {
+                if (match.isPrivate()
+                        || (match.getDeclaringClass().isInterface() && !method.getDeclaringClass().isInterface())) {
+                    // do not overwrite interface methods with instance methods
+                    // do not overwrite private methods
+                    // Note: private methods from parent classes are not shown here,
+                    // but when doing the multimethod connection step, we overwrite
+                    // methods of the parent class with methods of a subclass and
+                    // in that case we want to keep the private methods
+                } else {
+                    Class methodC = method.getDeclaringClass().getCachedClass();
+                    Class matchC = match.getDeclaringClass().getCachedClass();
+                    if (methodC == matchC) {
+                        if (method instanceof NewInstanceMetaMethod ||
+                                method instanceof NewStaticMetaMethod ||
+                                method instanceof ClosureMetaMethod ||
+                                method instanceof ClosureStaticMetaMethod) {
+                            return method;
+                        }
+                    } else if (!MetaClassHelper.isAssignableFrom(methodC, matchC)) {
+                        return method;
+                    }
+                }
+            }
+            return o;
+        }
+
+        if (o instanceof FastArray) {
+            FastArray list = (FastArray) o;
+            int found = findMatchingMethod(list, method);
+
+            if (found == -1) {
+                list.add(method);
+            } else {
+                MetaMethod match = (MetaMethod) list.get(found);
+                if (match==method) return o;
+                if (match.isPrivate()
+                        || (match.getDeclaringClass().isInterface() && !method.getDeclaringClass().isInterface())) {
+                    // do not overwrite interface methods with instance methods
+                    // do not overwrite private methods
+                    // Note: private methods from parent classes are not shown here,
+                    // but when doing the multimethod connection step, we overwrite
+                    // methods of the parent class with methods of a subclass and
+                    // in that case we want to keep the private methods
+                } else {
+                    Class methodC = method.getDeclaringClass().getCachedClass();
+                    Class matchC = match.getDeclaringClass().getCachedClass();
+                    if (methodC == matchC) {
+                        if (method instanceof NewInstanceMetaMethod ||
+                                method instanceof NewStaticMetaMethod ||
+                                method instanceof ClosureMetaMethod ||
+                                method instanceof ClosureStaticMetaMethod) {
+                            list.set(found, method);
+                        }
+                    } else if (!MetaClassHelper.isAssignableFrom(methodC, matchC)) {
+                        list.set(found, method);
+                    }
+                }
+            }
+        }
+
+        return o;
+    }
+
+    private boolean isMatchingMethod(MetaMethod aMethod, MetaMethod method) {
+        if (aMethod==method) return true;
+        CachedClass[] params1 = aMethod.getParameterTypes();
+        CachedClass[] params2 = method.getParameterTypes();
+        if (params1.length != params2.length) {
+            return false;
+        }
+
+        boolean matches = true;
+        for (int i = 0; i < params1.length; i++) {
+            if (params1[i] != params2[i]) {
+                matches = false;
+                break;
+            }
+        }
+        return matches;
+    }
+
+    private int findMatchingMethod(FastArray list, MetaMethod method) {
+        int len = list.size();
+        Object data[] = list.getArray();
+        for (int j = 0; j != len; ++j) {
+            MetaMethod aMethod = (MetaMethod) data[j];
+            if (isMatchingMethod(aMethod, method))
+                return j;
+        }
+        return -1;
+    }
+
+    public void copyMethodsToSuper() {
+        Entry[] table = this.table;
+        int length = table.length;
+
+        for (int j = 0; j < length; j++) {
+            for (Entry e = table[j]; e != null; e = e.nextHashEntry) {
+                if (e.methods instanceof FastArray)
+                    e.methodsForSuper = ((FastArray) e.methods).copy();
+                else
+                    e.methodsForSuper = e.methods;
+            }
+        }
+
+    }
+
+    public void copy(Class c, Header index) {
+        copy(getHeader(c), index);
+    }
+
+    public void copy(Header from, Header to) {
+        for (Entry e = from.head; e != null; e = e.nextClassEntry)
+            copyAllMethods(e, to);
+    }
+
+    private void copyAllMethods(Entry from, Header to) {
+        Object oldListOrMethod = from.methods;
+        if (oldListOrMethod instanceof FastArray) {
+            FastArray oldList = (FastArray) oldListOrMethod;
+            Entry e = null;
+            int len1 = oldList.size();
+            Object list[] = oldList.getArray();
+            for (int j = 0; j != len1; ++j) {
+                MetaMethod method = (MetaMethod) list[j];
+                if (e == null)
+                    e = getOrPutMethods(from.name, to);
+                e.methods = addMethodToList(e.methods, method);
+            }
+        } else {
+            MetaMethod method = (MetaMethod) oldListOrMethod;
+            if (!method.isPrivate()) {
+                Entry e = getOrPutMethods(from.name, to);
+                e.methods = addMethodToList(e.methods, method);
+            }
+        }
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MethodHelper.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MethodHelper.java
new file mode 100644
index 0000000..ac24057
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MethodHelper.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * Some reflection helper methods
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MethodHelper {
+ 
+    public static boolean isStatic(Method method) {
+        int flags = Modifier.STATIC;
+        return (method.getModifiers() & (flags)) == flags;
+    }
+
+    public static boolean isPublic(Method method) {
+        int flags = Modifier.PUBLIC;
+        return (method.getModifiers() & (flags)) == flags;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MethodSelectionException.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MethodSelectionException.java
new file mode 100644
index 0000000..5d08b43
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MethodSelectionException.java
@@ -0,0 +1,99 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.runtime.metaclass;

+

+import groovy.lang.GroovyRuntimeException;

+import groovy.lang.MetaMethod;

+import org.codehaus.groovy.reflection.CachedConstructor;

+import org.codehaus.groovy.reflection.FastArray;

+

+import java.lang.reflect.Modifier;

+

+/**

+ * This exception is thrown if the runtime is unable to select

+ * a method. This class builds the exception text when calling 

+ * getMessage. <br/>

+ * <b>Note:</b> This exception as for internal use only!

+ * 

+ * @author Jochen Theodorou

+ * @since Groovy 1.1

+ */

+public class MethodSelectionException extends GroovyRuntimeException {

+

+    private String methodName;

+    private FastArray methods;

+    private Class[] arguments;

+    

+    /**

+     * Creates a new MethodSelectionException.

+     * @param methodName name of the method

+     * @param methods    a FastArray of methods

+     * @param arguments  the method call argument classes

+     */

+    public MethodSelectionException(String methodName, FastArray methods, Class[] arguments) {

+        super(methodName);

+        this.methodName = methodName;

+        this.arguments = arguments;

+        this.methods = methods;

+    }

+

+    public String getMessage() {

+        StringBuffer buffer = new StringBuffer();

+        buffer.append("Could not find which method ").append(methodName);

+        appendClassNames(buffer,arguments);

+        buffer.append(" to invoke from this list:");

+        appendMethods(buffer);

+        return buffer.toString();

+    }

+    

+    

+    private void appendClassNames(StringBuffer argBuf, Class[] classes) {

+        argBuf.append("(");

+        for (int i = 0; i < classes.length; i++) {

+            if (i > 0) {

+                argBuf.append(", ");

+            }

+            Class clazz = classes[i];

+            String name = clazz==null? "null": clazz.getName();

+            argBuf.append(name);

+        }

+        argBuf.append(")");

+    }

+    

+    private void appendMethods(StringBuffer buffer) {

+        for (int i = 0; i < methods.size; i++) {

+            buffer.append("\n  ");

+            Object methodOrConstructor = methods.get(i);

+            if (methodOrConstructor instanceof MetaMethod) {

+                MetaMethod method = (MetaMethod) methodOrConstructor;

+                buffer.append(Modifier.toString(method.getModifiers()));

+                buffer.append(" ").append(method.getReturnType().getName());

+                buffer.append(" ").append(method.getDeclaringClass().getName());

+                buffer.append("#");

+                buffer.append(method.getName());

+                appendClassNames(buffer,method.getNativeParameterTypes());

+            }

+            else {

+                CachedConstructor method = (CachedConstructor) methodOrConstructor;

+                buffer.append(Modifier.toString(method.cachedConstructor.getModifiers()));

+                buffer.append(" ").append(method.cachedConstructor.getDeclaringClass().getName());

+                buffer.append("#<init>");

+                appendClassNames(buffer,method.getNativeParameterTypes());

+            }

+        }

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MissingMethodExceptionNoStack.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MissingMethodExceptionNoStack.java
new file mode 100644
index 0000000..6203bf5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MissingMethodExceptionNoStack.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import groovy.lang.MissingMethodException;
+
+public class MissingMethodExceptionNoStack extends MissingMethodException {
+
+    public MissingMethodExceptionNoStack(String method, Class type, Object[] arguments) {
+        this(method,type,arguments,false);
+    }
+
+    public MissingMethodExceptionNoStack(String method, Class type, Object[] arguments, boolean isStatic) {
+        super (method, type, arguments, isStatic);
+    }
+
+    public Throwable fillInStackTrace() {
+        return this;
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MissingPropertyExceptionNoStack.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MissingPropertyExceptionNoStack.java
new file mode 100644
index 0000000..5fd83e3
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/MissingPropertyExceptionNoStack.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import groovy.lang.MissingPropertyException;
+
+public class MissingPropertyExceptionNoStack extends MissingPropertyException {
+
+    public MissingPropertyExceptionNoStack(String propertyName, Class theClass) {
+        super(propertyName, theClass);
+    }
+
+    public Throwable fillInStackTrace() {
+        return this;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/NewInstanceMetaMethod.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/NewInstanceMetaMethod.java
new file mode 100644
index 0000000..8ce0a28
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/NewInstanceMetaMethod.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import org.codehaus.groovy.reflection.CachedMethod;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * A MetaMethod implementation where the underlying method is really a static
+ * helper method on some class but it appears to be an instance method on a class.
+ *
+ * This implementation is used to add new methods to the JDK writing them as normal
+ * static methods with the first parameter being the class on which the method is added.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class NewInstanceMetaMethod extends NewMetaMethod {
+
+
+    public NewInstanceMetaMethod(CachedMethod method) {
+        super(method);
+    }
+
+    public boolean isStatic() {
+        return false;
+    }
+
+    public int getModifiers() {
+        // lets clear the static bit
+        return Modifier.PUBLIC;
+    }
+
+    public Object invoke(Object object, Object[] arguments)  {
+        // we need to cheat using the type
+        int size = arguments.length;
+        Object[] newArguments = new Object[size + 1];
+        newArguments[0] = object;
+        System.arraycopy(arguments, 0, newArguments, 1, size);
+        return super.invoke(null, newArguments);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/NewMetaMethod.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/NewMetaMethod.java
new file mode 100644
index 0000000..5fdfefa
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/NewMetaMethod.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.CachedMethod;
+import org.codehaus.groovy.reflection.ParameterTypes;
+
+/**
+ * Base class for NewInstanceMetaMethod and NewStaticMetaMethod
+ */
+public class NewMetaMethod extends ReflectionMetaMethod {
+    protected static final CachedClass[] EMPTY_TYPE_ARRAY = {};
+    protected CachedClass[] bytecodeParameterTypes ;
+    protected ParameterTypes paramTypes;
+
+    public NewMetaMethod(CachedMethod method) {
+        super(method);
+        bytecodeParameterTypes = method.getParameterTypes();
+
+        int size = bytecodeParameterTypes.length;
+        CachedClass[] logicalParameterTypes;
+        if (size <= 1) {
+            logicalParameterTypes = EMPTY_TYPE_ARRAY;
+        } else {
+            logicalParameterTypes = new CachedClass[--size];
+            System.arraycopy(bytecodeParameterTypes, 1, logicalParameterTypes, 0, size);
+        }
+        paramTypes = new ParameterTypes(logicalParameterTypes);
+    }
+
+    public ParameterTypes getParamTypes() {
+        return paramTypes;
+    }
+
+    public CachedClass getDeclaringClass() {
+        return getBytecodeParameterTypes()[0];
+    }
+
+    public CachedClass[] getBytecodeParameterTypes() {
+        return bytecodeParameterTypes;
+    }
+
+    public CachedClass getOwnerClass() {
+        return getBytecodeParameterTypes()[0];
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/NewStaticMetaMethod.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/NewStaticMetaMethod.java
new file mode 100644
index 0000000..704dcea
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/NewStaticMetaMethod.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import org.codehaus.groovy.reflection.CachedMethod;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * A MetaMethod implementation where the underlying method is really a static
+ * helper method on some class.
+ *
+ * This implementation is used to add new static methods to the JDK writing them as normal
+ * static methods with the first parameter being the class on which the method is added.
+ *
+ * @author Guillaume Laforge
+ * @version $Revision$
+ */
+public class NewStaticMetaMethod extends NewMetaMethod {
+
+    public NewStaticMetaMethod(CachedMethod method) {
+        super(method);
+    }
+
+    public boolean isStatic() {
+        return true;
+    }
+
+    public int getModifiers() {
+        return Modifier.PUBLIC | Modifier.STATIC;
+    }
+
+    public Object invoke(Object object, Object[] arguments) {
+        int size = arguments.length;
+        Object[] newArguments = new Object[size + 1];
+        System.arraycopy(arguments, 0, newArguments, 1, size);
+        newArguments[0] = null;
+        return super.invoke(null, newArguments);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ReflectionMetaMethod.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ReflectionMetaMethod.java
new file mode 100644
index 0000000..442cb3d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ReflectionMetaMethod.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import groovy.lang.MetaMethod;
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.CachedMethod;
+import org.codehaus.groovy.reflection.ParameterTypes;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class ReflectionMetaMethod extends MetaMethod {
+    protected final CachedMethod method;
+
+    public ReflectionMetaMethod(CachedMethod method) {
+        this.method = method;
+    }
+
+    public int getModifiers() {
+        return method.getModifiers();
+    }
+
+    public String getName() {
+        return method.getName();
+    }
+
+    public Class getReturnType() {
+        return method.getReturnType();
+    }
+
+    public CachedClass getDeclaringClass() {
+        return method.cachedClass;
+    }
+
+    public ParameterTypes getParamTypes() {
+        return method;
+    }
+
+    public Object invoke(Object object, Object[] arguments) {
+        try {
+            return method.setAccessible().invoke(object, arguments);
+        } catch (IllegalArgumentException e) {
+            throw new InvokerInvocationException(e);
+        } catch (IllegalAccessException e) {
+            throw new InvokerInvocationException(e);
+        } catch (InvocationTargetException e) {
+            throw new InvokerInvocationException(e);
+        }
+    }
+
+    public boolean isMethod(Method method) {
+        return method == this.method.cachedMethod || super.isMethod(method);
+    }
+
+    public String toString () {
+        return method.toString();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ReflectorLoader.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ReflectorLoader.java
new file mode 100644
index 0000000..e415cab
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ReflectorLoader.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.metaclass;
+
+import org.codehaus.groovy.runtime.Reflector;
+
+import java.security.ProtectionDomain;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Reflector creation helper. This class is used to define the Refloctor classes.
+ * For each ClassLoader such a loader will be created by MetaClass.
+ * Special about this loader is, that it knows the classes form the 
+ * Groovy Runtime. The Reflector class is resolved in different ways: During
+ * the definition of a class Reflector will resolve to the Reflector class of
+ * the runtime, even if there is another Reflector class in the parent loader.
+ * After the new class is defined Reflector will resolve like other Groovy
+ * classes. This loader is able to resolve all Groovy classes even if the
+ * parent does not know them, but the parent serves first (Reflector during a
+ * class defintion is different). 
+ * 
+ * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
+ * @version $Revision$
+ */
+public class ReflectorLoader extends ClassLoader {
+    private boolean inDefine = false;
+    private final Map loadedClasses = new HashMap();
+    private final ClassLoader delegatationLoader;
+
+    private static final String REFLECTOR = Reflector.class.getName();
+    
+    /**
+     * Tries to find a Groovy class.
+     * 
+     * @return the class if found
+     * @throws ClassNotFoundException if not found
+     */
+    protected Class findClass(String name) throws ClassNotFoundException {
+        if (delegatationLoader==null) return super.findClass(name);
+        return delegatationLoader.loadClass(name);
+    }
+    
+    /**
+     * Loads a class per name. Unlike a normal loadClass this version
+     * behaves different during a class definition. In that case it
+     * checks if the class we want to load is Reflector and returns 
+     * class if the check is successful. If it is not during a class
+     * definition it just calls the super class version of loadClass. 
+     * 
+     * @param name of the class to load
+     * @param resolve is true if the class should be resolved
+     * @see Reflector
+     * @see ClassLoader#loadClass(String, boolean)
+     */
+    protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+        if (inDefine) {
+            if (name.equals(REFLECTOR)) return Reflector.class;
+        }
+        return super.loadClass(name, resolve);
+    }
+    
+    /**
+     * helper method to define Reflector classes.
+     * @param name of the Reflector
+     * @param bytecode the bytecode
+     * @param domain  the protection domain
+     * @return the generated class
+     */
+    public synchronized Class defineClass(String name, byte[] bytecode, ProtectionDomain domain) {
+        inDefine = true;
+        Class c = defineClass(name, bytecode, 0, bytecode.length, domain);
+        loadedClasses.put(name,c); 
+        resolveClass(c);
+        inDefine = false;
+        return c;
+    }
+    
+    /**
+     * creates a RelfectorLoader. 
+     * @param parent the parent loader. This should never be null!
+     */
+    public ReflectorLoader(ClassLoader parent) {
+        super(parent);
+        delegatationLoader = getClass().getClassLoader();
+    }
+    
+    /**
+     * try to load one of the defined Reflector classes by name.
+     * @param name of the Reflector class
+     * @return the Reflector class if defined else null.
+     */
+    public synchronized Class getLoadedClass(String name) {
+        return (Class)loadedClasses.get(name);
+    }
+
+    static String getReflectorName(Class theClass) {
+        String className = theClass.getName();
+        if (className.startsWith("java.")) {
+            String packagePrefix = "gjdk.";
+            String name = packagePrefix + className + "_GroovyReflector";
+            if (theClass.isArray()) {
+                   Class clazz = theClass;
+                   name = packagePrefix;
+                   int level = 0;
+                   while (clazz.isArray()) {
+                      clazz = clazz.getComponentType();
+                      level++;
+                   }
+                String componentName = clazz.getName();
+                name = packagePrefix + componentName + "_GroovyReflectorArray";
+                if (level>1) name += level;
+            }
+            return name;
+        }
+        else {
+            String name = className.replace('$','_') + "_GroovyReflector";
+            if (theClass.isArray()) {
+                   Class clazz = theClass;
+                   int level = 0;
+                   while (clazz.isArray()) {
+                      clazz = clazz.getComponentType();
+                      level++;
+                   }
+                String componentName = clazz.getName();
+                name = componentName.replace('$','_') + "_GroovyReflectorArray";
+                if (level>1) name += level;
+            }
+            return name;
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/TemporaryMethodKey.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/TemporaryMethodKey.java
new file mode 100644
index 0000000..99b6b70
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/TemporaryMethodKey.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import org.codehaus.groovy.runtime.MethodKey;
+import org.codehaus.groovy.runtime.MetaClassHelper;
+
+
+/**
+ * A temporary implementation of MethodKey used to perform a fast lookup
+ * for a method using a set of arguments to a method
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TemporaryMethodKey extends MethodKey {
+
+    private final Object[] parameterValues;
+
+    public TemporaryMethodKey(Class sender, String name, Object[] parameterValues, boolean isCallToSuper) {
+        super(sender, name, isCallToSuper);
+        if (parameterValues == null) {
+            parameterValues = MetaClassHelper.EMPTY_ARRAY;
+        }
+        this.parameterValues = parameterValues;
+    }
+
+    public int getParameterCount() {
+        return parameterValues.length;
+    }
+
+    public Class getParameterType(int index) {
+        Object value = parameterValues[index];
+
+        if (value != null ) {
+            Class type = (Class)((value.getClass() == java.lang.Class.class) ?
+                    value :
+                    value.getClass());
+            return type;
+        }
+
+        return Object.class;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java
new file mode 100644
index 0000000..4ee0363
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/ThreadManagedMetaBeanProperty.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+
+import groovy.lang.Closure;
+import groovy.lang.MetaBeanProperty;
+import groovy.lang.MetaMethod;
+
+import java.lang.reflect.Modifier;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.reflection.ParameterTypes;
+
+/**
+ * This MetaBeanProperty will create a pseudo property whose value is bound to the current
+ * Thread using soft references. The values will go out of scope and be garabage collected when
+ * the Thread dies or when memory is required by the JVM
+ * <p/>
+ * The property uses an InheritableThreadLocal instance internally so child threads will still be able
+ * to see the property
+ *
+ * @author Graeme Rocher
+ * @since 1.1
+ */
+public class ThreadManagedMetaBeanProperty extends MetaBeanProperty {
+    private static final CachedClass[] ZERO_ARGUMENT_LIST = new CachedClass[0];
+    private static final ThreadLocal PROPERTY_INSTANCE_HOLDER = new InheritableThreadLocal();
+
+    private Class declaringClass;
+    private ThreadBoundGetter getter;
+    private ThreadBoundSetter setter;
+    private Object initialValue;
+    private static final String PROPERTY_SET_PREFIX = "set";
+    private Closure initialValueCreator;
+
+    /**
+     * Retrieves the initial value of the ThreadBound property
+     *
+     * @return The initial value
+     */
+    public synchronized Object getInitialValue() {
+        return getInitialValue(null);
+    }
+
+    public synchronized Object getInitialValue(Object object) {
+        if (initialValueCreator != null) {
+            return initialValueCreator.call(object);
+        }
+        return initialValue;
+
+    }
+
+    /**
+     * Closure responsible for creating the initial value of thread-managed bean properties
+     *
+     * @param callable The closure responsible for creating the initial value
+     */
+    public void setInitialValueCreator(Closure callable) {
+        this.initialValueCreator = callable;
+    }
+
+    /**
+     * Constructs a new ThreadManagedBeanProperty for the given arguments
+     *
+     * @param declaringClass The class that declares the property
+     * @param name           The name of the property
+     * @param type           The type of the property
+     * @param iv             The properties initial value
+     */
+    public ThreadManagedMetaBeanProperty(Class declaringClass, String name, Class type, Object iv) {
+        super(name, type, null, null);
+        this.type = type;
+        this.declaringClass = declaringClass;
+
+        this.getter = new ThreadBoundGetter(name);
+        this.setter = new ThreadBoundSetter(name);
+        initialValue = iv;
+
+    }
+
+    /**
+     * Constructs a new ThreadManagedBeanProperty for the given arguments
+     *
+     * @param declaringClass      The class that declares the property
+     * @param name                The name of the property
+     * @param type                The type of the property
+     * @param initialValueCreator The closure responsible for creating the initial value
+     */
+    public ThreadManagedMetaBeanProperty(Class declaringClass, String name, Class type, Closure initialValueCreator) {
+        super(name, type, null, null);
+        this.type = type;
+        this.declaringClass = declaringClass;
+
+        this.getter = new ThreadBoundGetter(name);
+        this.setter = new ThreadBoundSetter(name);
+        this.initialValueCreator = initialValueCreator;
+
+    }
+
+    private static Object getThreadBoundPropertyValue(Object obj, String name, Object initialValue) {
+        Map propertyMap = getThreadBoundPropertMap();
+        String key = System.identityHashCode(obj) + name;
+        if (propertyMap.containsKey(key)) {
+            return propertyMap.get(key);
+        } else {
+            propertyMap.put(key, initialValue);
+            return initialValue;
+        }
+    }
+
+    private static Map getThreadBoundPropertMap() {
+        Map propertyMap = (Map) PROPERTY_INSTANCE_HOLDER.get();
+        if (propertyMap == null) {
+            propertyMap = new WeakHashMap();
+            PROPERTY_INSTANCE_HOLDER.set(propertyMap);
+        }
+        return propertyMap;
+    }
+
+    private static Object setThreadBoundPropertyValue(Object obj, String name, Object value) {
+        Map propertyMap = getThreadBoundPropertMap();
+        String key = System.identityHashCode(obj) + name;
+        return propertyMap.put(key, value);
+    }
+
+    /* (non-Javadoc)
+      * @see groovy.lang.MetaBeanProperty#getGetter()
+      */
+    public MetaMethod getGetter() {
+        return this.getter;
+    }
+
+    /* (non-Javadoc)
+      * @see groovy.lang.MetaBeanProperty#getSetter()
+      */
+    public MetaMethod getSetter() {
+        return this.setter;
+    }
+
+
+    /**
+     * Accesses the ThreadBound state of the property as a getter
+     *
+     * @author Graeme Rocher
+     */
+    class ThreadBoundGetter extends MetaMethod {
+
+
+        private final String name, name0;
+        private final ParameterTypes pt = new ParameterTypes(new CachedClass[0]);
+
+
+        public ThreadBoundGetter(String name) {
+            this.name = getGetterName(name, type);
+            this.name0 = name;
+        }
+
+
+        public int getModifiers() {
+            return Modifier.PUBLIC;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public Class getReturnType() {
+            return type;
+        }
+
+        public CachedClass getDeclaringClass() {
+            return ReflectionCache.getCachedClass(declaringClass);
+        }
+
+        public ParameterTypes getParamTypes() {
+            return pt;
+        }
+
+
+        /* (non-Javadoc)
+           * @see groovy.lang.MetaMethod#invoke(java.lang.Object, java.lang.Object[])
+           */
+        public Object invoke(Object object, Object[] arguments) {
+            return getThreadBoundPropertyValue(object, name0, getInitialValue());
+        }
+    }
+
+    /**
+     * Sets the ThreadBound state of the property like a setter
+     *
+     * @author Graeme Rocher
+     */
+    private class ThreadBoundSetter extends MetaMethod {
+
+
+        private final String name, name0;
+        private ParameterTypes pt = new ParameterTypes(new CachedClass[]{ReflectionCache.getCachedClass(type)});
+
+        public ThreadBoundSetter(String name) {
+            this.name = getSetterName(name);
+            this.name0 = name;
+        }
+
+
+        public int getModifiers() {
+            return Modifier.PUBLIC;
+        }/* (non-Javadoc)
+		 * @see groovy.lang.MetaMethod#getName()
+		 */
+
+        public String getName() {
+            return name;
+        }
+
+        public Class getReturnType() {
+            return type;
+        }
+
+        public CachedClass getDeclaringClass() {
+            return ReflectionCache.getCachedClass(declaringClass);
+        }
+
+        public ParameterTypes getParamTypes() {
+            return pt;
+        }
+
+        /* (non-Javadoc)
+           * @see groovy.lang.MetaMethod#invoke(java.lang.Object, java.lang.Object[])
+           */
+        public Object invoke(Object object, Object[] arguments) {
+            return setThreadBoundPropertyValue(object, name0, arguments[0]);
+        }
+    }
+
+    private String getGetterName(String propertyName, Class type) {
+        String prefix = type == boolean.class || type == Boolean.class ? "is" : "get";
+        return prefix + Character.toUpperCase(propertyName.charAt(0))
+                + propertyName.substring(1);
+    }
+
+    private String getSetterName(String propertyName) {
+        return PROPERTY_SET_PREFIX + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/TransformMetaMethod.java b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/TransformMetaMethod.java
new file mode 100644
index 0000000..18762d7
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/TransformMetaMethod.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.metaclass;
+
+import groovy.lang.MetaMethod;
+import org.codehaus.groovy.reflection.CachedClass;
+import org.codehaus.groovy.reflection.ParameterTypes;
+
+/**
+ * A MetaMethod implementation useful for implementing coercion based invocations
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+
+public class TransformMetaMethod extends MetaMethod {
+    
+    private MetaMethod metaMethod;
+
+    public TransformMetaMethod(MetaMethod metaMethod) {
+        this.metaMethod = metaMethod;
+    }
+
+    public int getModifiers() {
+        return metaMethod.getModifiers();
+    }
+
+    public String getName() {
+        return metaMethod.getName();
+    }
+
+    public Class getReturnType() {
+        return metaMethod.getReturnType();
+    }
+
+    public CachedClass getDeclaringClass() {
+        return metaMethod.getDeclaringClass();
+    }
+
+    public ParameterTypes getParamTypes() {
+        return metaMethod.getParamTypes();
+    }
+
+    public Object invoke(Object object, Object[] arguments) {
+        return metaMethod.invoke(object, arguments);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/metaclass/package.html b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/package.html
new file mode 100644
index 0000000..b35f8f2
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/metaclass/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.runtime.metaclass.*</title>
+  </head>
+  <body>
+    <p>Internal classes related to Groovy's metaclass implementation.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/package.html b/groovy/src/main/org/codehaus/groovy/runtime/package.html
new file mode 100644
index 0000000..0c86cdc
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.runtime.*</title>
+  </head>
+  <body>
+    <p>Runtime classes for Groovy - whether the dynamic interpreter is being used, the compiler or the bytecode generator.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/BigDecimalMath.java b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/BigDecimalMath.java
new file mode 100644
index 0000000..c77b9c4
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/BigDecimalMath.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.typehandling;
+
+import java.math.BigDecimal;
+
+/**
+ * BigDecimal NumberMath operations
+ * 
+ * @author Steve Goetze
+ */
+public class BigDecimalMath extends NumberMath {
+
+	//This is an arbitrary value, picked as a reasonable choice for a rounding point
+	//for typical user math.
+	public static final int MAX_DIVISION_SCALE = 10;
+	
+	public static final BigDecimalMath INSTANCE = new BigDecimalMath();
+	
+	private BigDecimalMath() {}
+
+	protected Number absImpl(Number number) {
+		return toBigDecimal(number).abs();
+	}
+	
+	protected Number addImpl(Number left, Number right) {
+		return toBigDecimal(left).add(toBigDecimal(right));
+	}
+
+	protected Number subtractImpl(Number left, Number right) {
+		return toBigDecimal(left).subtract(toBigDecimal(right));
+	}
+
+	protected Number multiplyImpl(Number left, Number right) {
+		return toBigDecimal(left).multiply(toBigDecimal(right));
+	}
+
+	protected Number divideImpl(Number left, Number right) {
+		//Hack until Java 1.5 BigDecimal is available.  For now, pick
+		//a result scale which is the maximum of the scale of the
+		//two operands and an arbitrary maximum (similar to what a
+		//handheld calculator would do).  Then, normalize the result
+		//by removing any trailing zeros.
+		BigDecimal bigLeft = toBigDecimal(left);
+		BigDecimal bigRight = toBigDecimal(right);
+		int scale = Math.max(bigLeft.scale(), bigRight.scale());
+		return normalize(bigLeft.divide(bigRight, Math.max(scale, MAX_DIVISION_SCALE), BigDecimal.ROUND_HALF_UP));
+	}
+	
+	protected int compareToImpl(Number left, Number right) {
+		return toBigDecimal(left).compareTo(toBigDecimal(right));
+	}
+	
+	private BigDecimal normalize(BigDecimal number) {
+        // we have to take care of the case number==0, because 0 can have every
+        // scale and the test in the while loop would never end
+        if (number.signum()==0) {
+            // the smallest scale for 0 is 0
+            return number.setScale(0);
+        }
+        // rescale until we found the smallest possible scale
+		try {
+			while (true) {
+				number = number.setScale(number.scale()-1);
+			} 
+		} catch (ArithmeticException e) {
+			return number;
+		}
+	}
+
+    protected Number unaryMinusImpl(Number left) {
+        return toBigDecimal(left).negate();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/BigIntegerMath.java b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/BigIntegerMath.java
new file mode 100644
index 0000000..49870c2
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/BigIntegerMath.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.typehandling;
+
+/**
+ * BigInteger NumberMath operations
+ * 
+ * @author Steve Goetze
+ */
+public class BigIntegerMath extends NumberMath {
+
+	public static final BigIntegerMath INSTANCE = new BigIntegerMath();
+	
+	private BigIntegerMath() {}
+
+	protected Number absImpl(Number number) {
+		return toBigInteger(number).abs();
+	}
+	
+	protected Number addImpl(Number left, Number right) {
+		return toBigInteger(left).add(toBigInteger(right));
+	}
+	protected Number subtractImpl(Number left, Number right) {
+		return toBigInteger(left).subtract(toBigInteger(right));
+	}
+
+	protected Number multiplyImpl(Number left, Number right) {
+		return toBigInteger(left).multiply(toBigInteger(right));
+	}
+
+	protected Number divideImpl(Number left, Number right) {
+		return BigDecimalMath.INSTANCE.divideImpl(left, right);
+	}
+	
+	protected int compareToImpl(Number left, Number right) {
+		return toBigInteger(left).compareTo(toBigInteger(right));
+	}
+
+    protected Number intdivImpl(Number left, Number right) {
+        return toBigInteger(left).divide(toBigInteger(right));
+    }
+    
+    protected Number modImpl(Number left, Number right) {
+        return toBigInteger(left).mod(toBigInteger(right));
+    }
+    
+    protected Number unaryMinusImpl(Number left) {
+        return toBigInteger(left).negate();
+    }
+
+    protected Number bitwiseNegateImpl(Number left) {
+        return toBigInteger(left).not();
+    }
+
+    protected Number orImpl(Number left, Number right) {
+        return toBigInteger(left).or(toBigInteger(right));
+    }
+
+    protected Number andImpl(Number left, Number right) {
+        return toBigInteger(left).and(toBigInteger(right));
+    }
+    
+    protected Number xorImpl(Number left, Number right) {
+        return toBigInteger(left).xor(toBigInteger(right));
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/ClassDistance.java b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/ClassDistance.java
new file mode 100644
index 0000000..cd6c277
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/ClassDistance.java
@@ -0,0 +1,160 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.runtime.typehandling;

+

+import java.io.Serializable;

+import java.math.BigDecimal;

+import java.math.BigInteger;

+import java.util.WeakHashMap;

+

+

+/*

+ * Class for calculating "distances" between classes. Such a distance

+ * is not a real distance to something but should be seen as the order

+ * classes and interfaces are choosen for method selection. The class

+ * will keep a weak cache and recalculate the distances on demand.

+ */

+public class ClassDistance {

+    private static final WeakHashMap CLASS_DISTANCES;

+    

+    private static class Entry {

+        

+    }

+    

+    private static class LinearEntry  extends Entry{

+        Class[] entries;

+        void concat(Class[] c,LinearEntry le){

+            entries = new Class[c.length+le.entries.length];

+            System.arraycopy(c,0,entries,0,c.length);

+            System.arraycopy(le.entries,0,entries,c.length,le.entries.length);

+        }

+        void concat(Class c,LinearEntry le){

+            entries = new Class[1+le.entries.length];

+            entries[0] = c;

+            System.arraycopy(le.entries,0,entries,1,le.entries.length);

+        }

+    }

+    

+    static {

+        CLASS_DISTANCES = new WeakHashMap();

+        initialPopulate();

+    }

+    

+    private static void initialPopulate() {

+        // int, double, byte, float, BigInteger, BigDecimal, long, short

+        // GString, char

+        

+        

+        LinearEntry object = new LinearEntry();

+        object.entries = new Class[]{Object.class};

+        CLASS_DISTANCES.put(Object.class,object);

+        

+        LinearEntry number = new LinearEntry();

+        number.concat(new Class[]{Number.class,Serializable.class},object);

+        CLASS_DISTANCES.put(Number.class,number);

+

+        LinearEntry compareableNumber = new LinearEntry();

+        compareableNumber.concat(Comparable.class,number);

+        

+        LinearEntry binteger = new LinearEntry();

+        binteger.concat(new Class[]{BigInteger.class, BigDecimal.class}, compareableNumber);

+        CLASS_DISTANCES.put(BigInteger.class,object);

+        

+        LinearEntry bdec = new LinearEntry();

+        binteger.concat(new Class[]{BigDecimal.class, BigInteger.class}, compareableNumber);

+        CLASS_DISTANCES.put(BigDecimal.class,object);

+        

+        

+        

+        // byte:

+        LinearEntry start = new LinearEntry();

+        start.entries =  new Class[]{

+                byte.class, Byte.class, short.class, Short.class,

+                int.class, Integer.class, long.class, Long.class,

+                BigInteger.class,

+                float.class, Float.class,  double.class, Double.class, 

+                BigDecimal.class,

+                Number.class,Object.class};

+        CLASS_DISTANCES.put(byte.class,start);

+        

+        // short:

+        start = new LinearEntry();

+        start.entries =  new Class[]{

+                short.class, Short.class,

+                int.class, Integer.class, long.class, Long.class,

+                BigInteger.class,

+                float.class, Float.class,  double.class, Double.class, 

+                BigDecimal.class,

+                Number.class,Object.class};

+        CLASS_DISTANCES.put(short.class,start);

+        

+        // int:

+        start = new LinearEntry();

+        start.entries =  new Class[]{

+                int.class, Integer.class, long.class, Long.class,

+                BigInteger.class,

+                float.class, Float.class,  double.class, Double.class, 

+                BigDecimal.class,

+                Number.class,Object.class};

+        CLASS_DISTANCES.put(int.class,start);

+        

+        // long:

+        start = new LinearEntry();

+        start.entries =  new Class[]{

+                long.class, Long.class,

+                BigInteger.class,

+                float.class, Float.class,  double.class, Double.class, 

+                BigDecimal.class,

+                Number.class,Object.class};

+        CLASS_DISTANCES.put(long.class,start);

+        

+        // Biginteger:

+        start = new LinearEntry();

+        start.entries =  new Class[]{

+                BigInteger.class,

+                float.class, Float.class,  double.class, Double.class, 

+                BigDecimal.class,

+                Number.class,Object.class};

+        CLASS_DISTANCES.put(long.class,start);

+        

+        // float:

+        start = new LinearEntry();

+        start.entries =  new Class[]{ 

+                byte.class, Byte.class, short.class, Short.class,

+                int.class, Integer.class, long.class, Long.class,

+                BigInteger.class,

+                float.class, Float.class,  double.class, Double.class, 

+                BigDecimal.class,

+                Number.class,Object.class};

+        CLASS_DISTANCES.put(float.class,start);

+        

+        // double:

+        start = new LinearEntry();

+        start.entries =  new Class[]{ 

+                double.class,

+                Double.class, BigDecimal.class,

+                Number.class,Object.class};

+        CLASS_DISTANCES.put(double.class,start);

+

+    }

+    

+    private static synchronized void popultate(Class clazz) {

+        if (CLASS_DISTANCES.get(clazz) != null) return;

+        

+    }

+    

+}

diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java
new file mode 100644
index 0000000..e49710b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java
@@ -0,0 +1,769 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.typehandling;
+
+import groovy.lang.GString;
+import groovy.lang.GroovyRuntimeException;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.runtime.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.lang.reflect.Modifier;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.*;
+import java.util.regex.Matcher;
+
+public class DefaultTypeTransformation {
+    
+    protected static final Object[] EMPTY_ARGUMENTS = {};
+    protected static final BigInteger ONE_NEG = new BigInteger("-1");
+    
+    //  --------------------------------------------------------
+    //                  unboxing methods
+    //  --------------------------------------------------------       
+    
+    public static byte byteUnbox(Object value) {
+        Number n = castToNumber(value);
+        return n.byteValue();
+    }
+
+    public static char charUnbox(Object value) {
+        return castToChar(value);
+    }
+
+    public static short shortUnbox(Object value) {
+        Number n = castToNumber(value);
+        return n.shortValue();
+    }
+
+    public static int intUnbox(Object value) {
+        Number n = castToNumber(value);
+        return n.intValue();
+    }
+
+    public static boolean booleanUnbox(Object value) {
+        return castToBoolean(value);
+    }
+
+    public static long longUnbox(Object value) {
+        Number n = castToNumber(value);
+        return n.longValue();
+    }
+
+    public static float floatUnbox(Object value) {
+        Number n = castToNumber(value);
+        return n.floatValue();
+    }
+
+    public static double doubleUnbox(Object value) {
+        Number n = castToNumber(value);
+        return n.doubleValue();
+    } 
+
+    //  --------------------------------------------------------
+    //                  boxing methods
+    //  --------------------------------------------------------       
+    
+    public static Object box(boolean value) {
+        return value ? Boolean.TRUE : Boolean.FALSE;
+    }
+
+    public static Object box(byte value) {
+        return new Byte(value);
+    }
+
+    public static Object box(char value) {
+        return new Character(value);
+    }
+
+    public static Object box(short value) {
+        return new Short(value);
+    }
+
+    public static Object box(int value) {
+        return IntegerCache.integerValue(value);
+    }
+
+    public static Object box(long value) {
+        return new Long(value);
+    }
+
+    public static Object box(float value) {
+        return new Float(value);
+    }
+
+    public static Object box(double value) {
+        return new Double(value);
+    }
+    
+    public static Number castToNumber(Object object) {
+        if (object instanceof Number) return (Number) object;
+        if (object instanceof Character) {
+            return new Integer(((Character) object).charValue());
+        } else if (object instanceof String) {
+            String c = (String) object;
+            if (c.length() == 1) {
+                return new Integer(c.charAt(0));
+            }
+            else {
+                throw new GroovyCastException(c,Integer.class);
+            }
+        }
+        throw new GroovyCastException(object,Number.class);
+    }
+    
+    public static boolean castToBoolean(Object object) {
+    	if (object == null) {
+    		return false;
+    	}
+    	else if (object instanceof Boolean) {
+            Boolean booleanValue = (Boolean) object;
+            return booleanValue.booleanValue();
+        }
+        else if (object instanceof Matcher) {
+            Matcher matcher = (Matcher) object;
+            RegexSupport.setLastMatcher(matcher);
+            return matcher.find();
+        }
+        else if (object instanceof Collection) {
+            Collection collection = (Collection) object;
+            return !collection.isEmpty();
+        }
+        else if (object instanceof Map) {
+            Map map = (Map) object;
+            return !map.isEmpty();
+        }
+        else if (object instanceof CharSequence) {
+        	CharSequence string =  (CharSequence) object;
+            return string.length() > 0;
+        } 
+        else if (object instanceof Object[]) {
+        	Object[] array =  (Object[]) object;
+            return array.length > 0;
+        } 
+        else if (object instanceof Character) {
+            Character c = (Character) object;
+            return c.charValue() != 0;
+        }
+        else if (object instanceof Number) {
+            Number n = (Number) object;
+            return n.doubleValue() != 0;
+        }
+        else {
+            return true;
+        }
+    }
+    
+    public static char castToChar(Object object) {
+        if (object instanceof Character) {
+            return ((Character) object).charValue();            
+        } else if (object instanceof Number) {
+            Number value = (Number) object;
+            return (char) value.intValue();
+        } else {
+            String text = object.toString();
+            if (text.length() == 1) {
+                return text.charAt(0);
+            }
+            else {
+                throw new GroovyCastException(text,char.class);
+            }
+        }
+    }
+    
+    public static Object castToType(Object object, Class type) {
+        if (object == null) {
+            return null;
+        }
+        
+        if (type == object.getClass()) return object;
+        // TODO we should move these methods to groovy method, like g$asType() so that
+        // we can use operator overloading to customize on a per-type basis
+        if (ReflectionCache.isArray(type)) {
+            return asArray(object, type);
+
+        }
+        if (ReflectionCache.isAssignableFrom(type,object.getClass())) {
+            return object;
+        }
+        if (Collection.class.isAssignableFrom(type)) {
+            int modifiers = type.getModifiers();
+            Collection answer;
+            if (object instanceof Collection && type.isAssignableFrom(HashSet.class) &&
+                    (type == HashSet.class || Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))) {
+                return new HashSet((Collection)object);
+            }
+            if (object.getClass().isArray()) {
+                if (type.isAssignableFrom(ArrayList.class) && (Modifier.isAbstract(modifiers) || Modifier.isInterface(modifiers))) {
+                    answer = new ArrayList();
+                } else {
+                    // lets call the collections constructor
+                    // passing in the list wrapper
+                    try {
+                        answer = (Collection) type.newInstance();
+                    }
+                    catch (Exception e) {
+                        throw new GroovyCastException("Could not instantiate instance of: " + type.getName() + ". Reason: " + e);
+                    }
+                }
+
+                // we cannot just wrap in a List as we support primitive type arrays
+                int length = Array.getLength(object);
+                for (int i = 0; i < length; i++) {
+                    Object element = Array.get(object, i);
+                    answer.add(element);
+                }
+                return answer;
+            }
+        }
+        if (type == String.class) {
+            return object.toString();
+        } else if (type == Character.class) {
+            return box(castToChar(object));
+        } else if (type == Boolean.class) {
+            return box(castToBoolean(object));
+        } else if (type == Class.class) {
+            return castToClass(object);
+        } else if (Number.class.isAssignableFrom(type)) {
+            Number n = castToNumber(object);
+            if (type == Byte.class) {
+                return new Byte(n.byteValue());
+            } else if (type == Character.class) {
+                return new Character((char) n.intValue());
+            } else if (type == Short.class) {
+                return new Short(n.shortValue());
+            } else if (type == Integer.class) {
+                return new Integer(n.intValue());
+            } else if (type == Long.class) {
+                return new Long(n.longValue());
+            } else if (type == Float.class) {
+                return new Float(n.floatValue());
+            } else if (type == Double.class) {
+                Double answer = new Double(n.doubleValue());
+                //throw a runtime exception if conversion would be out-of-range for the type.
+                if (!(n instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
+                        || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
+                    throw new GroovyRuntimeException("Automatic coercion of " + n.getClass().getName()
+                            + " value " + n + " to double failed.  Value is out of range.");
+                }
+                return answer;
+            } else if (type == BigDecimal.class) {
+                return new BigDecimal(n.toString());
+            } else if (type == BigInteger.class) {
+                if (object instanceof Float || object instanceof Double) {
+                    BigDecimal bd = new BigDecimal(n.doubleValue());
+                    return bd.toBigInteger();
+                } else if (object instanceof BigDecimal) {
+                    return ((BigDecimal) object).toBigInteger();
+                } else {
+                    return new BigInteger(n.toString());
+                }
+            }
+        } else if (type.isPrimitive()) {
+            if (type == boolean.class) {
+               return box(booleanUnbox(object)); 
+            } else if (type == byte.class) {
+                return box(byteUnbox(object));
+            } else if (type == char.class) {
+                return box(charUnbox(object));
+            } else if (type == short.class) {
+                return box(shortUnbox(object));
+            } else if (type == int.class) {
+                return box(intUnbox(object));
+            } else if (type == long.class) {
+                return box(longUnbox(object));
+            } else if (type == float.class) {
+                return box(floatUnbox(object));
+            } else if (type == double.class) {
+                Double answer = new Double(doubleUnbox(object));
+                //throw a runtime exception if conversion would be out-of-range for the type.
+                if (!(object instanceof Double) && (answer.doubleValue() == Double.NEGATIVE_INFINITY
+                        || answer.doubleValue() == Double.POSITIVE_INFINITY)) {
+                    throw new GroovyRuntimeException("Automatic coercion of " + object.getClass().getName()
+                            + " value " + object + " to double failed.  Value is out of range.");
+                }
+                return answer;
+            }
+        }
+        Object[] args = null;
+        if (object instanceof Collection) {
+            Collection list = (Collection) object;
+            args = list.toArray();
+        } else if (object instanceof Object[]) {
+            args = (Object[]) object;
+        } else if (object instanceof Map) {
+            // emulate named params constructor
+            args = new Object[1];
+            args[0] = object;
+        }
+        if (args != null) {
+            // lets try invoke the constructor with the list as arguments
+            // such as for creating a Dimension, Point, Color etc.
+            try {
+                return InvokerHelper.invokeConstructorOf(type, args);
+            } catch (InvokerInvocationException iie){
+                throw iie;
+            } catch (Exception e) {
+                // lets ignore exception and return the original object
+                // as the caller has more context to be able to throw a more
+                // meaningful exception
+            }
+        }
+        throw new GroovyCastException(object,type);
+    }
+
+    private static Class castToClass(Object object) {
+        try {
+            return Class.forName (object.toString());
+        } catch (Exception e) {
+            throw new GroovyCastException(object,Class.class);
+        }
+    }
+
+    public static Object asArray(Object object, Class type) {
+        Collection list = asCollection(object);
+        int size = list.size();
+        Class elementType = type.getComponentType();
+        Object array = Array.newInstance(elementType, size);
+        int idx = 0;
+
+        if (boolean.class.equals(elementType)) {
+            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
+                Object element = iter.next();
+                Array.setBoolean(array, idx, booleanUnbox(element));
+            }
+        }
+        else if (byte.class.equals(elementType)) {
+            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
+                Object element = iter.next();
+                Array.setByte(array, idx, byteUnbox(element));
+            }
+        }
+        else if (char.class.equals(elementType)) {
+            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
+                Object element = iter.next();
+                Array.setChar(array, idx, charUnbox(element));
+            }
+        }
+        else if (double.class.equals(elementType)) {
+            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
+                Object element = iter.next();
+                Array.setDouble(array, idx, doubleUnbox(element));
+            }
+        }
+        else if (float.class.equals(elementType)) {
+            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
+                Object element = iter.next();
+                Array.setFloat(array, idx, floatUnbox(element));
+            }
+        }
+        else if (int.class.equals(elementType)) {
+            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
+                Object element = iter.next();
+                Array.setInt(array, idx, intUnbox(element));
+            }
+        }
+        else if (long.class.equals(elementType)) {
+            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
+                Object element = iter.next();
+                Array.setLong(array, idx, longUnbox(element));
+            }
+        }
+        else if (short.class.equals(elementType)) {
+            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
+                Object element = iter.next();
+                Array.setShort(array, idx, shortUnbox(element));
+            }
+        }
+        else {
+            for (Iterator iter = list.iterator(); iter.hasNext(); idx++) {
+                Object element = iter.next();
+                Object coercedElement = castToType(element, elementType);
+                Array.set(array, idx, coercedElement);
+            }
+        }
+        return array;
+    }
+    
+    public static Collection asCollection(Object value) {
+        if (value == null) {
+            return Collections.EMPTY_LIST;
+        }
+        else if (value instanceof Collection) {
+            return (Collection) value;
+        }
+        else if (value instanceof Map) {
+            Map map = (Map) value;
+            return map.entrySet();
+        }
+        else if (value.getClass().isArray()) {
+            if (value.getClass().getComponentType().isPrimitive()) {
+                return primitiveArrayToList(value);
+            }
+            return Arrays.asList((Object[]) value);
+        }
+        else if (value instanceof MethodClosure) {
+            MethodClosure method = (MethodClosure) value;
+            IteratorClosureAdapter adapter = new IteratorClosureAdapter(method.getDelegate());
+            method.call(adapter);
+            return adapter.asList();
+        }
+        else if (value instanceof String) {
+            return DefaultGroovyMethods.toList((String) value);
+        }
+        else if (value instanceof GString) {
+            return DefaultGroovyMethods.toList(value.toString());
+        }
+        else if (value instanceof File) {
+            try {
+                return DefaultGroovyMethods.readLines((File) value);
+            }
+            catch (IOException e) {
+                throw new GroovyRuntimeException("Error reading file: " + value, e);
+            }
+        }
+        else {
+            // lets assume its a collection of 1
+            return Collections.singletonList(value);
+        }
+    }
+    
+    /**
+     * Allows conversion of arrays into a mutable List
+     *
+     * @return the array as a List
+     */
+    public static List primitiveArrayToList(Object array) {
+        int size = Array.getLength(array);
+        List list = new ArrayList(size);
+        for (int i = 0; i < size; i++) {
+            Object item = Array.get(array, i);
+            if (item != null && item.getClass().isArray() && item.getClass().getComponentType().isPrimitive()) {
+                item = primitiveArrayToList(item);
+            }
+            list.add(item);
+        }
+        return list;
+    }
+    
+    public static Object[] primitiveArrayBox(Object array) {
+        int size = Array.getLength(array);
+        Object[] ret = (Object[]) Array.newInstance(ReflectionCache.autoboxType(array.getClass().getComponentType()), size);
+        for (int i = 0; i < size; i++) {
+            ret[i]=Array.get(array, i);
+        }
+        return ret;
+    }
+    
+    /**
+     * Compares the two objects handling nulls gracefully and performing numeric type coercion if required
+     */
+    public static int compareTo(Object left, Object right) {
+        if (left == right) {
+            return 0;
+        }
+        if (left == null) {
+            return -1;
+        }
+        else if (right == null) {
+            return 1;
+        }
+        if (left instanceof Comparable) {
+            if (left instanceof Number) {
+                if (isValidCharacterString(right)) {
+                    return castToChar(left) - castToChar(right);
+                } else if (right instanceof Character || right instanceof Number) {
+                    return DefaultGroovyMethods.compareTo((Number) left, castToNumber(right));
+                }
+            }
+            else if (left instanceof Character) {
+                if (isValidCharacterString(right)) {
+                    return castToChar(left) - castToChar(right);
+                }
+                else if (right instanceof Number) {
+                    return castToChar(left) - castToChar(right);
+                }
+            }
+            else if (right instanceof Number) {
+                if (isValidCharacterString(left)) {
+                    return castToChar(left) - castToChar(right);
+                } 
+            }
+            else if (left instanceof String && right instanceof Character) {
+                return ((String) left).compareTo(right.toString());
+            }
+            else if (left instanceof String && right instanceof GString) {
+                return ((String) left).compareTo(right.toString());
+            }
+            Comparable comparable = (Comparable) left;
+            return comparable.compareTo(right);
+        }
+
+        throw new GroovyRuntimeException("Cannot compare " + left.getClass().getName() + " with value '" +
+                left + "' and " + right.getClass().getName() + " with value '" + right + "'");
+    }
+    
+    public static boolean compareEqual(Object left, Object right) {
+        if (left == right) return true;
+        if (left == null || right == null) return false;
+        if (left instanceof Comparable) {
+            return compareTo(left, right) == 0;
+        }
+        // handle arrays on both sides as special case for efficiency
+        Class leftClass = left.getClass();
+        Class rightClass = right.getClass();
+        if (leftClass.isArray() && rightClass.isArray()) {
+            return compareArrayEqual(left, right);
+        }
+        if (leftClass.isArray() && leftClass.getComponentType().isPrimitive()) {
+            left = primitiveArrayToList(left);
+        }
+        if (rightClass.isArray() && rightClass.getComponentType().isPrimitive()) {
+            right = primitiveArrayToList(right);
+        }
+        if (left instanceof Object[] && right instanceof List) {
+            return DefaultGroovyMethods.equals((Object[]) left, (List) right);
+        }
+        if (left instanceof List && right instanceof Object[]) {
+            return DefaultGroovyMethods.equals((List) left, (Object[]) right);
+        }
+        if (left instanceof List && right instanceof List) {
+            return DefaultGroovyMethods.equals((List) left, (List) right);
+        }
+        return ((Boolean) InvokerHelper.invokeMethod(left, "equals", right)).booleanValue();
+    }
+
+    public static boolean compareArrayEqual(Object left, Object right) {
+        if (left == null) {
+            return right == null;
+        }
+        if (right == null) {
+            return false;
+        }
+        if (Array.getLength(left) != Array.getLength(right)) {
+            return false;
+        }
+        for (int i = 0; i < Array.getLength(left); i++) {
+            Object l = Array.get(left, i);
+            Object r = Array.get(right, i);
+            if (!compareEqual(l, r)) return false;
+        }
+        return true;
+    }
+
+    /**
+     * @return true if the given value is a valid character string (i.e. has length of 1)
+     */
+    private static boolean isValidCharacterString(Object value) {
+        if (value instanceof String) {
+            String s = (String) value;
+            if (s.length() == 1) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static int[] convertToIntArray(Object a) {
+        int[] ans = null;
+
+        // conservative coding
+        if (a.getClass().getName().equals("[I")) {
+            ans = (int[]) a;
+        }
+        else {
+            Object[] ia = (Object[]) a;
+            ans = new int[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                if (ia[i] == null) {
+                    continue;
+                }
+                ans[i] = ((Number) ia[i]).intValue();
+            }
+        }
+        return ans;
+    }
+
+    public static boolean[] convertToBooleanArray(Object a) {
+        boolean[] ans = null;
+
+        // conservative coding
+        if (a.getClass().getName().equals("[Z")) {
+            ans = (boolean[]) a;
+        }
+        else {
+            Object[] ia = (Object[]) a;
+            ans = new boolean[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                if (ia[i] == null) {
+                    continue;
+                }
+                ans[i] = ((Boolean) ia[i]).booleanValue();
+            }
+        }
+        return ans;
+    }
+
+    public static byte[] convertToByteArray(Object a) {
+        byte[] ans = null;
+
+        // conservative coding
+        if (a.getClass().getName().equals("[B")) {
+            ans = (byte[]) a;
+        }
+        else {
+            Object[] ia = (Object[]) a;
+            ans = new byte[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                if (ia[i] != null) {
+                    ans[i] = ((Number) ia[i]).byteValue();
+                }
+            }
+        }
+        return ans;
+    }
+
+    public static short[] convertToShortArray(Object a) {
+        short[] ans = null;
+
+        // conservative coding
+        if (a.getClass().getName().equals("[S")) {
+            ans = (short[]) a;
+        }
+        else {
+            Object[] ia = (Object[]) a;
+            ans = new short[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                ans[i] = ((Number) ia[i]).shortValue();
+            }
+        }
+        return ans;
+    }
+
+    public static char[] convertToCharArray(Object a) {
+        char[] ans = null;
+
+        // conservative coding
+        if (a.getClass().getName().equals("[C")) {
+            ans = (char[]) a;
+        }
+        else {
+            Object[] ia = (Object[]) a;
+            ans = new char[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                if (ia[i] == null) {
+                    continue;
+                }
+                ans[i] = ((Character) ia[i]).charValue();
+            }
+        }
+        return ans;
+    }
+
+    public static long[] convertToLongArray(Object a) {
+        long[] ans = null;
+
+        // conservative coding
+        if (a.getClass().getName().equals("[J")) {
+            ans = (long[]) a;
+        }
+        else {
+            Object[] ia = (Object[]) a;
+            ans = new long[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                if (ia[i] == null) {
+                    continue;
+                }
+                ans[i] = ((Number) ia[i]).longValue();
+            }
+        }
+        return ans;
+    }
+
+    public static float[] convertToFloatArray(Object a) {
+        float[] ans = null;
+
+        // conservative coding
+        if (a.getClass().getName().equals("[F")) {
+            ans = (float[]) a;
+        }
+        else {
+            Object[] ia = (Object[]) a;
+            ans = new float[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                if (ia[i] == null) {
+                    continue;
+                }
+                ans[i] = ((Number) ia[i]).floatValue();
+            }
+        }
+        return ans;
+    }
+
+    public static double[] convertToDoubleArray(Object a) {
+        double[] ans = null;
+
+        // conservative coding
+        if (a.getClass().getName().equals("[D")) {
+            ans = (double[]) a;
+        }
+        else {
+            Object[] ia = (Object[]) a;
+            ans = new double[ia.length];
+            for (int i = 0; i < ia.length; i++) {
+                if (ia[i] == null) {
+                    continue;
+                }
+                ans[i] = ((Number) ia[i]).doubleValue();
+            }
+        }
+        return ans;
+    }
+
+    public static Object convertToPrimitiveArray(Object a, Class type) {
+        if (type == Byte.TYPE) {
+            return convertToByteArray(a);
+        }
+        if (type == Boolean.TYPE) {
+            return convertToBooleanArray(a);
+        }
+        if (type == Short.TYPE) {
+            return convertToShortArray(a);
+        }
+        if (type == Character.TYPE) {
+            return convertToCharArray(a);
+        }
+        if (type == Integer.TYPE) {
+            return convertToIntArray(a);
+        }
+        if (type == Long.TYPE) {
+            return convertToLongArray(a);
+        }
+        if (type == Float.TYPE) {
+            return convertToFloatArray(a);
+        }
+        if (type == Double.TYPE) {
+            return convertToDoubleArray(a);
+        }
+        else {
+            return a;
+        }
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/FloatingPointMath.java b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/FloatingPointMath.java
new file mode 100644
index 0000000..1bb527d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/FloatingPointMath.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.typehandling;
+
+/**
+ * FloatingPoint (Double and Float) NumberMath operations
+ * 
+ * @author Steve Goetze
+ */
+public class FloatingPointMath extends NumberMath {
+
+	public static final FloatingPointMath INSTANCE = new FloatingPointMath();
+	
+	private FloatingPointMath() {}
+				
+	protected Number absImpl(Number number) {
+		return new Double(Math.abs(number.doubleValue()));
+	}
+	
+	protected Number addImpl(Number left, Number right) {
+		return new Double(left.doubleValue() + right.doubleValue());
+	}
+
+	protected Number subtractImpl(Number left, Number right) {
+		return new Double(left.doubleValue() - right.doubleValue());
+	}
+
+	protected Number multiplyImpl(Number left, Number right) {
+		return new Double(left.doubleValue() * right.doubleValue());
+	}
+
+	protected Number divideImpl(Number left, Number right) {
+		return new Double(left.doubleValue() / right.doubleValue());
+	}
+	protected int compareToImpl(Number left, Number right) {
+		return Double.compare(left.doubleValue(), right.doubleValue());
+	}
+    
+    protected Number modImpl(Number left, Number right) {
+        return new Double(left.doubleValue() % right.doubleValue());
+    }
+    
+    protected Number unaryMinusImpl(Number left) {
+        return new Double(-left.doubleValue());
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/GroovyCastException.java b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/GroovyCastException.java
new file mode 100644
index 0000000..ce7c384
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/GroovyCastException.java
@@ -0,0 +1,41 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.runtime.typehandling;

+

+public class GroovyCastException extends ClassCastException {

+

+    public GroovyCastException(Object objectToCast, Class classToCastTo) {

+        super(makeMessage(objectToCast,classToCastTo));

+    }

+

+    public GroovyCastException(String string) {

+        super(string);

+    }

+

+    private static String makeMessage(Object objectToCast, Class classToCastTo) {

+       String classToCastFrom;

+       if (objectToCast!=null) {

+           classToCastFrom = objectToCast.getClass().getName();

+       } else {

+           objectToCast = "null";

+           classToCastFrom = "null";

+       }

+       return "Cannot cast object '" + objectToCast + "' " +

+              "with class '" + classToCastFrom + "' " +

+              "to class '" + classToCastTo.getName() + "'";

+    }

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/IntegerCache.java b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/IntegerCache.java
new file mode 100644
index 0000000..eff6105
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/IntegerCache.java
@@ -0,0 +1,36 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.runtime.typehandling;

+

+public class IntegerCache {

+    private IntegerCache(){}

+    

+    static final Integer CACHE[] = new Integer[-(-128) + 127 + 1];

+    

+    static {

+        for(int i = 0; i < CACHE.length; i++)

+            CACHE[i] = new Integer(i - 128);

+    }

+    

+    public static Integer integerValue(int i) {

+        final int offset = 128;

+        if (i >= -128 && i <= 127) { // must cache 

+            return CACHE[i + offset];

+        }

+        return new Integer(i);

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/IntegerMath.java b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/IntegerMath.java
new file mode 100644
index 0000000..46df5e2
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/IntegerMath.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.typehandling;
+
+/**
+ * Integer NumberMath operations
+ * 
+ * @author Steve Goetze
+ */
+public class IntegerMath extends NumberMath {
+
+	public static final IntegerMath INSTANCE = new IntegerMath();
+
+	private IntegerMath() {}
+					
+	protected Number absImpl(Number number) {
+		return new Integer(Math.abs(number.intValue()));
+	}
+	
+	protected Number addImpl(Number left, Number right) {
+		return new Integer(left.intValue() + right.intValue());
+	}
+
+	protected Number subtractImpl(Number left, Number right) {
+		return new Integer(left.intValue() - right.intValue());
+	}
+
+	protected Number multiplyImpl(Number left, Number right) {
+		return new Integer(left.intValue() * right.intValue());
+	}
+
+	protected Number divideImpl(Number left, Number right) {
+		return BigDecimalMath.INSTANCE.divideImpl(left, right);
+	}
+	
+	protected int compareToImpl(Number left, Number right) {
+		int leftVal = left.intValue();
+		int rightVal = right.intValue();
+		return (leftVal<rightVal ? -1 : (leftVal==rightVal ? 0 : 1));
+	}
+
+    protected Number orImpl(Number left, Number right) {
+        return new Integer(left.intValue() | right.intValue());
+    }
+
+    protected Number andImpl(Number left, Number right) {
+        return new Integer(left.intValue() & right.intValue());
+    }
+
+    protected Number xorImpl(Number left, Number right) {
+        return new Integer(left.intValue() ^ right.intValue());
+    }
+
+    protected Number intdivImpl(Number left, Number right) {
+        return new Integer(left.intValue() / right.intValue());
+    }
+	
+    protected Number modImpl(Number left, Number right) {
+        return new Integer(left.intValue() % right.intValue());
+    }
+
+    protected Number unaryMinusImpl(Number left) {
+        return new Integer(-left.intValue());
+    }
+
+    protected Number bitwiseNegateImpl(Number left) {
+        return new Integer(~left.intValue());
+    }
+
+    protected Number leftShiftImpl(Number left, Number right) {
+        return new Integer(left.intValue() << right.intValue());
+    }
+
+    protected Number rightShiftImpl(Number left, Number right) {
+        return new Integer(left.intValue() >> right.intValue());
+    }
+
+    protected Number rightShiftUnsignedImpl(Number left, Number right) {
+        return new Integer(left.intValue() >>> right.intValue());
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/LongMath.java b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/LongMath.java
new file mode 100644
index 0000000..63db86d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/LongMath.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.typehandling;
+
+/**
+ * Long NumberMath operations
+ * 
+ * @author Steve Goetze
+ */
+public class LongMath extends NumberMath {
+
+	public static final LongMath INSTANCE = new LongMath();
+
+	private LongMath() {}
+					
+	protected Number absImpl(Number number) {
+		return new Long(Math.abs(number.longValue()));
+	}
+	
+	protected Number addImpl(Number left, Number right) {
+		return new Long(left.longValue() + right.longValue());
+	}
+
+	protected Number subtractImpl(Number left, Number right) {
+		return new Long(left.longValue() - right.longValue());
+	}
+
+	protected Number multiplyImpl(Number left, Number right) {
+		return new Long(left.longValue() * right.longValue());
+	}
+
+	protected Number divideImpl(Number left, Number right) {
+		return BigDecimalMath.INSTANCE.divideImpl(left, right);
+	}
+	
+	protected int compareToImpl(Number left, Number right) {
+		long leftVal = left.longValue();
+		long rightVal = right.longValue();
+		return (leftVal<rightVal ? -1 : (leftVal==rightVal ? 0 : 1));
+	}
+
+	protected Number intdivImpl(Number left, Number right) {
+        return new Long(left.longValue() / right.longValue());
+	}
+	
+    protected Number modImpl(Number left, Number right) {
+        return new Long(left.longValue() % right.longValue());
+    }
+    
+    protected Number unaryMinusImpl(Number left) {
+        return new Long(-left.longValue());
+    }
+    
+    protected Number bitwiseNegateImpl(Number left) {
+        return new Long(~left.longValue());
+    }
+    
+    protected Number orImpl(Number left, Number right) {
+        return new Long(left.longValue() | right.longValue());
+    }
+
+    protected Number andImpl(Number left, Number right) {
+        return new Long(left.longValue() & right.longValue());
+    }
+    
+    protected Number xorImpl(Number left, Number right) {
+        return new Long(left.longValue() ^ right.longValue());
+    }
+    
+    protected Number leftShiftImpl(Number left, Number right) {
+        return new Long(left.longValue() << right.longValue());
+    }
+
+    protected Number rightShiftImpl(Number left, Number right) {
+        return new Long(left.longValue() >> right.longValue());
+    }
+
+    protected Number rightShiftUnsignedImpl(Number left, Number right) {
+        return new Long(left.longValue() >>> right.longValue());
+    }
+
+    protected Number bitAndImpl(Number left, Number right) {
+        return new Long(left.longValue() & right.longValue());
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/NumberMath.java b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/NumberMath.java
new file mode 100644
index 0000000..39c99fc
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/NumberMath.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime.typehandling;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+
+/**
+ * Stateless objects used to perform math on the various Number subclasses.
+ * Instances are required so that polymorphic calls work properly, but each
+ * subclass creates a singleton instance to minimize garbage.  All methods
+ * must be thread-safe.
+ * 
+ * The design goals of this class are as follows:
+ * <ol>
+ * <li>Support a 'least surprising' math model to scripting language users.  This
+ * means that exact, or decimal math should be used for default calculations.  This
+ * scheme assumes that by default, groovy literals with decimal points are instantiated
+ * as BigDecimal objects rather than binary floating points (Float, Double). 
+ * <li>Do not force the appearance of exactness on a number that is by definition not 
+ * guaranteed to be exact.  In particular this means that if an operand in a NumberMath 
+ * operation is a binary floating point number, ensure that the result remains a binary floating point 
+ * number (i.e. never automatically promote a binary floating point number to a BigDecimal).  
+ * This has the effect of preserving the expectations of binary floating point users and helps performance.
+ * <li>Provide an implementation that is as close as practical to the Java 1.5 BigDecimal math model 
+ * which implements precision based floating point decimal math (ANSI X3.274-1996 and 
+ * ANSI X3.274-1996/AM 1-2000 (section 7.4).  
+ * </ol>
+ * 
+ * @author Steve Goetze
+ */
+public abstract class NumberMath {
+		
+	public static Number abs(Number number) {
+		return getMath(number).absImpl(number);
+	}
+	
+	public static Number add(Number left, Number right) {
+		return getMath(left, right).addImpl(left,right);
+	}
+	
+	public static Number subtract(Number left, Number right) {
+		return getMath(left,right).subtractImpl(left,right);
+	}
+	
+	public static Number multiply(Number left, Number right) {
+		return getMath(left,right).multiplyImpl(left,right);
+	}
+	
+	public static Number divide(Number left, Number right) {
+		return getMath(left,right).divideImpl(left,right);
+ 	}
+ 	 
+	public static int compareTo(Number left, Number right) {
+		return getMath(left,right).compareToImpl(left, right);
+	}
+	
+    public static Number or(Number left, Number right) {
+        return getMath(left,right).orImpl(left, right);
+    }
+    
+    public static Number and(Number left, Number right) {
+        return getMath(left,right).andImpl(left, right);
+    }
+    
+    public static Number xor(Number left, Number right) {
+        return getMath(left,right).xorImpl(left, right);
+    }
+    
+	public static Number intdiv(Number left, Number right) {
+		return getMath(left,right).intdivImpl(left,right);
+ 	}
+
+	public static Number mod(Number left, Number right) {
+        return getMath(left,right).modImpl(left, right);
+    }
+
+    /**
+     * For this operation, consider the operands independently.  Throw an exception if the right operand
+     * (shift distance) is not an integral type.  For the left operand (shift value) also require an integral
+     * type, but do NOT promote from Integer to Long.  This is consistent with Java, and makes sense for the
+     * shift operators.
+     */
+    public static Number leftShift(Number left, Number right) {
+		if (isFloatingPoint(right) || isBigDecimal(right)) {
+	        throw new UnsupportedOperationException("Shift distance must be an integral type, but " +  right + " (" + right.getClass().getName() + ") was supplied");
+		}
+    	return getMath(left).leftShiftImpl(left,right);
+    }
+    
+    /**
+     * For this operation, consider the operands independently.  Throw an exception if the right operand
+     * (shift distance) is not an integral type.  For the left operand (shift value) also require an integral
+     * type, but do NOT promote from Integer to Long.  This is consistent with Java, and makes sense for the
+     * shift operators.
+     */
+    public static Number rightShift(Number left, Number right) {
+		if (isFloatingPoint(right) || isBigDecimal(right)) {
+	        throw new UnsupportedOperationException("Shift distance must be an integral type, but " +  right + " (" + right.getClass().getName() + ") was supplied");
+		}
+    	return getMath(left).rightShiftImpl(left,right);
+    }
+    
+    /**
+     * For this operation, consider the operands independently.  Throw an exception if the right operand
+     * (shift distance) is not an integral type.  For the left operand (shift value) also require an integral
+     * type, but do NOT promote from Integer to Long.  This is consistent with Java, and makes sense for the
+     * shift operators.
+     */
+    public static Number rightShiftUnsigned(Number left, Number right) {
+		if (isFloatingPoint(right) || isBigDecimal(right)) {
+	        throw new UnsupportedOperationException("Shift distance must be an integral type, but " +  right + " (" + right.getClass().getName() + ") was supplied");
+		}
+    	return getMath(left).rightShiftUnsignedImpl(left,right);
+    }
+    
+    public static Number unaryMinus(Number left) {
+        return getMath(left).unaryMinusImpl(left);
+    }
+    
+    public static boolean isFloatingPoint(Number number) {
+		return number instanceof Double || number instanceof Float;
+	}
+
+	public static boolean isInteger(Number number) {
+		return number instanceof Integer;
+	}
+
+	public static boolean isLong(Number number) {
+		return number instanceof Long;
+	}
+
+	public static boolean isBigDecimal(Number number) {
+		return number instanceof BigDecimal;
+	}
+
+	public static boolean isBigInteger(Number number) {
+		return number instanceof BigInteger;
+	}
+
+	public static BigDecimal toBigDecimal(Number n) {
+		return (n instanceof BigDecimal ? (BigDecimal) n : new BigDecimal(n.toString()));
+	}
+				
+	public static BigInteger toBigInteger(Number n) {
+		return (n instanceof BigInteger ? (BigInteger) n : new BigInteger(n.toString()));
+	}
+					
+	/**
+	 * Determine which NumberMath instance to use, given the supplied operands.  This method implements
+	 * the type promotion rules discussed in the documentation.  Note that by the time this method is
+	 * called, any Byte, Character or Short operands will have been promoted to Integer.  For reference,
+	 * here is the promotion matrix:
+	 *    bD bI  D  F  L  I
+	 * bD bD bD  D  D bD bD
+	 * bI bD bI  D  D bI bI
+	 *  D  D  D  D  D  D  D
+	 *  F  D  D  D  D  D  D
+	 *  L bD bI  D  D  L  L
+	 *  I bD bI  D  D  L  I
+	 * 
+	 * Note that for division, if either operand isFloatingPoint, the result will be floating.  Otherwise,
+	 * the result is BigDecimal
+	 */
+	private static NumberMath getMath(Number left, Number right) {
+		if (isFloatingPoint(left) || isFloatingPoint(right)) {
+			return FloatingPointMath.INSTANCE;
+		}
+		else if (isBigDecimal(left) || isBigDecimal(right)) {
+			return BigDecimalMath.INSTANCE;
+		}
+		else if (isBigInteger(left) || isBigInteger(right)) {
+			return BigIntegerMath.INSTANCE;
+		}
+		else if (isLong(left) || isLong(right)){
+			return LongMath.INSTANCE;
+		}
+		return IntegerMath.INSTANCE;
+	}
+
+	private static NumberMath getMath(Number number) {
+		if (isInteger(number)) {
+			return IntegerMath.INSTANCE;
+		}
+		else if (isLong(number)) {
+			return LongMath.INSTANCE;
+		}
+		else if (isFloatingPoint(number)) {
+			return FloatingPointMath.INSTANCE;
+		}			
+		else if (isBigDecimal(number)) {
+			return BigDecimalMath.INSTANCE;
+		}
+		else if (isBigInteger(number)) {
+			return BigIntegerMath.INSTANCE;
+		}
+		else {
+			throw new IllegalArgumentException("An unexpected Number subclass was supplied.");
+		}
+	}
+	
+	//Subclasses implement according to the type promotion hierarchy rules
+	protected abstract Number absImpl(Number number);
+	protected abstract Number addImpl(Number left, Number right);
+	protected abstract Number subtractImpl(Number left, Number right);
+	protected abstract Number multiplyImpl(Number left, Number right);
+	protected abstract Number divideImpl(Number left, Number right);
+	protected abstract int compareToImpl(Number left, Number right);
+    protected abstract Number unaryMinusImpl(Number left);
+
+
+    protected Number orImpl(Number left, Number right) {
+        throw createUnsupportedException("or()", left);
+    }
+    
+    protected Number andImpl(Number left, Number right) {
+        throw createUnsupportedException("and()", left);
+    }
+
+    protected Number xorImpl(Number left, Number right) {
+        throw createUnsupportedException("xor()", left);
+    }
+    
+    protected Number modImpl(Number left, Number right) {
+        throw createUnsupportedException("mod()", left);
+    }
+    
+    protected Number intdivImpl(Number left, Number right) {
+        throw createUnsupportedException("intdiv()", left);
+    }
+    
+    protected Number leftShiftImpl(Number left, Number right) {
+        throw createUnsupportedException("leftShift()", left);
+    }
+
+    protected Number rightShiftImpl(Number left, Number right) {
+        throw createUnsupportedException("rightShift()", left);
+    }
+
+    protected Number rightShiftUnsignedImpl(Number left, Number right) {
+        throw createUnsupportedException("rightShiftUnsigned()", left);
+    }
+
+    protected UnsupportedOperationException createUnsupportedException(String operation, Number left) {
+        return new UnsupportedOperationException("Cannot use " + operation + " on this number type: " + left.getClass().getName() + " with value: " + left);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/typehandling/package.html b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/package.html
new file mode 100644
index 0000000..9ee7a92
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/typehandling/package.html
@@ -0,0 +1,10 @@
+<html>

+  <head>

+    <title>package org.codehaus.groovy.runtime.typehandling*</title>

+  </head>

+  <body>

+    <p>Classes used to execute special actions based on the type. 

+    This includes mathematic operations and wrapper classes.

+    </p>

+  </body>

+</html>

diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/BooleanWrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/BooleanWrapper.java
new file mode 100644
index 0000000..873aa01
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/BooleanWrapper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class BooleanWrapper extends PojoWrapper {
+  public BooleanWrapper(final boolean wrapped) {
+    super(wrapped ? Boolean.TRUE : Boolean.FALSE, boolean.class);
+  }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/ByteWrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/ByteWrapper.java
new file mode 100644
index 0000000..d213ed8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/ByteWrapper.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class ByteWrapper extends PojoWrapper {
+  public ByteWrapper(final byte wrapped) {
+    super(new Byte(wrapped), byte.class);
+  }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/CharWrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/CharWrapper.java
new file mode 100644
index 0000000..6a71839
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/CharWrapper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class CharWrapper extends PojoWrapper {
+  public CharWrapper(final char wrapped) {
+    super(new Character(wrapped), char.class);
+  }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/DoubleWrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/DoubleWrapper.java
new file mode 100644
index 0000000..5ecbb35
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/DoubleWrapper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class DoubleWrapper extends PojoWrapper {
+  public DoubleWrapper(final double wrapped) {
+    super(new Double(wrapped), double.class);
+  }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/FloatWrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/FloatWrapper.java
new file mode 100644
index 0000000..ab45e5e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/FloatWrapper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class FloatWrapper extends PojoWrapper {
+  public FloatWrapper(final float wrapped) {
+    super(new Float(wrapped), float.class);
+  }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/GroovyObjectWrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/GroovyObjectWrapper.java
new file mode 100644
index 0000000..0e9d45f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/GroovyObjectWrapper.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.MetaClass;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class GroovyObjectWrapper extends Wrapper {
+  protected final GroovyObject wrapped;
+  
+  public GroovyObjectWrapper(final GroovyObject wrapped, final Class constrainedType) {
+    super(constrainedType);
+    this.wrapped = wrapped;
+  }
+  
+  public Object unwrap() {
+    return this.wrapped;
+  }
+  
+  /**
+   * Note the rest of these method will only be used post 1.0
+   */
+
+  /* (non-Javadoc)
+   * @see groovy.lang.GroovyObject#getProperty(java.lang.String)
+   */
+  public Object getProperty(final String property) {
+    return this.wrapped.getProperty(property);
+  }
+
+  /* (non-Javadoc)
+   * @see groovy.lang.GroovyObject#invokeMethod(java.lang.String, java.lang.Object)
+   */
+  public Object invokeMethod(final String name, final Object args) {
+    return this.wrapped.invokeMethod(name, args);
+  }
+
+  /* (non-Javadoc)
+   * @see groovy.lang.GroovyObject#setMetaClass(groovy.lang.MetaClass)
+   */
+  public void setMetaClass(final MetaClass metaClass) {
+    this.wrapped.setMetaClass(metaClass);
+  }
+
+  /* (non-Javadoc)
+   * @see groovy.lang.GroovyObject#setProperty(java.lang.String, java.lang.Object)
+   */
+  public void setProperty(final String property, final Object newValue) {
+    this.wrapped.setProperty(property, newValue);
+  }
+
+  protected Object getWrapped() {
+    return this.wrapped;
+  }
+
+  protected MetaClass getDelegatedMetaClass() {
+    return this.wrapped.getMetaClass();
+  }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/IntWrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/IntWrapper.java
new file mode 100644
index 0000000..7cd1cae
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/IntWrapper.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class IntWrapper extends PojoWrapper {
+  public IntWrapper(final int wrapped) {
+    super(new Integer(wrapped), int.class);
+  }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/LongWrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/LongWrapper.java
new file mode 100644
index 0000000..19d2576
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/LongWrapper.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class LongWrapper extends PojoWrapper {
+  public LongWrapper(final long wrapped) {
+    super(new Long(wrapped), long.class);
+  }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/PojoWrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/PojoWrapper.java
new file mode 100644
index 0000000..1a2a9f5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/PojoWrapper.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+import groovy.lang.MetaClass;
+
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class PojoWrapper extends Wrapper {
+  protected MetaClass delegate;
+  protected final Object wrapped;
+  
+  public PojoWrapper(final Object wrapped, final Class constrainedType) {
+    super(constrainedType);
+    this.wrapped = wrapped;
+    
+    /*
+     * This check is temporary - remove before 1.0 release
+     */
+//    if (wrapped instanceof GroovyObject) {
+//      throw new RuntimeException("trying to wrap the groovyObject "
+//                                 + wrapped.getClass().getName()
+//                                 + " in a PojoWrapper");
+//    }
+  }
+  
+  public Object unwrap() {
+    return this.wrapped;
+  }
+  
+  /**
+   * Note the rest of these method will only be used post 1.0
+   */
+
+  /* (non-Javadoc)
+   * @see groovy.lang.GroovyObject#getProperty(java.lang.String)
+   */
+  public Object getProperty(final String property) {
+    return this.delegate.getProperty(this.wrapped, property);
+  }
+
+  /* (non-Javadoc)
+   * @see groovy.lang.GroovyObject#invokeMethod(java.lang.String, java.lang.Object)
+   */
+  public Object invokeMethod(final String methodName, final Object arguments) {
+    return this.delegate.invokeMethod(this.wrapped, methodName, arguments);
+  }
+
+  /* (non-Javadoc)
+   * @see groovy.lang.GroovyObject#setMetaClass(groovy.lang.MetaClass)
+   */
+  public void setMetaClass(final MetaClass metaClass) {
+    this.delegate = metaClass;
+  }
+
+  /* (non-Javadoc)
+   * @see groovy.lang.GroovyObject#setProperty(java.lang.String, java.lang.Object)
+   */
+  public void setProperty(final String property, final Object newValue) {
+    this.delegate.setProperty(this.wrapped, property, newValue);
+  }
+
+  protected Object getWrapped() {
+    return this.wrapped;
+  }
+
+  protected MetaClass getDelegatedMetaClass() {
+    return this.delegate;
+  }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/ShortWrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/ShortWrapper.java
new file mode 100644
index 0000000..be4d74b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/ShortWrapper.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public class ShortWrapper extends PojoWrapper {
+  public ShortWrapper(final short wrapped) {
+    super(new Short(wrapped), short.class);
+  }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/Wrapper.java b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/Wrapper.java
new file mode 100644
index 0000000..2bfa77c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/Wrapper.java
@@ -0,0 +1,278 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.runtime.wrappers;
+
+//import java.lang.reflect.Constructor;
+//import java.lang.reflect.Method;
+//import java.util.List;
+//import java.util.Map;
+//
+//import org.codehaus.groovy.ast.ClassNode;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.MetaClass;
+//import groovy.lang.MetaMethod;
+
+/**
+ * @author John Wilson
+ *
+ */
+
+public abstract class Wrapper implements GroovyObject {
+    protected MetaClass delegatingMetaClass;
+    
+//  protected MetaClass delegatingMetaClass = new MetaClass(Object.class) {
+//    /**
+//     * @param obj
+//     * @see java.lang.Object#equals(java.lang.Object)
+//     */
+//    public boolean equals(Object obj) {
+//      return Wrapper.this.getDelegatedMetaClass().equals(obj);
+//    }
+//
+//    /**
+//     * @param object
+//     * @param attribute
+//     * @see groovy.lang.MetaClass#getAttribute(java.lang.Object, java.lang.String)
+//     */
+//    public Object getAttribute(Object object, String attribute) {
+//      return Wrapper.this.getDelegatedMetaClass().getAttribute(Wrapper.this.getWrapped(), attribute);
+//    }
+//
+//    /**
+//     * @see groovy.lang.MetaClass#getClassNode()
+//     */
+//    public ClassNode getClassNode() {
+//      return Wrapper.this.getDelegatedMetaClass().getClassNode();
+//    }
+//
+//    /**
+//     * @see groovy.lang.MetaClass#getMetaMethods()
+//     */
+//    public List getMetaMethods() {
+//      return Wrapper.this.getDelegatedMetaClass().getMetaMethods();
+//    }
+//
+//    /**
+//     * @see groovy.lang.MetaClass#getMethods()
+//     */
+//    public List getMethods() {
+//      return Wrapper.this.getDelegatedMetaClass().getMethods();
+//    }
+//
+//    /**
+//     * @see groovy.lang.MetaClass#getProperties()
+//     */
+//    public List getProperties() {
+//      return Wrapper.this.getDelegatedMetaClass().getProperties();
+//    }
+//
+//    /**
+//     * @param object
+//     * @param property
+//     * @see groovy.lang.MetaClass#getProperty(java.lang.Object, java.lang.String)
+//     */
+//    public Object getProperty(Object object, String property) {
+//      return Wrapper.this.getDelegatedMetaClass().getProperty(Wrapper.this.getWrapped(), property);
+//    }
+//
+//    /**
+//     * @see java.lang.Object#hashCode()
+//     */
+//    public int hashCode() {
+//      return Wrapper.this.getDelegatedMetaClass().hashCode();
+//    }
+//
+//    /**
+//     * @param arguments
+//     * @see groovy.lang.MetaClass#invokeConstructor(java.lang.Object[])
+//     */
+//    public Object invokeConstructor(Object[] arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().invokeConstructor(arguments);
+//    }
+//
+//    /**
+//     * @param at
+//     * @param arguments
+//     * @see groovy.lang.MetaClass#invokeConstructorAt(java.lang.Class, java.lang.Object[])
+//     */
+//    public Object invokeConstructorAt(Class at, Object[] arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().invokeConstructorAt(at, arguments);
+//    }
+//
+//    /**
+//     * @param object
+//     * @param methodName
+//     * @param arguments
+//     * @see groovy.lang.MetaClass#invokeMethod(java.lang.Object, java.lang.String, java.lang.Object)
+//     */
+//    public Object invokeMethod(Object object, String methodName, Object arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().invokeMethod(Wrapper.this.getWrapped(), methodName, arguments);
+//    }
+//
+//    /**
+//     * @param object
+//     * @param methodName
+//     * @param arguments
+//     * @see groovy.lang.MetaClass#invokeMethod(java.lang.Object, java.lang.String, java.lang.Object[])
+//     */
+//    public Object invokeMethod(Object object, String methodName, Object[] arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().invokeMethod(Wrapper.this.getWrapped(), methodName, arguments);
+//    }
+//
+//    /**
+//     * @param object
+//     * @param methodName
+//     * @param arguments
+//     * @see groovy.lang.MetaClass#invokeStaticMethod(java.lang.Object, java.lang.String, java.lang.Object[])
+//     */
+//    public Object invokeStaticMethod(Object object, String methodName, Object[] arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().invokeStaticMethod(Wrapper.this.getWrapped(), methodName, arguments);
+//    }
+//
+//    /**
+//     * @param arguments
+//     * @see groovy.lang.MetaClass#retrieveConstructor(java.lang.Class[])
+//     */
+//    public Constructor retrieveConstructor(Class[] arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().retrieveConstructor(arguments);
+//    }
+//
+//    /**
+//     * @param owner
+//     * @param methodName
+//     * @param arguments
+//     * @see groovy.lang.MetaClass#retrieveMethod(java.lang.Object, java.lang.String, java.lang.Object[])
+//     */
+//    public MetaMethod retrieveMethod(Object owner, String methodName, Object[] arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().retrieveMethod(owner, methodName, arguments);
+//    }
+//
+//    /**
+//     * @param methodName
+//     * @param arguments
+//     * @see groovy.lang.MetaClass#retrieveMethod(java.lang.String, java.lang.Class[])
+//     */
+//    public MetaMethod retrieveMethod(String methodName, Class[] arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().retrieveMethod(methodName, arguments);
+//    }
+//
+//    /**
+//     * @param methodName
+//     * @param arguments
+//     * @see groovy.lang.MetaClass#retrieveStaticMethod(java.lang.String, java.lang.Class[])
+//     */
+//    public MetaMethod retrieveStaticMethod(String methodName, Class[] arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().retrieveStaticMethod(methodName, arguments);
+//    }
+//
+//    /**
+//     * @param object
+//     * @param attribute
+//     * @param newValue
+//     * @see groovy.lang.MetaClass#setAttribute(java.lang.Object, java.lang.String, java.lang.Object)
+//     */
+//    public void setAttribute(Object object, String attribute, Object newValue) {
+//      Wrapper.this.getDelegatedMetaClass().setAttribute(Wrapper.this.getWrapped(), attribute, newValue);
+//    }
+//
+//    /**
+//     * @param bean
+//     * @param map
+//     * @see groovy.lang.MetaClass#setProperties(java.lang.Object, java.util.Map)
+//     */
+//    public void setProperties(Object bean, Map map) {
+//      Wrapper.this.getDelegatedMetaClass().setProperties(Wrapper.this.getWrapped(), map);
+//    }
+//
+//    /**
+//     * @param object
+//     * @param property
+//     * @param newValue
+//     * @see groovy.lang.MetaClass#setProperty(java.lang.Object, java.lang.String, java.lang.Object)
+//     */
+//    public void setProperty(Object object, String property, Object newValue) {
+//      Wrapper.this.getDelegatedMetaClass().setProperty(Wrapper.this.getWrapped(), property, newValue);
+//    }
+//
+//    /**
+//     * @see java.lang.Object#toString()
+//     */
+//    public String toString() {
+//      return Wrapper.this.getDelegatedMetaClass().toString();
+//    }
+//
+//    /* (non-Javadoc)
+//     * @see groovy.lang.MetaClass#addNewInstanceMethod(java.lang.reflect.Method)
+//     */
+//    public void addNewInstanceMethod(Method method) {
+//      Wrapper.this.getDelegatedMetaClass().addNewInstanceMethod(method);
+//    }
+//
+//    /* (non-Javadoc)
+//     * @see groovy.lang.MetaClass#addNewStaticMethod(java.lang.reflect.Method)
+//     */
+//    public void addNewStaticMethod(Method method) {
+//      Wrapper.this.getDelegatedMetaClass().addNewStaticMethod(method);
+//    }
+//
+//    /* (non-Javadoc)
+//     * @see groovy.lang.MetaClass#checkInitialised()
+//     */
+//    public void checkInitialised() {
+//      Wrapper.this.getDelegatedMetaClass().checkInitialised();
+//    }
+//
+//    /* (non-Javadoc)
+//     * @see groovy.lang.MetaClass#pickMethod(java.lang.Object, java.lang.String, java.lang.Object[])
+//     */
+//    public MetaMethod pickMethod(Object object, String methodName, Object[] arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().pickMethod(object, methodName, arguments);
+//    }
+//
+//    /* (non-Javadoc)
+//     * @see groovy.lang.MetaClass#pickMethod(java.lang.String, java.lang.Class[])
+//     */
+//    public MetaMethod pickMethod(String methodName, Class[] arguments) {
+//      return Wrapper.this.getDelegatedMetaClass().pickMethod(methodName, arguments);
+//    }
+//  };
+  
+  protected final Class constrainedType;
+  
+  public Wrapper(final Class constrainedType) {
+    this.constrainedType = constrainedType;
+  }
+
+  /* (non-Javadoc)
+   * @see groovy.lang.GroovyObject#getMetaClass()
+   * 
+   * This will only be useful post 1.0
+   */
+  public MetaClass getMetaClass() {
+    return this.delegatingMetaClass;
+  }
+  
+  public abstract Object unwrap();
+  
+  public Class getType() {
+    return this.constrainedType;
+  }
+  
+  protected abstract Object getWrapped();
+  protected abstract MetaClass getDelegatedMetaClass();
+}
diff --git a/groovy/src/main/org/codehaus/groovy/runtime/wrappers/package.html b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/package.html
new file mode 100644
index 0000000..1fd55af
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/runtime/wrappers/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.runtime.wrappers.*</title>
+  </head>
+  <body>
+    <p>Groovy wrapper classes for primitive types.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/ASTHelper.java b/groovy/src/main/org/codehaus/groovy/syntax/ASTHelper.java
new file mode 100644
index 0000000..3074b02
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/ASTHelper.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.syntax;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A common base class of AST helper methods which can be shared across the classic and new parsers
+ *
+ * @author Jochen Theodorou
+ * @author James Strachan
+ * @author Bob McWhirter
+ * @author Sam Pullara
+ * @author Chris Poirier
+ * @version $Revision$
+ */
+public class ASTHelper {
+
+    /** The SourceUnit controlling us */
+    private SourceUnit controller;
+
+    /** Our ClassLoader, which provides information on external types */
+    private ClassLoader classLoader;
+
+    /** Our imports, simple name => fully qualified name */
+    protected Map imports;
+    /** Our explicit static imports, simple name => fully qualified name */
+    protected Map staticImports;
+    /** Our implicit static imports */
+    protected List staticDotImports;
+    protected ModuleNode output;
+
+    /** The package name in which the module sits */
+    private String packageName;   //
+
+    // TODO should this really be static???
+    protected static Map resolutions = new HashMap();  // cleared on build(), to be safe
+
+//    private static String NOT_RESOLVED = new String();
+
+    /** temporarily store the class names that the current modulenode contains */
+    private final List newClasses = new ArrayList();
+
+    public ASTHelper(SourceUnit controller, ClassLoader classLoader) {
+        this();
+        this.controller = controller;
+        this.classLoader = classLoader;
+    }
+
+    public ASTHelper() {
+        imports = new HashMap();
+        staticImports = new HashMap();
+        staticDotImports = new ArrayList();
+    }
+
+    public String getPackageName() {
+        return packageName;
+    }
+
+    public void setPackageName(String packageName) {
+        this.packageName = packageName;
+        if (packageName!=null && packageName.length()>0){
+            packageName+='.';
+        }
+        output.setPackageName(packageName);
+    }
+
+
+    /**
+     * Returns our class loader (as supplied on construction).
+     */
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    public void setClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    public SourceUnit getController() {
+        return controller;
+    }
+
+    public void setController(SourceUnit controller) {
+        this.controller = controller;
+    }
+    
+    /**
+     * Returns a fully qualified name for any given potential type
+     * name.  Returns null if no qualified name could be determined.
+     */
+/*    protected String resolveName(String name, boolean safe) {
+        //
+        // Use our cache of resolutions, if possible
+
+        String resolution = (String) resolutions.get(name);
+        if (NOT_RESOLVED.equals(resolution)) {
+            return (safe ? name : null);
+        }
+        else if (resolution != null) {
+            return (String) resolution;
+        }
+
+        try {
+            getClassLoader().loadClass(name);
+            resolutions.put(name,name);
+            return name;
+        } catch (ClassNotFoundException cnfe){
+            if (cnfe.getCause() instanceof MultipleCompilationErrorsException) {
+                MultipleCompilationErrorsException mcee = (MultipleCompilationErrorsException) cnfe.getCause();
+                controller.getErrorCollector().addCollectorContents(mcee.getErrorCollector());
+                resolutions.put(name,name);
+                return name;
+            }
+        } catch (NoClassDefFoundError ncdfe) {
+            //fall through
+        }
+
+        do {
+            //
+            // If the type name contains a ".", it's probably fully
+            // qualified, and we don't take it to verification here.
+
+            if (name.indexOf(".") >= 0) {
+                resolution = name;
+                break;                                            // <<< FLOW CONTROL <<<<<<<<<
+            }
+
+
+            //
+            // Otherwise, we'll need the scalar type for checking, and
+            // the postfix for reassembly.
+
+            String scalar = name, postfix = "";
+            while (scalar.endsWith("[]")) {
+                scalar = scalar.substring(0, scalar.length() - 2);
+                postfix += "[]";
+            }
+
+
+            //
+            // Primitive types are all valid...
+
+            if (Types.ofType(Types.lookupKeyword(scalar), Types.PRIMITIVE_TYPE)) {
+                resolution = name;
+                break;                                            // <<< FLOW CONTROL <<<<<<<<<
+            }
+
+
+            //
+            // Next, check our imports and return the qualified name,
+            // if available.
+
+            if (this.imports.containsKey(scalar)) {
+                resolution = ((String) this.imports.get(scalar)) + postfix;
+                break;                                            // <<< FLOW CONTROL <<<<<<<<<
+            }
+
+
+            //
+            // Next, see if our class loader can resolve it in the current package.
+
+            if (packageName != null && packageName.length() > 0) {
+                try {
+                    getClassLoader().loadClass(dot(packageName, scalar));
+                    resolution = dot(packageName, name);
+
+                    break;                                        // <<< FLOW CONTROL <<<<<<<<<
+                } catch (ClassNotFoundException cnfe){
+                    if (cnfe.getCause() instanceof CompilationFailedException) {
+                        resolution = dot(packageName, name);
+                        break;
+                    }
+                } catch (NoClassDefFoundError ncdfe) {
+                    //fall through
+                }
+            }
+
+            // search the package imports path
+            List packageImports = output.getImportPackages();
+            for (int i = 0; i < packageImports.size(); i++) {
+                String pack = (String) packageImports.get(i);
+                String clsName = pack + name;
+                try {
+                    getClassLoader().loadClass(clsName);
+                    resolution = clsName;
+                    break;
+                } catch (ClassNotFoundException cnfe){
+                    if (cnfe.getCause() instanceof CompilationFailedException) {
+                        resolution = clsName;
+                        break;
+                    }
+                } catch (NoClassDefFoundError ncdfe) {
+                    //fall through
+                }
+            }
+            if (resolution != null) {
+                break;
+            }
+
+            //
+            // Last chance, check the default imports.
+
+            for (int i = 0; i < DEFAULT_IMPORTS.length; i++) {
+                String qualified = DEFAULT_IMPORTS[i] + scalar;
+                try {
+                    getClassLoader().loadClass(qualified);
+
+                    resolution = qualified + postfix;
+                    break;                                        // <<< FLOW CONTROL <<<<<<<<<
+                } catch (ClassNotFoundException cnfe){
+                    if (cnfe.getCause() instanceof CompilationFailedException) {
+                        resolution = qualified + postfix;
+                        break;
+                    }
+                } catch (NoClassDefFoundError ncdfee) {
+                    // fall through
+                }
+            }
+
+        }
+        while (false);
+
+
+        //
+        // Cache the solution and return it
+
+        if (resolution == null) {
+            resolutions.put(name, NOT_RESOLVED);
+            return (safe ? name : null);
+        }
+        else {
+            resolutions.put(name, resolution);
+            return resolution;
+        }
+    }
+*/
+    
+    /**
+     * Returns two names joined by a dot.  If the base name is
+     * empty, returns the name unchanged.
+     */
+    public static String dot(String base, String name) {
+        if (base != null && base.length() > 0) {
+            return base + "." + name;
+        }
+
+        return name;
+    }
+
+    protected void makeModule() {
+        this.newClasses.clear();
+        this.output = new ModuleNode(controller);
+        resolutions.clear();
+    }
+
+    /**
+     * A synonym for <code>dot( base, "" )</code>.
+     */
+    protected String dot(String base) {
+        return dot(base, "");
+    }
+
+    /*protected String resolveNewClassOrName(String name, boolean safe) {
+        if (this.newClasses.contains(name)) {
+            return dot(packageName, name);
+        }
+        else {
+            return resolveName(name, safe);
+        }
+    }*/
+
+    protected void addNewClassName(String name) {
+        this.newClasses.add(name);
+    }
+
+    protected void importClass(ClassNode type, String name, String as) {
+        if (as == null) as=name;
+
+        output.addImport(as, type); 
+        imports.put(as, type);
+    }
+
+    protected void staticImportMethodOrField(ClassNode type, String name, String alias) {
+        if (alias == null) alias = name;
+        output.addStaticMethodOrField(type, name, alias);
+    }
+
+    protected void staticImportClassWithStar(ClassNode type, String importClass) {
+        // keep track of the fact that it was a static import
+        output.addStaticImportClass(importClass, type);
+    }
+
+    protected void importPackageWithStar(String importPackage) {
+        String[] classes = output.addImportPackage( dot(importPackage) );
+        for( int i = 0; i < classes.length; i++ ) {
+            imports.put( classes[i], dot(importPackage, classes[i]) );
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/CSTNode.java b/groovy/src/main/org/codehaus/groovy/syntax/CSTNode.java
new file mode 100644
index 0000000..859e921
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/CSTNode.java
@@ -0,0 +1,583 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.syntax;
+
+import org.codehaus.groovy.GroovyBugError;
+
+import java.io.StringWriter;
+import java.io.PrintWriter;
+
+
+/**
+ *  An abstract base class for nodes in the concrete syntax tree that is
+ *  the result of parsing.  Note that the CSTNode is inextricably linked
+ *  with the Token in that every CSTNode has a Token as it's root.
+ *
+ *  @see antlr.Parser
+ *  @see Token
+ *  @see org.codehaus.groovy.syntax.Reduction
+ *  @see org.codehaus.groovy.syntax.Types
+ *
+ *  @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public abstract class CSTNode
+{
+
+  //---------------------------------------------------------------------------
+  // NODE IDENTIFICATION AND MEANING
+
+
+   /**
+    *  Returns the meaning of this node.  If the node isEmpty(), returns
+    *  the type of Token.NULL.
+    */
+
+    public int getMeaning()
+    {
+        return getRoot( true ).getMeaning();
+    }
+
+
+
+   /**
+    *  Sets the meaning for this node (and it's root Token).  Not
+    *  valid if the node isEmpty().  Returns the node, for convenience.
+    */
+
+    public CSTNode setMeaning( int meaning )
+    {
+        getRoot().setMeaning( meaning );
+        return this;
+    }
+
+
+
+   /**
+    *  Returns the actual type of the node.  If the node isEmpty(), returns
+    *  the type of Token.NULL.
+    */
+
+    public int getType()
+    {
+        return getRoot( true ).getType();
+    }
+
+
+
+   /**
+    *  Returns true if the node can be coerced to the specified type.
+    */
+
+    public boolean canMean( int type )
+    {
+        return Types.canMean( getMeaning(), type );
+    }
+
+
+
+   /**
+    *  Returns true if the node's meaning matches the specified type.
+    */
+
+    public boolean isA( int type )
+    {
+        return Types.ofType( getMeaning(), type );
+    }
+
+
+
+   /**
+    *  Returns true if the node's meaning matches any of the specified types.
+    */
+
+    public boolean isOneOf( int[] types )
+    {
+        int meaning = getMeaning();
+        for( int i = 0; i < types.length; i++ )
+        {
+            if( Types.ofType(meaning, types[i]) )
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+
+   /**
+    *  Returns true if the node's meaning matches all of the specified types.
+    */
+
+    public boolean isAllOf( int[] types )
+    {
+        int meaning = getMeaning();
+        for( int i = 0; i < types.length; i++ )
+        {
+            if( !Types.ofType(meaning, types[i]) )
+            {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+
+   /**
+    *  Returns the first matching meaning of the specified types.
+    *  Returns Types.UNKNOWN if there are no matches.
+    */
+
+    public int getMeaningAs( int[] types )
+    {
+
+        for( int i = 0; i < types.length; i++ )
+        {
+            if( isA(types[i]) )
+            {
+                return types[i];
+            }
+        }
+
+        return Types.UNKNOWN;
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // TYPE SUGAR
+
+
+   /**
+    *  Returns true if the node matches the specified type.  Effectively
+    *  a synonym for <code>isA()</code>.  Missing nodes are Token.NULL.
+    */
+
+    boolean matches( int type )
+    {
+        return isA(type);
+    }
+
+
+
+   /**
+    *  Returns true if the node and it's first child match the specified
+    *  types.  Missing nodes are Token.NULL.
+    */
+
+    boolean matches( int type, int child1 )
+    {
+        return isA(type) && get(1, true).isA(child1);
+    }
+
+
+
+   /**
+    *  Returns true if the node and it's first and second child match the
+    *  specified types.  Missing nodes are Token.NULL.
+    */
+
+    boolean matches( int type, int child1, int child2 )
+    {
+        return matches( type, child1 ) && get(2, true).isA(child2);
+    }
+
+
+
+   /**
+    *  Returns true if the node and it's first three children match the
+    *  specified types.  Missing nodes are Token.NULL.
+    */
+
+    boolean matches( int type, int child1, int child2, int child3 )
+    {
+        return matches( type, child1, child2 ) && get(3, true).isA(child3);
+    }
+
+
+
+   /**
+    *  Returns true if the node an it's first four children match the
+    *  specified types.  Missing nodes have type Types.NULL.
+    */
+
+    boolean matches( int type, int child1, int child2, int child3, int child4 )
+    {
+        return matches( type, child1, child2, child3 ) && get(4, true).isA(child4);
+    }
+
+
+
+
+
+  //---------------------------------------------------------------------------
+  // MEMBER ACCESS
+
+
+   /**
+    *  Returns true if the node is completely empty (no root, even).
+    */
+
+    public boolean isEmpty()
+    {
+        return false;
+    }
+
+
+
+   /**
+    *  Returns the number of elements in the node (including root).
+    */
+
+    public abstract int size();
+
+
+
+   /**
+    *  Returns true if the node has any non-root elements.
+    */
+
+    public boolean hasChildren()
+    {
+        return children() > 0;
+    }
+
+
+
+   /**
+    *  Returns the number of non-root elements in the node.
+    */
+
+    public int children()
+    {
+        int size = size();
+        if( size > 1 )
+        {
+            return size - 1;
+        }
+        return 0;
+    }
+
+
+
+   /**
+    *  Returns the specified element, or null.
+    */
+
+    public abstract CSTNode get( int index );
+
+
+
+   /**
+    *  Returns the specified element, or Token.NULL if
+    *  safe is set and the specified element is null (or doesn't
+    *  exist).
+    */
+
+    public CSTNode get( int index, boolean safe )
+    {
+        CSTNode element = get( index );
+
+        if( element == null && safe )
+        {
+            element = Token.NULL;
+        }
+
+        return element;
+    }
+
+
+
+   /**
+    *  Returns the root of the node.  By convention, all nodes have
+    *  a Token as the first element (or root), which indicates the type
+    *  of the node.  May return null if the node <code>isEmpty()</code>.
+    */
+
+    public abstract Token getRoot();
+
+
+
+   /**
+    *  Returns the root of the node, the Token that indicates it's
+    *  type.  Returns a Token.NULL if safe and the actual root is null.
+    */
+
+    public Token getRoot( boolean safe )
+    {
+        Token root = getRoot();
+
+        if( root == null && safe )
+        {
+            root = Token.NULL;
+        }
+
+        return root;
+    }
+
+
+
+   /**
+    *  Returns the text of the root.  Uses <code>getRoot(true)</code>
+    *  to get the root, so you will only receive null in return if the
+    *  root token returns it.
+    */
+
+    public String getRootText()
+    {
+        Token root = getRoot( true );
+        return root.getText();
+    }
+
+
+
+   /**
+    *  Returns a description of the node.
+    */
+
+    public String getDescription()
+    {
+        return Types.getDescription( getMeaning() );
+    }
+
+
+
+   /**
+    *  Returns the starting line of the node.  Returns -1
+    *  if not known.
+    */
+
+    public int getStartLine()
+    {
+        return getRoot(true).getStartLine();
+    }
+
+
+
+   /**
+    *  Returns the starting column of the node.  Returns -1
+    *  if not known.
+    */
+
+    public int getStartColumn()
+    {
+        return getRoot(true).getStartColumn();
+    }
+
+
+
+   /**
+    *  Marks the node a complete expression.  Not all nodes support
+    *  this operation!
+    */
+
+    public void markAsExpression()
+    {
+        throw new GroovyBugError( "markAsExpression() not supported for this CSTNode type" );
+    }
+
+
+
+   /**
+    *  Returns true if the node is a complete expression.
+    */
+
+    public boolean isAnExpression()
+    {
+        return isA(Types.SIMPLE_EXPRESSION);
+    }
+
+
+
+
+
+  //---------------------------------------------------------------------------
+  // OPERATIONS
+
+
+   /**
+    *  Adds an element to the node.  Returns the element for convenience.
+    *  Not all nodes support this operation!
+    */
+
+    public CSTNode add( CSTNode element )
+    {
+        throw new GroovyBugError( "add() not supported for this CSTNode type" );
+    }
+
+
+
+   /**
+    *  Adds all children of the specified node to this one.  Not all
+    *  nodes support this operation!
+    */
+
+    public void addChildrenOf( CSTNode of )
+    {
+        for( int i = 1; i < of.size(); i++ )
+        {
+            add( of.get(i) );
+        }
+    }
+
+
+
+   /**
+    *  Sets an element node in at the specified index.  Returns the element
+    *  for convenience.  Not all nodes support this operation!
+    */
+
+    public CSTNode set( int index, CSTNode element )
+    {
+        throw new GroovyBugError( "set() not supported for this CSTNode type" );
+    }
+
+
+
+   /**
+    *  Creates a <code>Reduction</code> from this node.  Returns self if the
+    *  node is already a <code>Reduction</code>.
+    */
+
+    public abstract Reduction asReduction();
+
+
+
+
+  //---------------------------------------------------------------------------
+  // STRING CONVERSION
+
+
+   /**
+    *  Formats the node as a <code>String</code> and returns it.
+    */
+
+    public String toString()
+    {
+        StringWriter string = new StringWriter();
+        write( new PrintWriter(string) );
+
+        string.flush();
+        return string.toString();
+    }
+
+
+   /**
+    *  Formats the node and writes it to the specified <code>Writer</code>.
+    */
+
+    public void write( PrintWriter writer )
+    {
+        write( writer, "" );
+    }
+
+
+   /**
+    *  Formats the node and writes it to the specified <code>Writer</code>.
+    *  The indent is prepended to each output line, and is increased for each
+    *  recursion.
+    */
+
+    protected void write( PrintWriter writer, String indent )
+    {
+        writer.print( "(" );
+
+        if( !isEmpty() )
+        {
+            Token  root = getRoot( true );
+            int    type = root.getType();
+            int meaning = root.getMeaning();
+
+
+            //
+            // Display our type, text, and (optional) meaning
+
+            writer.print( Types.getDescription(type) );
+
+            if( meaning != type )
+            {
+                writer.print( " as " );
+                writer.print( Types.getDescription(meaning) );
+            }
+
+            if( getStartLine() > -1 )
+            {
+                writer.print( " at " + getStartLine() + ":" + getStartColumn() );
+            }
+
+            String text = root.getText();
+            int  length = text.length();
+            if( length > 0 )
+            {
+                writer.print( ": " );
+                if( length > 40 )
+                {
+                   text = text.substring( 0, 17 ) + "..." + text.substring( length - 17, length );
+                }
+
+                writer.print( " \"" );
+                writer.print( text );
+                writer.print( "\" " );
+            }
+            else if( children() > 0 )
+            {
+                writer.print( ": " );
+            }
+
+
+
+            //
+            // Recurse to display the children.
+
+            int count = size();
+            if( count > 1 )
+            {
+                writer.println( "" );
+
+                String indent1 = indent + "  ";
+                String indent2 = indent + "   ";
+                for( int i = 1; i < count; i++ )
+                {
+                    writer.print( indent1 );
+                    writer.print( i );
+                    writer.print( ": " );
+
+                    get( i, true ).write( writer, indent2 );
+                }
+
+                writer.print( indent );
+            }
+        }
+
+        if( indent.length() > 0 )
+        {
+            writer.println( ")" );
+        }
+        else
+        {
+            writer.print( ")" );
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/Numbers.java b/groovy/src/main/org/codehaus/groovy/syntax/Numbers.java
new file mode 100644
index 0000000..460e3d1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/Numbers.java
@@ -0,0 +1,295 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.syntax;
+
+import java.math.BigInteger;
+import java.math.BigDecimal;
+
+/**
+ *  Helper class for processing Groovy numeric literals.
+ *
+ *  @author Brian Larson
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class Numbers
+{
+
+
+
+  //---------------------------------------------------------------------------
+  // LEXING SUPPORT
+
+
+   /**
+    *  Returns true if the specified character is a base-10 digit.
+    */
+
+    public static boolean isDigit( char c )
+    {
+        return c >= '0' && c <= '9';
+    }
+
+
+   /**
+    *  Returns true if the specific character is a base-8 digit.
+    */
+
+    public static boolean isOctalDigit( char c )
+    {
+        return c >= '0' && c <= '7';
+    }
+
+
+   /**
+    *  Returns true if the specified character is a base-16 digit.
+    */
+
+    public static boolean isHexDigit( char c )
+    {
+        return isDigit(c) || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f');
+    }
+
+
+
+   /**
+    *  Returns true if the specified character is a valid type specifier
+    *  for a numeric value.
+    */
+
+    public static boolean isNumericTypeSpecifier( char c, boolean isDecimal )
+    {
+        if( isDecimal )
+        {
+            switch( c )
+            {
+                case 'G':
+                case 'g':
+                case 'D':
+                case 'd':
+                case 'F':
+                case 'f':
+                    return true;
+            }
+        }
+        else
+        {
+            switch( c )
+            {
+                case 'G':
+                case 'g':
+                case 'I':
+                case 'i':
+                case 'L':
+                case 'l':
+                    return true;
+            }
+        }
+
+        return false;
+    }
+
+
+
+
+
+  //---------------------------------------------------------------------------
+  // PARSING SUPPORT
+
+
+    private static final BigInteger MAX_LONG    = BigInteger.valueOf(Long.MAX_VALUE);
+    private static final BigInteger MIN_LONG    = BigInteger.valueOf(Long.MIN_VALUE);
+
+    private static final BigInteger MAX_INTEGER = BigInteger.valueOf(Integer.MAX_VALUE);
+    private static final BigInteger MIN_INTEGER = BigInteger.valueOf(Integer.MIN_VALUE);
+
+    private static final BigDecimal MAX_DOUBLE  = new BigDecimal(String.valueOf(Double.MAX_VALUE));
+    private static final BigDecimal MIN_DOUBLE  = MAX_DOUBLE.negate();
+
+    private static final BigDecimal MAX_FLOAT   = new BigDecimal(String.valueOf(Float.MAX_VALUE));
+    private static final BigDecimal MIN_FLOAT   = MAX_FLOAT.negate();
+
+
+
+   /**
+    *  Builds a Number from the given integer descriptor.  Creates the narrowest
+    *  type possible, or a specific type, if specified.
+    *
+    *  @param  text literal text to parse
+    *  @return instantiated Number object
+    *  @throws NumberFormatException if the number does not fit within the type
+    *          requested by the type specifier suffix (invalid numbers don't make
+    *          it here)
+    */
+
+    public static Number parseInteger( String text )
+    {
+        char c = ' ';
+        int length = text.length();
+
+
+        //
+        // Strip off the sign, if present
+
+        boolean negative = false;
+        if( (c = text.charAt(0)) == '-' || c == '+' )
+        {
+            negative = (c == '-');
+            text = text.substring( 1, length );
+            length -= 1;
+        }
+
+
+        //
+        // Determine radix (default is 10).
+
+        int radix = 10;
+        if( text.charAt(0) == '0' && length > 1 )
+        {
+            if( (c = text.charAt(1)) == 'X' || c == 'x' )
+            {
+                radix = 16;
+                text = text.substring( 2, length);
+                length -= 2;
+            }
+            else
+            {
+                radix = 8;
+            }
+        }
+
+
+        //
+        // Strip off any type specifier and convert it to lower
+        // case, if present.
+
+        char type = 'x';  // pick best fit
+        if( isNumericTypeSpecifier(text.charAt(length-1), false) )
+        {
+            type = Character.toLowerCase( text.charAt(length-1) );
+            text = text.substring( 0, length-1);
+
+            length -= 1;
+        }
+
+
+        //
+        // Add the sign back, if necessary
+
+        if( negative )
+        {
+            text = "-" + text;
+        }
+
+
+        //
+        // Build the specified type or, if no type was specified, the
+        // smallest type in which the number will fit.
+
+        switch (type)
+        {
+            case 'i':
+                return new Integer( Integer.parseInt(text, radix) );
+
+            case 'l':
+                return new Long( Long.parseLong(text, radix) );
+
+            case 'g':
+                return new BigInteger( text, radix );
+
+            default:
+
+                //
+                // If not specified, we will return the narrowest possible
+                // of Integer, Long, and BigInteger.
+
+                BigInteger value = new BigInteger( text, radix );
+
+                if( value.compareTo(MAX_INTEGER) <= 0 && value.compareTo(MIN_INTEGER) >= 0 )
+                {
+                    return new Integer(value.intValue());
+                }
+                else if( value.compareTo(MAX_LONG) <= 0 && value.compareTo(MIN_LONG) >= 0 )
+                {
+                    return new Long(value.longValue());
+                }
+
+                return value;
+        }
+    }
+
+
+
+   /**
+    *  Builds a Number from the given decimal descriptor.  Uses BigDecimal,
+    *  unless, Double or Float is requested.
+    *
+    *  @param  text literal text to parse
+    *  @return instantiated Number object
+    *  @throws NumberFormatException if the number does not fit within the type
+    *          requested by the type specifier suffix (invalid numbers don't make
+    *          it here)
+    */
+
+    public static Number parseDecimal( String text )
+    {
+        int length = text.length();
+
+
+        //
+        // Strip off any type specifier and convert it to lower
+        // case, if present.
+
+        char type = 'x';
+        if( isNumericTypeSpecifier(text.charAt(length-1), true) )
+        {
+            type = Character.toLowerCase( text.charAt(length-1) );
+            text = text.substring( 0, length-1 );
+
+            length -= 1;
+        }
+
+
+        //
+        // Build the specified type or default to BigDecimal
+
+        BigDecimal value = new BigDecimal( text );
+        switch( type )
+        {
+            case 'f':
+                if( value.compareTo(MAX_FLOAT) <= 0 && value.compareTo(MIN_FLOAT) >= 0)
+                {
+                    return new Float( text );
+                }
+                throw new NumberFormatException( "out of range" );
+
+            case 'd':
+                if( value.compareTo(MAX_DOUBLE) <= 0 && value.compareTo(MIN_DOUBLE) >= 0)
+                {
+                    return new Double( text );
+                }
+                throw new NumberFormatException( "out of range" );
+
+            case 'g':
+            default:
+                return value;
+        }
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/ParserException.java b/groovy/src/main/org/codehaus/groovy/syntax/ParserException.java
new file mode 100644
index 0000000..4591272
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/ParserException.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.syntax;
+
+public class ParserException extends TokenException {
+    public ParserException(String message, Token token) {
+        super(message, token);
+    }
+
+    public ParserException(String message, Throwable cause, int lineNumber, int columnNumber) {
+        super(message, cause, lineNumber, columnNumber);
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/ReadException.java b/groovy/src/main/org/codehaus/groovy/syntax/ReadException.java
new file mode 100644
index 0000000..c02283d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/ReadException.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.syntax;
+
+import org.codehaus.groovy.GroovyException;
+
+import java.io.IOException;
+
+/**
+ * Encapsulates non-specific i/o exceptions.
+ */
+
+public class ReadException extends GroovyException {
+    private final IOException cause;
+
+    public ReadException(IOException cause) {
+        super();
+        this.cause = cause;
+    }
+
+    public ReadException(String message, IOException cause) {
+        super(message);
+        this.cause = cause;
+    }
+
+    public IOException getIOCause() {
+        return this.cause;
+    }
+
+    public String toString() {
+        String message = super.getMessage();
+        if (message == null || message.trim().equals("")) {
+            message = cause.getMessage();
+        }
+
+        return message;
+    }
+
+    public String getMessage() {
+        return toString();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/Reduction.java b/groovy/src/main/org/codehaus/groovy/syntax/Reduction.java
new file mode 100644
index 0000000..4d1aed0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/Reduction.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.syntax;
+
+import org.codehaus.groovy.GroovyBugError;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+
+
+/** 
+ *  A syntax reduction, produced by the <code>Parser</code>.
+ *
+ *  @see antlr.Parser
+ *  @see Token
+ *  @see CSTNode
+ *  @see Types
+ *
+ *  @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class Reduction extends CSTNode
+{
+    public static final Reduction EMPTY = new Reduction();
+
+
+  //---------------------------------------------------------------------------
+  // INITIALIZATION AND SUCH
+
+    private List    elements  = null;    // The set of child nodes   
+    private boolean marked    = false;   // Used for completion marking by some parts of the parser
+
+
+   /**
+    *  Initializes the <code>Reduction</code> with the specified root.
+    */
+
+    public Reduction( Token root ) 
+    {
+        elements = new ArrayList();
+        set( 0, root );
+    }
+
+
+   /**
+    *  Initializes the <code>Reduction</code> to empty.
+    */
+
+    private Reduction() 
+    {
+        elements = Collections.EMPTY_LIST;
+    }
+
+
+   /**
+    *  Creates a new <code>Reduction</code> with <code>Token.NULL</code>
+    *  as it's root.
+    */
+
+    public static Reduction newContainer() 
+    {
+        return new Reduction( Token.NULL );
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // MEMBER ACCESS
+
+
+   /**
+    *  Returns true if the node is completely empty (no root, even).
+    */
+
+    public boolean isEmpty() 
+    {
+        return size() == 0;
+    }
+
+
+
+   /**
+    *  Returns the number of elements in the node.
+    */
+
+    public int size() 
+    {
+        return elements.size();
+    }
+
+
+
+   /**
+    *  Returns the specified element, or null.
+    */
+
+    public CSTNode get( int index ) 
+    {
+        CSTNode element = null;
+
+        if( index < size() ) 
+        {
+            element = (CSTNode)elements.get( index );
+        }
+
+        return element;
+    }
+
+
+
+   /**
+    *  Returns the root of the node, the Token that indicates it's
+    *  type.  Returns null if there is no root (usually only if the
+    *  node is a placeholder of some kind -- see isEmpty()).
+    */
+
+    public Token getRoot() 
+    {
+        if( size() > 0 )
+        {
+            return (Token)elements.get(0);
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+
+
+   /**
+    *  Marks the node a complete expression.
+    */
+
+    public void markAsExpression() 
+    {
+        marked = true;
+    }
+
+
+
+   /**
+    *  Returns true if the node is a complete expression.
+    */
+
+    public boolean isAnExpression() 
+    {
+        if( isA(Types.COMPLEX_EXPRESSION) ) 
+        {
+            return true;
+        }
+
+        return marked;
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // OPERATIONS
+
+
+   /**
+    *  Adds an element to the node.
+    */
+
+    public CSTNode add( CSTNode element ) 
+    {
+        return set( size(), element );
+    }
+
+
+
+   /**
+    *  Sets an element in at the specified index.
+    */
+
+    public CSTNode set( int index, CSTNode element ) 
+    {
+        
+        if( elements == null ) 
+        {
+            throw new GroovyBugError( "attempt to set() on a EMPTY Reduction" );
+        }
+
+        if( index == 0 && !(element instanceof Token) ) 
+        {
+
+            //
+            // It's not the greatest of design that the interface allows this, but it
+            // is a tradeoff with convenience, and the convenience is more important.
+
+            throw new GroovyBugError( "attempt to set() a non-Token as root of a Reduction" );
+        }
+
+
+        //
+        // Fill slots with nulls, if necessary.
+
+        int count = elements.size();
+        if( index >= count ) 
+        {
+            for( int i = count; i <= index; i++ ) 
+            {
+                elements.add( null );
+            }
+        }
+
+        //
+        // Then set in the element.
+
+        elements.set( index, element );
+
+        return element;
+    }
+
+
+
+   /**
+    *  Removes a node from the <code>Reduction</code>.  You cannot remove 
+    *  the root node (index 0).
+    */
+
+    public CSTNode remove( int index )
+    {
+        if( index < 1 ) 
+        {
+            throw new GroovyBugError( "attempt to remove() root node of Reduction" );
+        }
+
+        return (CSTNode)elements.remove( index );
+    }
+
+
+
+   /**
+    *  Creates a <code>Reduction</code> from this node.  Returns self if the
+    *  node is already a <code>Reduction</code>.
+    */
+
+    public Reduction asReduction() 
+    {
+        return this;
+    }
+
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/RuntimeParserException.java b/groovy/src/main/org/codehaus/groovy/syntax/RuntimeParserException.java
new file mode 100644
index 0000000..d80d552
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/RuntimeParserException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.syntax;
+
+import groovy.lang.GroovyRuntimeException;
+
+import org.codehaus.groovy.ast.ASTNode;
+
+/**
+ * A helper class to allow parser exceptions to be thrown anywhere in the code.
+ * Should be replaced when no longer required.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class RuntimeParserException extends GroovyRuntimeException {
+
+    public RuntimeParserException(String message, ASTNode node) {
+        super(message + ".\nNode: " + node.getClass().getName(), node);
+    }
+
+    public void throwParserException() throws SyntaxException {
+        throw new SyntaxException(getMessage(), getNode().getLineNumber(), getNode().getColumnNumber());
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/SyntaxException.java b/groovy/src/main/org/codehaus/groovy/syntax/SyntaxException.java
new file mode 100644
index 0000000..5bd7598
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/SyntaxException.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.syntax;
+
+import org.codehaus.groovy.GroovyException;
+
+/** Base exception indicating a syntax error.
+ *
+ *  @author <a href="bob@werken.com">bob mcwhirter</a>
+ *
+ *  @version $Id$
+ */
+public class SyntaxException extends GroovyException {
+
+    /** Line upon which the error occurred. */
+    private final int line;
+
+    /** Column upon which the error occurred. */
+    private final int column;
+
+    private String sourceLocator;
+
+    public SyntaxException(String message, int line, int column) {
+        super(message, false);
+        this.line = line;
+        this.column = column;
+    }
+
+    public SyntaxException(String message, Throwable cause, int line, int column) {
+        super(message, cause);
+        this.line = line;
+        this.column = column;
+    }
+
+    // Properties
+    // ----------------------------------------------------------------------
+    public void setSourceLocator(String sourceLocator) {
+        this.sourceLocator = sourceLocator;
+    }
+
+    public String getSourceLocator() {
+        return this.sourceLocator;
+    }
+
+    /** Retrieve the line upon which the error occurred.
+     *
+     *  @return The line.
+     */
+    public int getLine() {
+        return line;
+    }
+
+    /** Retrieve the column upon which the error occurred.
+     *
+     *  @return The column.
+     */
+    public int getStartColumn() {
+        return column;
+    }
+    
+    /** 
+     * @return the end of the line on which the error occurs
+     */
+    public int getStartLine() {
+        return getLine();
+    }
+
+    /**
+     * @return the end column on which the error occurs
+     */
+    public int getEndColumn() {
+        return getStartColumn() + 1;
+    }
+
+    public String getMessage() {
+        return super.getMessage() + " @ line " + line + ", column " + column + ".";
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/Token.java b/groovy/src/main/org/codehaus/groovy/syntax/Token.java
new file mode 100644
index 0000000..8a0031f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/Token.java
@@ -0,0 +1,393 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.syntax;
+
+import org.codehaus.groovy.GroovyBugError;
+
+
+/**
+ *  A <code>CSTNode</code> produced by the <code>Lexer</code>.
+ *
+ *  @see antlr.Parser
+ *  @see antlr.Token
+ *  @see Reduction
+ *  @see Types
+ *
+ *  @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class Token extends CSTNode
+{
+    public static final Token NULL = new Token();
+    public static final Token EOF  = new Token( Types.EOF, "", -1, -1 );
+
+
+  //---------------------------------------------------------------------------
+  // TOKEN INITIALIZATION AND SUCH
+
+    private int type        = Types.UNKNOWN;  // the actual type identified by the lexer
+    private int meaning     = Types.UNKNOWN;  // an interpretation applied to the token after the fact
+
+    private String     text = "";             // the text of the token
+    private int   startLine = -1;             // the source line on which the token begins
+    private int startColumn = -1;             // the source column on which the token begins
+
+
+   /**
+    *  Initializes the Token with the specified information.
+    */
+
+    public Token( int type, String text, int startLine, int startColumn )
+    {
+        this.type        = type;
+        this.meaning     = type;
+        this.text        = text;
+        this.startLine   = startLine;
+        this.startColumn = startColumn;
+    }
+
+
+   /**
+    *  Initializes the NULL Token.
+    */
+
+    private Token() { }
+
+
+
+   /**
+    *  Returns a copy of this Token.
+    */
+
+    public Token dup()
+    {
+        Token token = new Token( this.type, this.text, this.startLine, this.startColumn );
+        token.setMeaning( this.meaning );
+
+        return token;
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // NODE IDENTIFICATION AND MEANING
+
+
+   /**
+    *  Returns the meaning of this node.  If the node isEmpty(), returns
+    *  the type of Token.NULL.
+    */
+
+    public int getMeaning()
+    {
+        return meaning;
+    }
+
+
+
+   /**
+    *  Sets the meaning for this node (and it's root Token).  Not
+    *  valid if the node isEmpty().  Returns this token, for
+    *  convenience.
+    */
+
+    public CSTNode setMeaning( int meaning )
+    {
+        this.meaning = meaning;
+        return this;
+    }
+
+
+
+   /**
+    *  Returns the actual type of the node.  If the node isEmpty(), returns
+    *  the type of Token.NULL.
+    */
+
+    public int getType()
+    {
+        return type;
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // MEMBER ACCESS
+
+
+   /**
+    *  Returns the number of elements in the node (including root).
+    */
+
+    public int size()
+    {
+        return 1;
+    }
+
+
+
+   /**
+    *  Returns the specified element, or null.
+    */
+
+    public CSTNode get( int index )
+    {
+        if( index > 0 )
+        {
+            throw new GroovyBugError( "attempt to access Token element other than root" );
+        }
+
+        return this;
+    }
+
+
+
+   /**
+    *  Returns the root of the node.  By convention, all nodes have
+    *  a Token as the first element (or root), which indicates the type
+    *  of the node.  May return null if the node <code>isEmpty()</code>.
+    */
+
+    public Token getRoot()
+    {
+        return this;
+    }
+
+
+
+   /**
+    *  Returns the text of the root node.  Uses <code>getRoot(true)</code>
+    *  to get the root, so you will only receive null in return if the
+    *  root token returns it.
+    */
+
+    public String getRootText()
+    {
+        return text;
+    }
+
+
+
+   /**
+    *  Returns the text of the token.  Equivalent to
+    *  <code>getRootText()</code> when called directly.
+    */
+
+    public String getText()
+    {
+        return text;
+    }
+
+
+
+   /**
+    *  Not advisable, but if you need to adjust the token's text, this
+    *  will do it.
+    */
+
+    public void setText( String text )
+    {
+        this.text = text;
+    }
+
+
+
+   /**
+    *  Returns the starting line of the node.  Returns -1
+    *  if not known.
+    */
+
+    public int getStartLine()
+    {
+        return startLine;
+    }
+
+
+
+   /**
+    *  Returns the starting column of the node.  Returns -1
+    *  if not known.
+    */
+
+    public int getStartColumn()
+    {
+        return startColumn;
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // OPERATIONS
+
+
+   /**
+    *  Creates a <code>Reduction</code> from this token.  Returns self if the
+    *  node is already a <code>Reduction</code>.
+    */
+
+    public Reduction asReduction()
+    {
+        return new Reduction( this );
+    }
+
+
+
+   /**
+    *  Creates a <code>Reduction</code> from this token, adding the supplied
+    *  node as the second element.
+    */
+
+    public Reduction asReduction( CSTNode second )
+    {
+        Reduction created = asReduction();
+        created.add( second );
+        return created;
+    }
+
+
+
+   /**
+    *  Creates a <code>Reduction</code> from this token, adding the supplied
+    *  nodes as the second and third element, respectively.
+    */
+
+    public Reduction asReduction( CSTNode second, CSTNode third )
+    {
+        Reduction created = asReduction( second );
+        created.add( third );
+        return created;
+    }
+
+
+
+   /**
+    *  Creates a <code>Reduction</code> from this token, adding the supplied
+    *  nodes as the second, third, and fourth element, respectively.
+    */
+
+    public Reduction asReduction( CSTNode second, CSTNode third, CSTNode fourth )
+    {
+        Reduction created = asReduction( second, third );
+        created.add( fourth );
+        return created;
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // TOKEN FACTORIES
+
+
+   /**
+    *  Creates a token that represents a keyword.  Returns null if the
+    *  specified text isn't a keyword.
+    */
+
+    public static Token newKeyword( String text, int startLine, int startColumn )
+    {
+
+        int type = Types.lookupKeyword( text );
+        if( type != Types.UNKNOWN )
+        {
+            return new Token( type, text, startLine, startColumn );
+        }
+
+        return null;
+
+    }
+
+
+   /**
+    *  Creates a token that represents a double-quoted string.
+    */
+
+    public static Token newString( String text, int startLine, int startColumn )
+    {
+        return new Token( Types.STRING, text, startLine, startColumn );
+    }
+
+
+   /**
+    *  Creates a token that represents an identifier.
+    */
+
+    public static Token newIdentifier( String text, int startLine, int startColumn )
+    {
+        return new Token( Types.IDENTIFIER, text, startLine, startColumn );
+    }
+
+
+   /**
+    *  Creates a token that represents an integer.
+    */
+
+    public static Token newInteger( String text, int startLine, int startColumn )
+    {
+        return new Token( Types.INTEGER_NUMBER, text, startLine, startColumn );
+    }
+
+
+   /**
+    *  Creates a token that represents a decimal number.
+    */
+
+    public static Token newDecimal( String text, int startLine, int startColumn )
+    {
+        return new Token( Types.DECIMAL_NUMBER, text, startLine, startColumn );
+    }
+
+
+   /**
+    *  Creates a token that represents a symbol, using a library for the text.
+    */
+
+    public static Token newSymbol( int type, int startLine, int startColumn )
+    {
+        return new Token( type, Types.getText(type), startLine, startColumn );
+    }
+
+
+   /**
+    *  Creates a token that represents a symbol, using a library for the type.
+    */
+
+    public static Token newSymbol( String type, int startLine, int startColumn )
+    {
+        return new Token( Types.lookupSymbol(type), type, startLine, startColumn );
+    }
+
+
+   /**
+    *  Creates a token with the specified meaning.
+    */
+
+    public static Token newPlaceholder( int type )
+    {
+        Token token = new Token( Types.UNKNOWN, "", -1, -1 );
+        token.setMeaning( type );
+
+        return token;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/TokenException.java b/groovy/src/main/org/codehaus/groovy/syntax/TokenException.java
new file mode 100644
index 0000000..50da0a9
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/TokenException.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.syntax;
+
+
+public class TokenException extends SyntaxException {
+    public TokenException(String message, Token token) {
+        super(
+            (token == null)
+                ? message + ". No token"
+                : message,
+            getLine(token),
+            getColumn(token));
+    }
+
+    public TokenException(String message, Throwable cause, int line, int column) {
+        super(message, cause, line, column);
+    }
+
+    public int getEndColumn() {
+        int length = 1;
+        return getStartColumn() + length;
+    }
+
+
+    // Implementation methods
+    // ----------------------------------------------------------------------
+    private static int getColumn(Token token) {
+        return (token != null) ? token.getStartColumn() : -1;
+    }
+
+    private static int getLine(Token token) {
+        return (token != null) ? token.getStartLine() : -1;
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/TokenMismatchException.java b/groovy/src/main/org/codehaus/groovy/syntax/TokenMismatchException.java
new file mode 100644
index 0000000..91e05a4
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/TokenMismatchException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.syntax;
+
+public class TokenMismatchException extends TokenException {
+    private final Token unexpectedToken;
+    private final int expectedType;
+
+    public TokenMismatchException(Token token, int expectedType) {
+        super("Expected token: " + expectedType + " but found: " + token, token);
+        this.unexpectedToken = token;
+        this.expectedType = expectedType;
+    }
+
+    public Token getUnexpectedToken() {
+        return this.unexpectedToken;
+    }
+
+    public int getExpectedType() {
+        return this.expectedType;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/Types.java b/groovy/src/main/org/codehaus/groovy/syntax/Types.java
new file mode 100644
index 0000000..1ad1913
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/Types.java
@@ -0,0 +1,1441 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.syntax;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.codehaus.groovy.GroovyBugError;
+
+
+/**
+ *  Typing information for the CST system.  The types here are those
+ *  used by CSTNode, Token, and Reduction.
+ *
+ *  @author <a href="mailto:bob@werken.com">bob mcwhirter</a>
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class Types
+{
+
+
+  //---------------------------------------------------------------------------
+  // TYPES: NOTE THAT ORDERING AND VALUES ARE IMPORTANT TO LOCAL ROUTINES!
+
+
+    //
+    // SPECIAL TOKENS
+
+    public static final int EOF                         = -1;    // end of file
+    public static final int UNKNOWN                     = 0;     // the unknown token
+
+
+    //
+    // RELEVANT WHITESPACE
+
+    public static final int NEWLINE                     = 5;     // \n
+
+
+    //
+    // OPERATORS AND OTHER MARKERS
+
+    public static final int LEFT_CURLY_BRACE            = 10;    // {
+    public static final int RIGHT_CURLY_BRACE           = 20;    // }
+    public static final int LEFT_SQUARE_BRACKET         = 30;    // [
+    public static final int RIGHT_SQUARE_BRACKET        = 40;    // ]
+    public static final int LEFT_PARENTHESIS            = 50;    // (
+    public static final int RIGHT_PARENTHESIS           = 60;    // )
+
+    public static final int DOT                         = 70;    // .
+    public static final int DOT_DOT                     = 75;    // ..
+    public static final int DOT_DOT_DOT                 = 77;    // ...
+
+    public static final int NAVIGATE                    = 80;    // ->
+
+    public static final int FIND_REGEX                  = 90;    // =~
+    public static final int MATCH_REGEX                 = 94;    // ==~
+    public static final int REGEX_PATTERN               = 97;    // ~
+
+    public static final int EQUAL                       = 100;   // =
+    public static final int EQUALS                      = EQUAL;
+    public static final int ASSIGN                      = EQUAL;
+
+    public static final int COMPARE_NOT_EQUAL           = 120;   // !=
+    public static final int COMPARE_IDENTICAL           = 121;   // ===
+    public static final int COMPARE_NOT_IDENTICAL       = 122;   // !==
+    public static final int COMPARE_EQUAL               = 123;   // ==
+    public static final int COMPARE_LESS_THAN           = 124;   // <
+    public static final int COMPARE_LESS_THAN_EQUAL     = 125;   // <=
+    public static final int COMPARE_GREATER_THAN        = 126;   // >
+    public static final int COMPARE_GREATER_THAN_EQUAL  = 127;   // >=
+    public static final int COMPARE_TO                  = 128;   // <=>
+
+    public static final int NOT                         = 160;   // !
+    public static final int LOGICAL_OR                  = 162;   // ||
+    public static final int LOGICAL_AND                 = 164;   // &&
+
+    public static final int LOGICAL_OR_EQUAL            = 166;   // ||=
+    public static final int LOGICAL_AND_EQUAL           = 168;   // &&=
+
+    public static final int PLUS                        = 200;   // +
+    public static final int MINUS                       = 201;   // -
+    public static final int MULTIPLY                    = 202;   // *
+    public static final int DIVIDE                      = 203;   // /
+    public static final int INTDIV                      = 204;   // \
+    public static final int MOD                         = 205;   // %
+    public static final int STAR_STAR                   = 206;   // **
+    public static final int POWER                       = STAR_STAR;   // **
+
+    public static final int PLUS_EQUAL                  = 210;   // +=
+    public static final int MINUS_EQUAL                 = 211;   // -=
+    public static final int MULTIPLY_EQUAL              = 212;   // *=
+    public static final int DIVIDE_EQUAL                = 213;   // /=
+    public static final int INTDIV_EQUAL                = 214;   // \=
+    public static final int MOD_EQUAL                   = 215;   // %=
+    public static final int POWER_EQUAL                 = 216;   // **=
+
+    public static final int PLUS_PLUS                   = 250;   // ++
+    public static final int PREFIX_PLUS_PLUS            = 251;   // ++
+    public static final int POSTFIX_PLUS_PLUS           = 252;   // ++
+    public static final int PREFIX_PLUS                 = 253;   // +
+
+    public static final int MINUS_MINUS                 = 260;   // --
+    public static final int PREFIX_MINUS_MINUS          = 261;   // --
+    public static final int POSTFIX_MINUS_MINUS         = 262;   // --
+    public static final int PREFIX_MINUS                = 263;   // - (negation)
+
+    public static final int LEFT_SHIFT                  = 280;   // <<
+    public static final int RIGHT_SHIFT                 = 281;   // >>
+    public static final int RIGHT_SHIFT_UNSIGNED        = 282;   // >>>
+
+    public static final int LEFT_SHIFT_EQUAL            = 285;   // <<=
+    public static final int RIGHT_SHIFT_EQUAL           = 286;   // >>=
+    public static final int RIGHT_SHIFT_UNSIGNED_EQUAL  = 287;   // >>>=
+
+    public static final int STAR                        = MULTIPLY;
+
+    public static final int COMMA                       = 300;   // -
+    public static final int COLON                       = 310;   // :
+    public static final int SEMICOLON                   = 320;   // ;
+    public static final int QUESTION                    = 330;   // ?
+
+    // TODO refactor PIPE to be BITWISE_OR
+    public static final int PIPE                        = 340;   // |
+    public static final int DOUBLE_PIPE                 = LOGICAL_OR;   // ||
+    public static final int BITWISE_OR                  = PIPE;  // |
+    public static final int BITWISE_AND                 = 341;   // &
+    public static final int BITWISE_XOR                 = 342;   // ^
+
+    public static final int BITWISE_OR_EQUAL            = 350;   // |=
+    public static final int BITWISE_AND_EQUAL           = 351;   // &=
+    public static final int BITWISE_XOR_EQUAL           = 352;   // ^=
+    public static final int BITWISE_NEGATION            = REGEX_PATTERN;    // ~
+
+
+    //
+    // LITERALS
+
+    public static final int STRING                      = 400;   // any bare string data
+
+    public static final int IDENTIFIER                  = 440;   // anything text and not a keyword
+
+    public static final int INTEGER_NUMBER              = 450;   // integer
+    public static final int DECIMAL_NUMBER              = 451;   // decimal
+
+
+    //
+    // KEYWORDS: (PRIMARILY) CLASS/METHOD DECLARATION MODIFIERS
+
+    public static final int KEYWORD_PRIVATE             = 500;   // declaration visibility
+    public static final int KEYWORD_PROTECTED           = 501;   // declaration visibility
+    public static final int KEYWORD_PUBLIC              = 502;   // declaration visibility
+
+    public static final int KEYWORD_ABSTRACT            = 510;   // method body missing
+    public static final int KEYWORD_FINAL               = 511;   // declaration cannot be overridden
+    public static final int KEYWORD_NATIVE              = 512;   // a native code entry point
+    public static final int KEYWORD_TRANSIENT           = 513;   // property should not be persisted
+    public static final int KEYWORD_VOLATILE            = 514;   // compiler should never cache property
+
+    public static final int KEYWORD_SYNCHRONIZED        = 520;   // modifier and block type
+    public static final int KEYWORD_STATIC              = 521;   // modifier and block type
+
+
+    //
+    // KEYWORDS: TYPE SYSTEM
+
+    public static final int KEYWORD_DEF                 = 530;   // identifies a function declaration
+    public static final int KEYWORD_DEFMACRO            = 539;   // XXX br identifies a macro declaration
+    public static final int KEYWORD_CLASS               = 531;   // identifies a class declaration
+    public static final int KEYWORD_INTERFACE           = 532;   // identifies an interface declaration
+    public static final int KEYWORD_MIXIN               = 533;   // identifies a mixin declaration
+
+    public static final int KEYWORD_IMPLEMENTS          = 540;   // specifies the interfaces implemented by a class
+    public static final int KEYWORD_EXTENDS             = 541;   // specifies the base class/interface for a new one
+    public static final int KEYWORD_THIS                = 542;   // method variable points to the current instance
+    public static final int KEYWORD_SUPER               = 543;   // method variable points to the base instance
+    public static final int KEYWORD_INSTANCEOF          = 544;   // type comparator
+    public static final int KEYWORD_PROPERTY            = 545;   // deprecated; identifies a property
+    public static final int KEYWORD_NEW                 = 546;   // used to create a new instance of a class
+
+    public static final int KEYWORD_PACKAGE             = 550;   // declares the package scope
+    public static final int KEYWORD_IMPORT              = 551;   // declares an external class
+    public static final int KEYWORD_AS                  = 552;   // used in import statements to create an alias
+
+
+    //
+    // KEYWORDS: CONTROL STRUCTURES
+
+    public static final int KEYWORD_RETURN              = 560;   // returns from a closure or method
+    public static final int KEYWORD_IF                  = 561;   // if
+    public static final int KEYWORD_ELSE                = 562;   // else
+    public static final int KEYWORD_DO                  = 570;   // do loop
+    public static final int KEYWORD_WHILE               = 571;   // while loop
+    public static final int KEYWORD_FOR                 = 572;   // for loop
+    public static final int KEYWORD_IN                  = 573;   // for (each) loop separator
+    public static final int KEYWORD_BREAK               = 574;   // exits a loop or block
+    public static final int KEYWORD_CONTINUE            = 575;   // restarts a loop on the next iteration
+    public static final int KEYWORD_SWITCH              = 576;   // switch block
+    public static final int KEYWORD_CASE                = 577;   // item in a switch block
+    public static final int KEYWORD_DEFAULT             = 578;   // catch-all item in a switch block
+
+    public static final int KEYWORD_TRY                 = 580;   // block to monitor for exceptions
+    public static final int KEYWORD_CATCH               = 581;   // catch block for a particular exception
+    public static final int KEYWORD_FINALLY             = 582;   // block to always execute on exit of the try
+    public static final int KEYWORD_THROW               = 583;   // statement to throw an exception
+    public static final int KEYWORD_THROWS              = 584;   // method modifier to declare thrown transactions
+    public static final int KEYWORD_ASSERT              = 585;   // alternate throw for code invariants
+
+
+    //
+    // KEYWORDS: PRIMITIVE TYPES
+
+    public static final int KEYWORD_VOID                = 600;   // void
+    public static final int KEYWORD_BOOLEAN             = 601;   // boolean
+    public static final int KEYWORD_BYTE                = 602;   // 1 byte integer
+    public static final int KEYWORD_SHORT               = 603;   // 2 byte integer
+    public static final int KEYWORD_INT                 = 604;   // 4 byte integer
+    public static final int KEYWORD_LONG                = 605;   // 8 byte integer
+    public static final int KEYWORD_FLOAT               = 606;   // 32 bit floating point number
+    public static final int KEYWORD_DOUBLE              = 607;   // 64 bit floating point number
+    public static final int KEYWORD_CHAR                = 608;   // unicode character code
+
+
+    //
+    // KEYWORDS: SPECIAL VALUES
+
+    public static final int KEYWORD_TRUE                = 610;   // boolean truth
+    public static final int KEYWORD_FALSE               = 611;   // boolean false
+    public static final int KEYWORD_NULL                = 612;   // missing instance
+
+
+    //
+    // KEYWORDS: RESERVED
+
+    public static final int KEYWORD_CONST               = 700;   // reserved in java and groovy
+    public static final int KEYWORD_GOTO                = 701;   // reserved in java and groovy
+
+
+    //
+    // SPECIAL (CALCULATED) MEANINGS
+
+    public static final int SYNTH_COMPILATION_UNIT      = 800;   // reserved: a synthetic root for a CST
+
+    public static final int SYNTH_CLASS                 = 801;   // applied to class names
+    public static final int SYNTH_INTERFACE             = 802;   // applied to interface names
+    public static final int SYNTH_MIXIN                 = 803;   // applied to mixin names
+    public static final int SYNTH_METHOD                = 804;   // applied to method names
+    public static final int SYNTH_PROPERTY              = 805;   // applied to property names
+    public static final int SYNTH_PARAMETER_DECLARATION = 806;   // applied to method/closure parameter names
+
+    public static final int SYNTH_LIST                  = 810;   // applied to "[" that marks a list
+    public static final int SYNTH_MAP                   = 811;   // applied to "[" that marks a map
+    public static final int SYNTH_GSTRING               = 812;   // a complete GString
+
+    public static final int SYNTH_METHOD_CALL           = 814;   // applied to the optional "(" that marks a call to a method
+    public static final int SYNTH_CAST                  = 815;   // applied to "(" that marks a type cast
+    public static final int SYNTH_BLOCK                 = 816;   // applied to "{" that marks a block
+    public static final int SYNTH_CLOSURE               = 817;   // applied to "{" that marks a closure
+    public static final int SYNTH_LABEL                 = 818;   // applied to a statement label
+    public static final int SYNTH_TERNARY               = 819;   // applied to "?" that marks a ternary expression
+    public static final int SYNTH_TUPLE                 = 820;   // applied to "{" that marks an array initializer
+
+    public static final int SYNTH_VARIABLE_DECLARATION  = 830;   // applied to an identifier that specifies
+                                                                 // the type of a variable declaration
+
+    //
+    // GSTRING TOKENS
+
+    public static final int GSTRING_START               = 901;   // any marker tha begins a GString
+    public static final int GSTRING_END                 = 902;   // any matching marker that ends a GString
+    public static final int GSTRING_EXPRESSION_START    = 903;   // the ${ marker that starts a GString expression
+    public static final int GSTRING_EXPRESSION_END      = 904;   // the } marker that ends a GString expresssion
+
+
+    //
+    // TYPE CLASSES
+
+    public static final int ANY                         = 1000;  // anything
+    public static final int NOT_EOF                     = 1001;  // anything but EOF
+    public static final int GENERAL_END_OF_STATEMENT    = 1002;  // ";", "\n", EOF
+    public static final int ANY_END_OF_STATEMENT        = 1003;  // ";", "\n", EOF, "}"
+
+    public static final int ASSIGNMENT_OPERATOR         = 1100;  // =, +=, etc.
+    public static final int COMPARISON_OPERATOR         = 1101;  // ==, ===, >, <, etc.
+    public static final int MATH_OPERATOR               = 1102;  // +, -, / *, %, plus the LOGICAL_OPERATORS
+    public static final int LOGICAL_OPERATOR            = 1103;  // ||, &&, !
+    public static final int RANGE_OPERATOR              = 1104;  // .., ...
+    public static final int REGEX_COMPARISON_OPERATOR   = 1105;  // =~, etc.
+    public static final int DEREFERENCE_OPERATOR        = 1106;  // ., ->
+    public static final int BITWISE_OPERATOR            = 1107;  // |, &, <<, >>, >>>, ^, ~
+
+    public static final int PREFIX_OPERATOR             = 1200;  // ++, !, etc.
+    public static final int POSTFIX_OPERATOR            = 1210;  // ++, etc.
+    public static final int INFIX_OPERATOR              = 1220;  // +, -, =, etc.
+    public static final int PREFIX_OR_INFIX_OPERATOR    = 1230;  // +, -
+    public static final int PURE_PREFIX_OPERATOR        = 1235;  // prefix +, prefix -
+
+    public static final int KEYWORD                     = 1300;  // any keyword
+    public static final int SYMBOL                      = 1301;  // any symbol
+    public static final int LITERAL                     = 1310;  // strings, numbers, identifiers
+    public static final int NUMBER                      = 1320;  // integers and decimals
+    public static final int SIGN                        = 1325;  // "+", "-"
+    public static final int NAMED_VALUE                 = 1330;  // true, false, null
+    public static final int TRUTH_VALUE                 = 1331;  // true, false
+    public static final int PRIMITIVE_TYPE              = 1340;  // void, byte, short, int, etc.
+    public static final int CREATABLE_PRIMITIVE_TYPE    = 1341;  // any PRIMITIVE_TYPE except void
+    public static final int LOOP                        = 1350;  // do, while, etc.
+    public static final int RESERVED_KEYWORD            = 1360;  // const, goto, etc.
+    public static final int KEYWORD_IDENTIFIER          = 1361;  // keywords that can appear as identifiers
+    public static final int SYNTHETIC                   = 1370;  // any of the SYNTH types
+
+    public static final int TYPE_DECLARATION            = 1400;  // class, interface, mixin
+    public static final int DECLARATION_MODIFIER        = 1410;  // public, private, abstract, etc.
+
+    public static final int TYPE_NAME                   = 1420;  // identifiers, primitive types
+    public static final int CREATABLE_TYPE_NAME         = 1430;  // identifiers, primitive types except void
+
+    public static final int MATCHED_CONTAINER           = 1500;  // (, ), [, ], {, }
+    public static final int LEFT_OF_MATCHED_CONTAINER   = 1501;  // (, [, {
+    public static final int RIGHT_OF_MATCHED_CONTAINER  = 1502;  // ), ], }
+
+    public static final int EXPRESSION                  = 1900;  // all of the below 1900 series
+
+    public static final int OPERATOR_EXPRESSION         = 1901;  // "."-"<<"
+    public static final int SYNTH_EXPRESSION            = 1902;  // cast, ternary, and closure expression
+    public static final int KEYWORD_EXPRESSION          = 1903;  // new, this, super, instanceof, true, false, null
+    public static final int LITERAL_EXPRESSION          = 1904;  // LITERAL
+    public static final int ARRAY_EXPRESSION            = 1905;  // "["
+
+    public static final int SIMPLE_EXPRESSION           = 1910;  // LITERAL, this, true, false, null
+    public static final int COMPLEX_EXPRESSION          = 1911;  // SIMPLE_EXPRESSION, and various molecules
+
+
+
+    //
+    // TYPE GROUPS (OPERATIONS SUPPORT)
+
+    public static final int PARAMETER_TERMINATORS       = 2000;  // ")", ","
+    public static final int ARRAY_ITEM_TERMINATORS      = 2001;  // "]", ","
+    public static final int TYPE_LIST_TERMINATORS       = 2002;  // "implements", "throws", "{", ","
+    public static final int OPTIONAL_DATATYPE_FOLLOWERS = 2003;  // identifier, "[", "."
+
+    public static final int SWITCH_BLOCK_TERMINATORS    = 2004;  // "case", "default", "}"
+    public static final int SWITCH_ENTRIES              = 2005;  // "case", "default"
+
+    public static final int METHOD_CALL_STARTERS        = 2006;  // LITERAL, "(", "{"
+    public static final int UNSAFE_OVER_NEWLINES        = 2007;  // things the expression parser should cross lines for in it doesn't have to
+
+    public static final int PRECLUDES_CAST_OPERATOR     = 2008;  // anything that prevents (X) from being a cast
+
+
+
+
+
+  //---------------------------------------------------------------------------
+  // TYPE HIERARCHIES
+
+
+   /**
+    *  Given two types, returns true if the second describes the first.
+    */
+
+    public static boolean ofType( int specific, int general )
+    {
+
+        if( general == specific )
+        {
+            return true;
+        }
+
+        switch( general )
+        {
+            case ANY:
+                return true;
+
+            case NOT_EOF:
+                return specific >= UNKNOWN && specific <= SYNTH_VARIABLE_DECLARATION;
+
+            case GENERAL_END_OF_STATEMENT:
+                switch( specific )
+                {
+                    case EOF:
+                    case NEWLINE:
+                    case SEMICOLON:
+                        return true;
+                }
+                break;
+
+            case ANY_END_OF_STATEMENT:
+                switch( specific )
+                {
+                    case EOF:
+                    case NEWLINE:
+                    case SEMICOLON:
+                    case RIGHT_CURLY_BRACE:
+                        return true;
+                }
+                break;
+
+            case ASSIGNMENT_OPERATOR:
+                return specific == EQUAL || (specific >= PLUS_EQUAL && specific <= POWER_EQUAL) || (specific >= LOGICAL_OR_EQUAL && specific <= LOGICAL_AND_EQUAL)
+                                         || (specific >= LEFT_SHIFT_EQUAL && specific <= RIGHT_SHIFT_UNSIGNED_EQUAL)
+                                         || (specific >= BITWISE_OR_EQUAL && specific <= BITWISE_XOR_EQUAL);
+
+            case COMPARISON_OPERATOR:
+                return specific >= COMPARE_NOT_EQUAL && specific <= COMPARE_TO;
+
+            case MATH_OPERATOR:
+                return (specific >= PLUS && specific <= RIGHT_SHIFT_UNSIGNED) || (specific >= NOT && specific <= LOGICAL_AND)
+                                 || (specific >= BITWISE_OR && specific <= BITWISE_XOR);
+
+            case LOGICAL_OPERATOR:
+                return specific >= NOT && specific <= LOGICAL_AND;
+
+            case BITWISE_OPERATOR:
+                return (specific >= BITWISE_OR && specific <= BITWISE_XOR) || specific == BITWISE_NEGATION;
+
+            case RANGE_OPERATOR:
+                return specific == DOT_DOT || specific == DOT_DOT_DOT;
+
+            case REGEX_COMPARISON_OPERATOR:
+                return specific == FIND_REGEX || specific == MATCH_REGEX;
+
+            case DEREFERENCE_OPERATOR:
+                return specific == DOT || specific == NAVIGATE;
+
+            case PREFIX_OPERATOR:
+                switch( specific )
+                {
+                    case MINUS:
+                    case PLUS_PLUS:
+                    case MINUS_MINUS:
+                        return true;
+                }
+
+                /* FALL THROUGH */
+
+            case PURE_PREFIX_OPERATOR:
+                switch( specific )
+                {
+                    case REGEX_PATTERN:
+                    case NOT:
+                    case PREFIX_PLUS:
+                    case PREFIX_PLUS_PLUS:
+                    case PREFIX_MINUS:
+                    case PREFIX_MINUS_MINUS:
+                    case SYNTH_CAST:
+                        return true;
+                }
+                break;
+
+            case POSTFIX_OPERATOR:
+                switch( specific )
+                {
+                    case PLUS_PLUS:
+                    case POSTFIX_PLUS_PLUS:
+                    case MINUS_MINUS:
+                    case POSTFIX_MINUS_MINUS:
+                        return true;
+                }
+                break;
+
+            case INFIX_OPERATOR:
+                switch( specific )
+                {
+                    case DOT:
+                    case NAVIGATE:
+                    case LOGICAL_OR:
+                    case LOGICAL_AND:
+                    case BITWISE_OR:
+                    case BITWISE_AND:
+                    case BITWISE_XOR:
+                    case LEFT_SHIFT:
+                    case RIGHT_SHIFT:
+                    case RIGHT_SHIFT_UNSIGNED:
+                    case FIND_REGEX:
+                    case MATCH_REGEX:
+                    case DOT_DOT:
+                    case DOT_DOT_DOT:
+                    case KEYWORD_INSTANCEOF:
+                        return true;
+                }
+
+                return (specific >= COMPARE_NOT_EQUAL && specific <= COMPARE_TO) || (specific >= PLUS && specific <= MOD_EQUAL) || specific == EQUAL || (specific >= PLUS_EQUAL && specific <= POWER_EQUAL) || (specific >= LOGICAL_OR_EQUAL && specific <= LOGICAL_AND_EQUAL)
+                                 || (specific >= LEFT_SHIFT_EQUAL && specific <= RIGHT_SHIFT_UNSIGNED_EQUAL) || (specific >= BITWISE_OR_EQUAL && specific <= BITWISE_XOR_EQUAL);
+
+            case PREFIX_OR_INFIX_OPERATOR:
+                switch( specific )
+                {
+                    case POWER:
+                    case PLUS:
+                    case MINUS:
+                    case PREFIX_PLUS:
+                    case PREFIX_MINUS:
+                        return true;
+                }
+                break;
+
+
+            case KEYWORD:
+                return specific >= KEYWORD_PRIVATE && specific <= KEYWORD_GOTO;
+
+            case SYMBOL:
+                return specific >= NEWLINE && specific <= PIPE;
+
+            case LITERAL:
+                return specific >= STRING && specific <= DECIMAL_NUMBER;
+
+            case NUMBER:
+                return specific == INTEGER_NUMBER || specific == DECIMAL_NUMBER;
+
+            case SIGN:
+                switch( specific )
+                {
+                    case PLUS:
+                    case MINUS:
+                        return true;
+                }
+                break;
+
+            case NAMED_VALUE:
+                return specific >= KEYWORD_TRUE && specific <= KEYWORD_NULL;
+
+            case TRUTH_VALUE:
+                return specific == KEYWORD_TRUE || specific == KEYWORD_FALSE;
+
+            case TYPE_NAME:
+                if( specific == IDENTIFIER )
+                {
+                    return true;
+                }
+
+                /* FALL THROUGH */
+
+            case PRIMITIVE_TYPE:
+                return specific >= KEYWORD_VOID && specific <= KEYWORD_CHAR;
+
+            case CREATABLE_TYPE_NAME:
+                if( specific == IDENTIFIER )
+                {
+                    return true;
+                }
+
+                /* FALL THROUGH */
+
+            case CREATABLE_PRIMITIVE_TYPE:
+                return specific >= KEYWORD_BOOLEAN && specific <= KEYWORD_CHAR;
+
+            case LOOP:
+                switch( specific )
+                {
+                    case KEYWORD_DO:
+                    case KEYWORD_WHILE:
+                    case KEYWORD_FOR:
+                        return true;
+                }
+                break;
+
+            case RESERVED_KEYWORD:
+                return specific >= KEYWORD_CONST && specific <= KEYWORD_GOTO;
+
+            case KEYWORD_IDENTIFIER:
+                switch( specific )
+                {
+                    case KEYWORD_CLASS:
+                    case KEYWORD_INTERFACE:
+                    case KEYWORD_MIXIN:
+                    case KEYWORD_DEF:
+                    case KEYWORD_DEFMACRO:
+                    case KEYWORD_IN:
+                    case KEYWORD_PROPERTY:
+                        return true;
+                }
+                break;
+
+            case SYNTHETIC:
+                return specific >= SYNTH_COMPILATION_UNIT && specific <= SYNTH_VARIABLE_DECLARATION;
+
+            case TYPE_DECLARATION:
+                return specific >= KEYWORD_CLASS && specific <= KEYWORD_MIXIN;
+
+            case DECLARATION_MODIFIER:
+                return specific >= KEYWORD_PRIVATE && specific <= KEYWORD_STATIC;
+
+            case MATCHED_CONTAINER:
+                switch( specific )
+                {
+                    case LEFT_CURLY_BRACE:
+                    case RIGHT_CURLY_BRACE:
+                    case LEFT_SQUARE_BRACKET:
+                    case RIGHT_SQUARE_BRACKET:
+                    case LEFT_PARENTHESIS:
+                    case RIGHT_PARENTHESIS:
+                        return true;
+                }
+                break;
+
+            case LEFT_OF_MATCHED_CONTAINER:
+                switch( specific )
+                {
+                    case LEFT_CURLY_BRACE:
+                    case LEFT_SQUARE_BRACKET:
+                    case LEFT_PARENTHESIS:
+                        return true;
+                }
+                break;
+
+            case RIGHT_OF_MATCHED_CONTAINER:
+                switch( specific )
+                {
+                    case RIGHT_CURLY_BRACE:
+                    case RIGHT_SQUARE_BRACKET:
+                    case RIGHT_PARENTHESIS:
+                        return true;
+                }
+                break;
+
+
+            case PARAMETER_TERMINATORS:
+                return specific == RIGHT_PARENTHESIS || specific == COMMA;
+
+            case ARRAY_ITEM_TERMINATORS:
+                return specific == RIGHT_SQUARE_BRACKET || specific == COMMA;
+
+            case TYPE_LIST_TERMINATORS:
+                switch( specific )
+                {
+                    case KEYWORD_IMPLEMENTS:
+                    case KEYWORD_THROWS:
+                    case LEFT_CURLY_BRACE:
+                    case COMMA:
+                        return true;
+                }
+                break;
+
+            case OPTIONAL_DATATYPE_FOLLOWERS:
+                switch( specific )
+                {
+                    case IDENTIFIER:
+                    case LEFT_SQUARE_BRACKET:
+                    case DOT:
+                        return true;
+                }
+                break;
+
+            case SWITCH_BLOCK_TERMINATORS:
+                if( specific == RIGHT_CURLY_BRACE )
+                {
+                    return true;
+                }
+
+                /* FALL THROUGH */
+
+            case SWITCH_ENTRIES:
+                return specific == KEYWORD_CASE || specific == KEYWORD_DEFAULT;
+
+            case METHOD_CALL_STARTERS:
+                if( specific >= STRING && specific <= DECIMAL_NUMBER )
+                {
+                    return true;
+                }
+                switch( specific )
+				{
+                	case LEFT_PARENTHESIS:
+                    case GSTRING_START:
+                    case SYNTH_GSTRING:
+                    case KEYWORD_NEW:
+                    	return true;
+                }
+                break;
+
+            case UNSAFE_OVER_NEWLINES:
+                if( ofType(specific, SYMBOL) )
+                {
+                    switch( specific )
+                    {
+                        case LEFT_CURLY_BRACE:
+                        case LEFT_PARENTHESIS:
+                        case LEFT_SQUARE_BRACKET:
+                        case PLUS:
+                        case PLUS_PLUS:
+                        case MINUS:
+                        case MINUS_MINUS:
+                        case REGEX_PATTERN:
+                        case NOT:
+                            return true;
+                    }
+
+                    return false;
+                }
+
+                switch( specific )
+                {
+                    case KEYWORD_INSTANCEOF:
+                    case GSTRING_EXPRESSION_START:
+                    case GSTRING_EXPRESSION_END:
+                    case GSTRING_END:
+                        return false;
+                }
+
+                return true;
+
+            case PRECLUDES_CAST_OPERATOR:
+                switch( specific )
+                {
+                    case PLUS:
+                    case MINUS:
+                    case PREFIX_MINUS:
+                    case PREFIX_MINUS_MINUS:
+                    case PREFIX_PLUS:
+                    case PREFIX_PLUS_PLUS:
+                    case LEFT_PARENTHESIS:
+                        return false;
+                }
+
+                return !ofType( specific, COMPLEX_EXPRESSION );
+
+
+
+
+            case OPERATOR_EXPRESSION:
+                return specific >= DOT && specific <= RIGHT_SHIFT_UNSIGNED;
+
+            case SYNTH_EXPRESSION:
+                switch( specific )
+                {
+                    case SYNTH_CAST:
+                    case SYNTH_CLOSURE:
+                    case SYNTH_TERNARY:
+                        return true;
+                }
+                break;
+
+            case KEYWORD_EXPRESSION:
+                switch( specific )
+                {
+                    case KEYWORD_NEW:
+                    case KEYWORD_THIS:
+                    case KEYWORD_SUPER:
+                    case KEYWORD_INSTANCEOF:
+                    case KEYWORD_TRUE:
+                    case KEYWORD_FALSE:
+                    case KEYWORD_NULL:
+                        return true;
+                }
+                break;
+
+            case LITERAL_EXPRESSION:
+                return specific >= STRING && specific <= DECIMAL_NUMBER;
+
+            case ARRAY_EXPRESSION:
+                return specific == LEFT_SQUARE_BRACKET;
+
+            case EXPRESSION:
+                if( specific >= DOT && specific <= RIGHT_SHIFT_UNSIGNED )
+                {
+                    return true;
+                }
+
+                if( specific >= STRING && specific <= DECIMAL_NUMBER )
+                {
+                    return true;
+                }
+
+                switch( specific )
+                {
+                    case SYNTH_CAST:
+                    case SYNTH_CLOSURE:
+                    case SYNTH_TERNARY:
+                    case SYNTH_GSTRING:
+                    case KEYWORD_NEW:
+                    case KEYWORD_THIS:
+                    case KEYWORD_SUPER:
+                    case KEYWORD_INSTANCEOF:
+                    case KEYWORD_TRUE:
+                    case KEYWORD_FALSE:
+                    case KEYWORD_NULL:
+                    case LEFT_SQUARE_BRACKET:
+                        return true;
+                }
+                break;
+
+            case COMPLEX_EXPRESSION:
+                switch( specific )
+                {
+                    case KEYWORD_NEW:
+                    case SYNTH_METHOD_CALL:
+                    case SYNTH_GSTRING:
+                    case SYNTH_LIST:
+                    case SYNTH_MAP:
+                    case SYNTH_CLOSURE:
+                    case SYNTH_TERNARY:
+                    case SYNTH_VARIABLE_DECLARATION:
+                        return true;
+                }
+
+                /* FALL THROUGH */
+
+            case SIMPLE_EXPRESSION:
+                if( specific >= STRING && specific <= DECIMAL_NUMBER ) {
+                    return true;
+                }
+
+                switch( specific ) {
+                    case KEYWORD_SUPER:
+                    case KEYWORD_THIS:
+                    case KEYWORD_TRUE:
+                    case KEYWORD_FALSE:
+                    case KEYWORD_NULL:
+                        return true;
+                }
+
+                break;
+        }
+
+        return false;
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // TYPE COERSIONS
+
+
+   /**
+    *  Given two types, returns true if the first can be viewed as the second.
+    *  NOTE that <code>canMean()</code> is orthogonal to <code>ofType()</code>.
+    */
+
+    public static boolean canMean( int actual, int preferred ) {
+
+        if( actual == preferred ) {
+            return true;
+        }
+
+        switch( preferred ) {
+
+            case SYNTH_PARAMETER_DECLARATION:
+            case IDENTIFIER:
+                switch( actual ) {
+                    case IDENTIFIER:
+                    case KEYWORD_DEF:
+                    case KEYWORD_DEFMACRO:
+                    case KEYWORD_CLASS:
+                    case KEYWORD_INTERFACE:
+                    case KEYWORD_MIXIN:
+                        return true;
+                }
+                break;
+
+            case SYNTH_CLASS:
+            case SYNTH_INTERFACE:
+            case SYNTH_MIXIN:
+            case SYNTH_METHOD:
+            case SYNTH_PROPERTY:
+                return actual == IDENTIFIER;
+
+            case SYNTH_LIST:
+            case SYNTH_MAP:
+                return actual == LEFT_SQUARE_BRACKET;
+
+            case SYNTH_CAST:
+                return actual == LEFT_PARENTHESIS;
+
+            case SYNTH_BLOCK:
+            case SYNTH_CLOSURE:
+                return actual == LEFT_CURLY_BRACE;
+
+            case SYNTH_LABEL:
+                return actual == COLON;
+
+            case SYNTH_VARIABLE_DECLARATION:
+                return actual == IDENTIFIER;
+        }
+
+        return false;
+    }
+
+
+
+   /**
+    *  Converts a node from a generic type to a specific prefix type.
+    *  Throws a <code>GroovyBugError</code> if the type can't be converted
+    *  and requested.
+    */
+
+    public static void makePrefix( CSTNode node, boolean throwIfInvalid ) {
+
+        switch( node.getMeaning() ) {
+            case PLUS:
+                node.setMeaning( PREFIX_PLUS );
+                break;
+
+            case MINUS:
+                node.setMeaning( PREFIX_MINUS );
+                break;
+
+            case PLUS_PLUS:
+                node.setMeaning( PREFIX_PLUS_PLUS );
+                break;
+
+            case MINUS_MINUS:
+                node.setMeaning( PREFIX_MINUS_MINUS );
+                break;
+
+            default:
+                if( throwIfInvalid ) {
+                    throw new GroovyBugError( "cannot convert to prefix for type [" + node.getMeaning() + "]" );
+                }
+        }
+
+    }
+
+
+
+   /**
+    *  Converts a node from a generic type to a specific postfix type.
+    *  Throws a <code>GroovyBugError</code> if the type can't be converted.
+    */
+
+    public static void makePostfix( CSTNode node, boolean throwIfInvalid ) {
+
+        switch( node.getMeaning() ) {
+            case PLUS_PLUS:
+                node.setMeaning( POSTFIX_PLUS_PLUS );
+                break;
+
+            case MINUS_MINUS:
+                node.setMeaning( POSTFIX_MINUS_MINUS );
+                break;
+
+            default:
+                if( throwIfInvalid ) {
+                    throw new GroovyBugError( "cannot convert to postfix for type [" + node.getMeaning() + "]" );
+                }
+        }
+
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // OPERATOR PRECEDENCE
+
+
+   /**
+    *  Returns the precendence of the specified operator.  Non-operator's will
+    *  receive -1 or a GroovyBugError, depending on your preference.
+    */
+
+    public static int getPrecedence( int type, boolean throwIfInvalid ) {
+
+        switch( type ) {
+
+            case LEFT_PARENTHESIS:
+                return 0;
+
+            case EQUAL:
+            case PLUS_EQUAL:
+            case MINUS_EQUAL:
+            case MULTIPLY_EQUAL:
+            case DIVIDE_EQUAL:
+            case INTDIV_EQUAL:
+            case MOD_EQUAL:
+            case POWER_EQUAL:
+            case LOGICAL_OR_EQUAL:
+            case LOGICAL_AND_EQUAL:
+            case LEFT_SHIFT_EQUAL:
+            case RIGHT_SHIFT_EQUAL:
+            case RIGHT_SHIFT_UNSIGNED_EQUAL:
+            case BITWISE_OR_EQUAL:
+            case BITWISE_AND_EQUAL:
+            case BITWISE_XOR_EQUAL:
+                return 5;
+
+            case QUESTION:
+                return 10;
+
+            case LOGICAL_OR:
+                return 15;
+
+            case LOGICAL_AND:
+                return 20;
+
+            case BITWISE_OR:
+	    case BITWISE_AND:
+            case BITWISE_XOR:
+                return 22;
+
+            case COMPARE_IDENTICAL:
+            case COMPARE_NOT_IDENTICAL:
+                return 24;
+
+            case COMPARE_NOT_EQUAL:
+            case COMPARE_EQUAL:
+            case COMPARE_LESS_THAN:
+            case COMPARE_LESS_THAN_EQUAL:
+            case COMPARE_GREATER_THAN:
+            case COMPARE_GREATER_THAN_EQUAL:
+            case COMPARE_TO:
+            case FIND_REGEX:
+            case MATCH_REGEX:
+            case KEYWORD_INSTANCEOF:
+                return 25;
+
+            case DOT_DOT:
+            case DOT_DOT_DOT:
+                return 30;
+
+            case LEFT_SHIFT:
+            case RIGHT_SHIFT:
+            case RIGHT_SHIFT_UNSIGNED:
+                return 35;
+
+            case PLUS:
+            case MINUS:
+                return 40;
+
+            case MULTIPLY:
+            case DIVIDE:
+            case INTDIV:
+            case MOD:
+                return 45;
+
+            case NOT:
+            case REGEX_PATTERN:
+                return 50;
+
+            case SYNTH_CAST:
+                return 55;
+
+            case PLUS_PLUS:
+            case MINUS_MINUS:
+            case PREFIX_PLUS_PLUS:
+            case PREFIX_MINUS_MINUS:
+            case POSTFIX_PLUS_PLUS:
+            case POSTFIX_MINUS_MINUS:
+                return 65;
+
+            case PREFIX_PLUS:
+            case PREFIX_MINUS:
+                return 70;
+
+            case POWER:
+                return 72;
+
+            case SYNTH_METHOD:
+            case LEFT_SQUARE_BRACKET:
+                return 75;
+
+            case DOT:
+            case NAVIGATE:
+                return 80;
+
+            case KEYWORD_NEW:
+                return 85;
+        }
+
+        if( throwIfInvalid ) {
+            throw new GroovyBugError( "precedence requested for non-operator" );
+        }
+
+        return -1;
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // TEXTS
+
+    private static final Map TEXTS  = new HashMap();  // symbol/keyword type -> text
+    private static final Map LOOKUP = new HashMap();  // text -> symbol/keyword type
+
+
+   /**
+    *  Returns the type for the specified symbol/keyword text.  Returns UNKNOWN
+    *  if the text isn't found.  You can filter finds on a type.
+    */
+
+    public static int lookup( String text, int filter ) {
+        int type = UNKNOWN;
+
+        if( LOOKUP.containsKey(text) ) {
+            type = ((Integer)LOOKUP.get(text)).intValue();
+            if( filter != UNKNOWN && !ofType(type, filter) ) {
+                type = UNKNOWN;
+            }
+        }
+
+        return type;
+    }
+
+
+   /**
+    *  Returns the type for the specified keyword text.  Returns UNKNOWN
+    *  if the text isn't found.
+    */
+
+    public static int lookupKeyword( String text ) {
+        return lookup( text, KEYWORD );
+    }
+
+
+   /**
+    *  Returns the type for the specified symbol text.  Returns UNKNOWN
+    *  if the text isn't found.
+    */
+
+    public static int lookupSymbol( String text ) {
+        return lookup( text, SYMBOL );
+    }
+
+
+   /**
+    *  Returns the text for the specified type.  Returns "" if the
+    *  text isn't found.
+    */
+
+    public static String getText( int type ) {
+        Integer key = new Integer( type );
+        String text = "";
+
+        if( TEXTS.containsKey(key) ) {
+            text = (String)TEXTS.get( key );
+        }
+
+        return text;
+    }
+
+
+   /**
+    *  Adds a element to the TEXTS and LOOKUP.
+    */
+
+    private static void addTranslation( String text, int type ) {
+        Integer key = new Integer( type );
+
+        TEXTS.put( key, text );
+        LOOKUP.put( text, key );
+    }
+
+
+    static {
+
+        //
+        // SYMBOLS
+
+        addTranslation( "\n"          , NEWLINE                     );
+
+        addTranslation( "{"           , LEFT_CURLY_BRACE            );
+        addTranslation( "}"           , RIGHT_CURLY_BRACE           );
+        addTranslation( "["           , LEFT_SQUARE_BRACKET         );
+        addTranslation( "]"           , RIGHT_SQUARE_BRACKET        );
+        addTranslation( "("           , LEFT_PARENTHESIS            );
+        addTranslation( ")"           , RIGHT_PARENTHESIS           );
+
+        addTranslation( "."           , DOT                         );
+        addTranslation( ".."          , DOT_DOT                     );
+        addTranslation( "..."         , DOT_DOT_DOT                 );
+
+        addTranslation( "->"          , NAVIGATE                    );
+
+        addTranslation( "=~"          , FIND_REGEX                  );
+        addTranslation( "==~"         , MATCH_REGEX                 );
+        addTranslation( "~"           , REGEX_PATTERN               );
+
+        addTranslation( "="           , EQUAL                       );
+
+        addTranslation( "!="          , COMPARE_NOT_EQUAL           );
+        addTranslation( "==="         , COMPARE_IDENTICAL           );
+        addTranslation( "!=="         , COMPARE_NOT_IDENTICAL       );
+        addTranslation( "=="          , COMPARE_EQUAL               );
+        addTranslation( "<"           , COMPARE_LESS_THAN           );
+        addTranslation( "<="          , COMPARE_LESS_THAN_EQUAL     );
+        addTranslation( ">"           , COMPARE_GREATER_THAN        );
+        addTranslation( ">="          , COMPARE_GREATER_THAN_EQUAL  );
+        addTranslation( "<=>"         , COMPARE_TO                  );
+
+        addTranslation( "!"           , NOT                         );
+        addTranslation( "||"          , LOGICAL_OR                  );
+        addTranslation( "&&"          , LOGICAL_AND                 );
+
+        addTranslation( "||="         , LOGICAL_OR_EQUAL            );
+        addTranslation( "&&="         , LOGICAL_AND_EQUAL           );
+
+        addTranslation( "+"           , PLUS                        );
+        addTranslation( "-"           , MINUS                       );
+        addTranslation( "*"           , MULTIPLY                    );
+        addTranslation( "/"           , DIVIDE                      );
+        addTranslation( "\\"          , INTDIV                      );
+        addTranslation( "%"           , MOD                         );
+
+	addTranslation( "**"          , POWER                       );
+
+        addTranslation( "+="          , PLUS_EQUAL                  );
+        addTranslation( "-="          , MINUS_EQUAL                 );
+        addTranslation( "*="          , MULTIPLY_EQUAL              );
+        addTranslation( "/="          , DIVIDE_EQUAL                );
+        addTranslation( "\\="         , INTDIV_EQUAL                );
+        addTranslation( "%="          , MOD_EQUAL                   );
+        addTranslation( "**="         , POWER_EQUAL                 );
+
+        addTranslation( "++"          , PLUS_PLUS                   );
+        addTranslation( "--"          , MINUS_MINUS                 );
+
+        addTranslation( "<<"          , LEFT_SHIFT                  );
+        addTranslation( ">>"          , RIGHT_SHIFT                 );
+        addTranslation( ">>>"         , RIGHT_SHIFT_UNSIGNED        );
+
+        addTranslation( "<<="         , LEFT_SHIFT_EQUAL            );
+        addTranslation( ">>="         , RIGHT_SHIFT_EQUAL           );
+        addTranslation( ">>>="        , RIGHT_SHIFT_UNSIGNED_EQUAL  );
+
+        addTranslation( "&"           , BITWISE_AND                 );
+        addTranslation( "^"           , BITWISE_XOR                 );
+
+        addTranslation( "|="          , BITWISE_OR_EQUAL           );
+        addTranslation( "&="          , BITWISE_AND_EQUAL           );
+        addTranslation( "^="          , BITWISE_XOR_EQUAL           );
+
+        addTranslation( ","           , COMMA                       );
+        addTranslation( ":"           , COLON                       );
+        addTranslation( ";"           , SEMICOLON                   );
+        addTranslation( "?"           , QUESTION                    );
+        addTranslation( "|"           , PIPE                        );
+
+        addTranslation( "${}"         , GSTRING_EXPRESSION_START    );
+
+
+        //
+        // Keywords
+
+        addTranslation( "abstract"    , KEYWORD_ABSTRACT            );
+        addTranslation( "as"          , KEYWORD_AS                  );
+        addTranslation( "assert"      , KEYWORD_ASSERT              );
+        addTranslation( "break"       , KEYWORD_BREAK               );
+        addTranslation( "case"        , KEYWORD_CASE                );
+        addTranslation( "catch"       , KEYWORD_CATCH               );
+        addTranslation( "class"       , KEYWORD_CLASS               );
+        addTranslation( "const"       , KEYWORD_CONST               );
+        addTranslation( "continue"    , KEYWORD_CONTINUE            );
+        addTranslation( "def"         , KEYWORD_DEF                 );
+        addTranslation( "defmacro"    , KEYWORD_DEF                 ); // xxx br defmacro
+        addTranslation( "default"     , KEYWORD_DEFAULT             );
+        addTranslation( "do"          , KEYWORD_DO                  );
+        addTranslation( "else"        , KEYWORD_ELSE                );
+        addTranslation( "extends"     , KEYWORD_EXTENDS             );
+        addTranslation( "final"       , KEYWORD_FINAL               );
+        addTranslation( "finally"     , KEYWORD_FINALLY             );
+        addTranslation( "for"         , KEYWORD_FOR                 );
+        addTranslation( "goto"        , KEYWORD_GOTO                );
+        addTranslation( "if"          , KEYWORD_IF                  );
+        addTranslation( "in"          , KEYWORD_IN                  );
+        addTranslation( "implements"  , KEYWORD_IMPLEMENTS          );
+        addTranslation( "import"      , KEYWORD_IMPORT              );
+        addTranslation( "instanceof"  , KEYWORD_INSTANCEOF          );
+        addTranslation( "interface"   , KEYWORD_INTERFACE           );
+        addTranslation( "mixin"       , KEYWORD_MIXIN               );
+        addTranslation( "native"      , KEYWORD_NATIVE              );
+        addTranslation( "new"         , KEYWORD_NEW                 );
+        addTranslation( "package"     , KEYWORD_PACKAGE             );
+        addTranslation( "private"     , KEYWORD_PRIVATE             );
+        addTranslation( "property"    , KEYWORD_PROPERTY            );
+        addTranslation( "protected"   , KEYWORD_PROTECTED           );
+        addTranslation( "public"      , KEYWORD_PUBLIC              );
+        addTranslation( "return"      , KEYWORD_RETURN              );
+        addTranslation( "static"      , KEYWORD_STATIC              );
+        addTranslation( "super"       , KEYWORD_SUPER               );
+        addTranslation( "switch"      , KEYWORD_SWITCH              );
+        addTranslation( "synchronized", KEYWORD_SYNCHRONIZED        );
+        addTranslation( "this"        , KEYWORD_THIS                );
+        addTranslation( "throw"       , KEYWORD_THROW               );
+        addTranslation( "throws"      , KEYWORD_THROWS              );
+        addTranslation( "transient"   , KEYWORD_TRANSIENT           );
+        addTranslation( "try"         , KEYWORD_TRY                 );
+        addTranslation( "volatile"    , KEYWORD_VOLATILE            );
+        addTranslation( "while"       , KEYWORD_WHILE               );
+
+        addTranslation( "true"        , KEYWORD_TRUE                );
+        addTranslation( "false"       , KEYWORD_FALSE               );
+        addTranslation( "null"        , KEYWORD_NULL                );
+
+        addTranslation( "void"        , KEYWORD_VOID                );
+        addTranslation( "boolean"     , KEYWORD_BOOLEAN             );
+        addTranslation( "byte"        , KEYWORD_BYTE                );
+        addTranslation( "int"         , KEYWORD_INT                 );
+        addTranslation( "short"       , KEYWORD_SHORT               );
+        addTranslation( "long"        , KEYWORD_LONG                );
+        addTranslation( "float"       , KEYWORD_FLOAT               );
+        addTranslation( "double"      , KEYWORD_DOUBLE              );
+        addTranslation( "char"        , KEYWORD_CHAR                );
+    }
+
+
+
+
+  //---------------------------------------------------------------------------
+  // DESCRIPTIONS
+
+
+    private static final Map DESCRIPTIONS = new HashMap();
+
+
+   /**
+    *  Gets the description for the specified type.
+    */
+
+    public static String getDescription( int type ) {
+        Integer typeKey = new Integer(type);
+
+        if (DESCRIPTIONS.containsKey(typeKey)) {
+            return (String)DESCRIPTIONS.get(typeKey);
+        }
+
+        return "<>";
+    }
+
+
+   /**
+    *  Adds a description to the set.
+    */
+
+    private static void addDescription(int type, String description) {
+        addDescription(new Integer(type), description);
+    }
+
+
+   /**
+    *  Adds a description to the set.
+    */
+
+    private static void addDescription(Integer type, String description) {
+        if (description.startsWith("<") && description.endsWith(">")) {
+            DESCRIPTIONS.put(type, description);
+        }
+        else {
+            DESCRIPTIONS.put(type, '"' + description + '"');
+        }
+    }
+
+
+    static {
+
+        Iterator iterator = LOOKUP.keySet().iterator();
+        while( iterator.hasNext() )
+        {
+            String text = (String)iterator.next();
+            Integer key = (Integer)LOOKUP.get(text);
+
+            addDescription( key, text );
+        }
+
+        addDescription( NEWLINE                     , "<newline>"        );
+        addDescription( PREFIX_PLUS_PLUS            , "<prefix ++>"      );
+        addDescription( POSTFIX_PLUS_PLUS           , "<postfix ++>"     );
+        addDescription( PREFIX_MINUS_MINUS          , "<prefix -->"      );
+        addDescription( POSTFIX_MINUS_MINUS         , "<postfix -->"     );
+        addDescription( PREFIX_PLUS                 , "<positive>"       );
+        addDescription( PREFIX_MINUS                , "<negative>"       );
+
+        addDescription( STRING                      , "<string literal>" );
+        addDescription( IDENTIFIER                  , "<identifier>"     );
+        addDescription( INTEGER_NUMBER              , "<integer>"        );
+        addDescription( DECIMAL_NUMBER              , "<decimal>"        );
+
+        addDescription( SYNTH_COMPILATION_UNIT      , "<compilation unit>" );
+        addDescription( SYNTH_CLASS                 , "<class>"          );
+        addDescription( SYNTH_INTERFACE             , "<interface>"      );
+        addDescription( SYNTH_MIXIN                 , "<mixin>"          );
+        addDescription( SYNTH_METHOD                , "<method>"         );
+        addDescription( SYNTH_METHOD_CALL           , "<method call>"    );
+        addDescription( SYNTH_PROPERTY              , "<property>"       );
+        addDescription( SYNTH_PARAMETER_DECLARATION , "<parameter>"      );
+        addDescription( SYNTH_LIST                  , "<list>"           );
+        addDescription( SYNTH_MAP                   , "<map>"            );
+        addDescription( SYNTH_TUPLE                 , "<tuple>"          );
+        addDescription( SYNTH_GSTRING               , "<gstring>"        );
+        addDescription( SYNTH_CAST                  , "<cast>"           );
+        addDescription( SYNTH_BLOCK                 , "<block>"          );
+        addDescription( SYNTH_CLOSURE               , "<closure>"        );
+        addDescription( SYNTH_TERNARY               , "<ternary>"        );
+        addDescription( SYNTH_LABEL                 , "<label>"          );
+        addDescription( SYNTH_VARIABLE_DECLARATION  , "<variable declaration>"       );
+
+        addDescription( GSTRING_START               , "<start of gstring tokens>"    );
+        addDescription( GSTRING_END                 , "<end of gstring tokens>"      );
+        addDescription( GSTRING_EXPRESSION_START    , "<start of gstring expression>");
+        addDescription( GSTRING_EXPRESSION_END      , "<end of gstring expression>"  );
+
+        addDescription( ASSIGNMENT_OPERATOR         , "<assignment operator>"        );
+        addDescription( COMPARISON_OPERATOR         , "<comparison operator>"        );
+        addDescription( MATH_OPERATOR               , "<math operator>"              );
+        addDescription( LOGICAL_OPERATOR            , "<logical operator>"           );
+        addDescription( BITWISE_OPERATOR            , "<bitwise operator>"           );
+        addDescription( RANGE_OPERATOR              , "<range operator>"             );
+        addDescription( REGEX_COMPARISON_OPERATOR   , "<regex comparison operator>"  );
+        addDescription( DEREFERENCE_OPERATOR        , "<dereference operator>"       );
+        addDescription( PREFIX_OPERATOR             , "<prefix operator>"            );
+        addDescription( POSTFIX_OPERATOR            , "<postfix operator>"           );
+        addDescription( INFIX_OPERATOR              , "<infix operator>"             );
+        addDescription( KEYWORD                     , "<keyword>"                    );
+        addDescription( LITERAL                     , "<literal>"                    );
+        addDescription( NUMBER                      , "<number>"                     );
+        addDescription( NAMED_VALUE                 , "<named value>"                );
+        addDescription( TRUTH_VALUE                 , "<truth value>"                );
+        addDescription( PRIMITIVE_TYPE              , "<primitive type>"             );
+        addDescription( CREATABLE_PRIMITIVE_TYPE    , "<creatable primitive type>"   );
+        addDescription( LOOP                        , "<loop>"                       );
+        addDescription( RESERVED_KEYWORD            , "<reserved keyword>"           );
+        addDescription( SYNTHETIC                   , "<synthetic>"                  );
+        addDescription( TYPE_DECLARATION            , "<type declaration>"           );
+        addDescription( DECLARATION_MODIFIER        , "<declaration modifier>"       );
+        addDescription( TYPE_NAME                   , "<type name>"                  );
+        addDescription( CREATABLE_TYPE_NAME         , "<creatable type name>"        );
+        addDescription( MATCHED_CONTAINER           , "<matched container>"          );
+        addDescription( LEFT_OF_MATCHED_CONTAINER   , "<left of matched container>"  );
+        addDescription( RIGHT_OF_MATCHED_CONTAINER  , "<right of matched container>" );
+        addDescription( SWITCH_ENTRIES              , "<valid in a switch body>"     );
+    }
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/syntax/package.html b/groovy/src/main/org/codehaus/groovy/syntax/package.html
new file mode 100644
index 0000000..05b7042
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/syntax/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.syntax.*</title>
+  </head>
+  <body>
+    <p>Lexer, parser and trees.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/Compiler.java b/groovy/src/main/org/codehaus/groovy/tools/Compiler.java
new file mode 100644
index 0000000..0ca3191
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/Compiler.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools;
+
+import java.io.File;
+
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.SourceUnit;
+
+/**
+ *  A convenience front end for getting standard compilations done.
+ *  All compile() routines generate classes to the filesystem.
+ *
+ *  @author <a href="mailto:cpoirier@dreaming.org">Chris Poirier</a>
+ *
+ *  @version $Id$
+ */
+
+public class Compiler {
+    // TODO: delete this constant?
+    public static final Compiler DEFAULT = new Compiler();
+    
+    private CompilerConfiguration configuration = null;  // Optional configuration data
+    
+   /**
+    *  Initializes the Compiler with default configuration.
+    */
+    
+    public Compiler()
+    {
+        configuration = null;
+    }
+    
+    
+   /**
+    *  Initializes the Compiler with the specified configuration.
+    */
+    
+    public Compiler( CompilerConfiguration configuration )
+    {
+        this.configuration = configuration;
+    }
+
+    
+   
+   /**
+    *  Compiles a single File.
+    */
+   
+    public void compile( File file ) throws CompilationFailedException
+    {
+        CompilationUnit unit = new CompilationUnit( configuration );
+        unit.addSource( file );
+        unit.compile();
+    }
+    
+    
+    
+   /**
+    *  Compiles a series of Files.
+    */
+    
+    public void compile( File[] files ) throws CompilationFailedException
+    {
+        CompilationUnit unit = new CompilationUnit( configuration );
+        unit.addSources( files );
+        unit.compile();
+    }
+
+    
+    
+   /**
+    *  Compiles a series of Files from file names.
+    */
+    
+    public void compile( String[] files ) throws CompilationFailedException
+    {
+        CompilationUnit unit = new CompilationUnit( configuration );
+        unit.addSources( files );
+        unit.compile();
+    }
+
+    
+    
+   /**
+    *  Compiles a string of code.
+    */
+    
+    public void compile( String name, String code ) throws CompilationFailedException
+    {
+        CompilationUnit unit = new CompilationUnit( configuration );
+        unit.addSource( new SourceUnit(name, code, configuration, unit.getClassLoader(), unit.getErrorCollector()) );
+        unit.compile();
+    }
+
+}
+
+
+
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/ErrorReporter.java b/groovy/src/main/org/codehaus/groovy/tools/ErrorReporter.java
new file mode 100644
index 0000000..19d44db
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/ErrorReporter.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.codehaus.groovy.tools;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+import org.codehaus.groovy.GroovyExceptionInterface;
+import org.codehaus.groovy.control.CompilationFailedException;
+import groovy.lang.GroovyRuntimeException;
+
+
+/**
+ *  Provides services for reporting compilation errors to the
+ *  user.  Primary entry point is <code>write()</code>.
+ *
+ *  @author <a href="mailto:cpoirier%20AT%20tapestry_os%20DOT%20org">Chris Poirier</a>
+ *  @version $Revision$
+ */
+
+public class ErrorReporter
+{
+    private Throwable   base     = null;    // The exception on which to report
+    private boolean     debug    = false;   // If true, stack traces are always output
+
+    private Object      output   = null;    // The stream/writer to which to output
+
+
+   /**
+    *  Configures a new Reporter.  Default mode is not to report a stack trace unless
+    *  the error was not of one of the supported types.
+    *
+    *  @param e  the exception on which to report
+    */
+
+    public ErrorReporter( Throwable e )
+    {
+        this.base     = e;
+    }
+
+
+   /**
+    *  Configures a new Reporter.  
+    *
+    *  @param e      the exception on which to report
+    *  @param debug  if set, stack traces will be output for all reports
+    */
+
+    public ErrorReporter( Throwable e, boolean debug )
+    {
+        this.base  = e;
+        this.debug = debug;
+    }
+
+
+   /**
+    *  Writes the error to the specified <code>PrintStream</code>.
+    */
+
+    public void write( PrintStream stream )
+    {
+        this.output = stream;
+        dispatch( base, false );
+        stream.flush();
+    }
+
+
+   /**
+    *  Writes the error to the specified <code>PrintWriter</code>.
+    */
+
+    public void write( PrintWriter writer )
+    {
+        this.output = writer;
+        dispatch( base, false );
+        writer.flush();
+    }
+
+
+   /**
+    *  Runs the report once all initialization is complete.
+    */
+
+    protected void dispatch( Throwable object, boolean child )
+    {
+        if( object instanceof CompilationFailedException )
+        {
+            report( (CompilationFailedException)object, child );
+        }
+        else if( object instanceof GroovyExceptionInterface )
+        {
+            report( (GroovyExceptionInterface)object, child );
+        }
+        else if( object instanceof GroovyRuntimeException )
+        {
+            report( (GroovyRuntimeException)object, child );
+        }
+        else if( object instanceof Exception )
+        {
+            report( (Exception)object, child );
+        }
+        else
+        {
+            report( object, child );
+        }
+
+    }
+
+
+
+  //---------------------------------------------------------------------------
+  // REPORTING ROUTINES
+
+
+   /**
+    *  For CompilationFailedException.
+    */
+
+    protected void report( CompilationFailedException e, boolean child )
+    {
+        println( e.toString() );
+        stacktrace( e, false );
+    }
+
+
+
+   /**
+    *  For GroovyException.
+    */
+
+    protected void report( GroovyExceptionInterface e, boolean child )
+    {
+        println( ((Exception)e).getMessage() );
+        stacktrace( (Exception)e, false );
+    }
+
+
+
+   /**
+    *  For Exception.
+    */
+
+    protected void report( Exception e, boolean child )
+    {
+        println( e.getMessage() );
+        stacktrace( e, false );
+    }
+
+
+
+   /**
+    *  For everything else.
+    */
+
+    protected void report( Throwable e, boolean child )
+    {
+        println( ">>> a serious error occurred: " + e.getMessage() );
+        stacktrace( e, true );
+    }
+
+
+
+  //---------------------------------------------------------------------------
+  // GENERAL SUPPORT ROUTINES
+
+
+   /**
+    *  Prints a line to the underlying <code>PrintStream</code>
+    */
+
+    protected void println( String line )
+    {
+        if( output instanceof PrintStream )
+        {
+            ((PrintStream)output).println( line );
+        }
+        else
+        {
+            ((PrintWriter)output).println( line );
+        }
+    }
+
+    protected void println( StringBuffer line )
+    {
+        if( output instanceof PrintStream )
+        {
+            ((PrintStream)output).println( line );
+        }
+        else
+        {
+            ((PrintWriter)output).println( line );
+        }
+    }
+
+
+   /**
+    *  Displays an exception's stack trace, if <code>debug</code> or 
+    *  <code>always</code>.
+    */
+
+    protected void stacktrace( Throwable e, boolean always )
+    {
+        if( debug || always )
+        {
+            println( ">>> stacktrace:" );
+            if( output instanceof PrintStream )
+            {
+                e.printStackTrace( (PrintStream)output );
+            }
+            else
+            {
+                e.printStackTrace( (PrintWriter)output );
+            }
+        }
+    }
+
+
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/FileSystemCompiler.java b/groovy/src/main/org/codehaus/groovy/tools/FileSystemCompiler.java
new file mode 100644
index 0000000..0f5f16b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/FileSystemCompiler.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.PosixParser;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.ConfigurationException;
+import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * Command-line compiler (aka. <tt>groovyc</tt>).
+ * 
+ * @version $Id$
+ */
+public class FileSystemCompiler
+{
+    private final CompilationUnit unit;
+
+    public FileSystemCompiler( CompilerConfiguration configuration) throws ConfigurationException {
+        if (configuration.getJointCompilationOptions()!=null) {
+            this.unit = new JavaAwareCompilationUnit(configuration);
+        } else {
+            this.unit = new CompilationUnit(configuration);
+        }
+    }
+
+    
+    public void compile( String[] paths ) throws Exception 
+    {
+        unit.addSources( paths );
+        unit.compile( );
+    }
+
+    
+    public void compile( File[] files ) throws Exception 
+    {
+        unit.addSources( files );
+        unit.compile( );
+    }
+
+
+    public static void displayHelp(final Options options)
+    {
+        final HelpFormatter formatter = new HelpFormatter ( ) ;
+        formatter.printHelp ( 80 , "groovyc [options] <source-files>" , "options:", options , "" ) ;
+    }
+
+    public static void displayVersion() 
+    {
+        String version = InvokerHelper.getVersion();
+        System.err.println("Groovy compiler version " + version);
+        System.err.println("Copyright 2003-2007 The Codehaus. http://groovy.codehaus.org/");
+        System.err.println("");
+    }
+
+    public static int checkFiles( String[] filenames ) 
+    {
+        int errors = 0;
+
+        for(int i = 0; i < filenames.length; ++i ) 
+        {
+            File file = new File( filenames[i] );
+
+            if( !file.exists() ) 
+            {
+                System.err.println( "error: file not found: " + file );
+                ++errors;
+            }
+            else if( !file.canRead() ) 
+            {
+                System.err.println( "error: file not readable: " + file );
+                ++errors;
+            } 
+        }
+
+        return errors;
+    }
+
+    
+    
+   /**
+    *  Primary entry point for compiling from the command line
+    *  (using the groovyc script).
+    */
+    
+    public static void main( String[] args )
+    {
+        boolean displayStackTraceOnError = false;
+        boolean jointCompilation;
+        
+        try
+        {
+            //
+            // Parse the command line
+            
+            Options options = new Options();
+    
+            options.addOption(OptionBuilder.withLongOpt("classpath").hasArg().withArgName("path").withDescription("Specify where to find the class files.").create());
+            options.addOption(OptionBuilder.withLongOpt("sourcepath").hasArg().withArgName("path").withDescription("Specify where to find the source files.").create());
+            options.addOption(OptionBuilder.withLongOpt("temp").hasArg().withArgName("temp").withDescription("").create());
+            options.addOption(OptionBuilder.withLongOpt("encoding").hasArg().withArgName("encoding").withDescription("Specify the encoding of the user class files.").create());
+            options.addOption(OptionBuilder.hasArg().withDescription("Specify where to place generated class files.").create('d'));
+//            options.addOption(OptionBuilder.withLongOpt("strict").withDescription("Turn on strict type safety.").create('s'));
+            options.addOption(OptionBuilder.withLongOpt("help").withDescription("Print a synopsis of standard options.").create('h'));
+            options.addOption(OptionBuilder.withLongOpt("version").withDescription("Print the version.").create('v'));
+            options.addOption(OptionBuilder.withLongOpt("exception").withDescription("Print stack trace on error.").create('e'));
+            options.addOption(OptionBuilder.withLongOpt("jointCompilation").withDescription("Attach javac compiler to compile .java files.").create('j'));
+    
+            options.addOption(
+                    OptionBuilder.withArgName( "property=value" )
+                    .withValueSeparator()
+                    .hasArgs(2)
+                    .withDescription("")
+                    .create( "J" ));
+            options.addOption(
+                    OptionBuilder.withArgName( "flag" )
+                    .hasArg()
+                    .withDescription("")
+                    .create( "F" ));
+            
+            PosixParser cliParser = new PosixParser();
+    
+            CommandLine cli = cliParser.parse(options, args);
+    
+            if( cli.hasOption('h') ) 
+            {
+                displayHelp(options);
+                return;
+            }
+    
+            if( cli.hasOption('v') ) 
+            {
+                displayVersion();
+                return;
+            }
+    
+            
+            //
+            // Setup the configuration data
+            
+            CompilerConfiguration configuration = new CompilerConfiguration();
+    
+            if( cli.hasOption("classpath") ) 
+            {
+                configuration.setClasspath( cli.getOptionValue("classpath") );
+            }
+    
+            if( cli.hasOption('d') ) 
+            {
+                configuration.setTargetDirectory( cli.getOptionValue('d') );
+            }
+
+            if (cli.hasOption("encoding")) {
+                configuration.setSourceEncoding(cli.getOptionValue("encoding"));
+            }
+
+            displayStackTraceOnError = cli.hasOption('e');
+            
+            // joint compilation parameters
+            jointCompilation = cli.hasOption('j');
+            if (jointCompilation) {
+                Map compilerOptions =  new HashMap();
+                
+                String[] opts = cli.getOptionValues("J");
+                compilerOptions.put("namedValues", opts);
+                
+                opts = cli.getOptionValues("F");
+                compilerOptions.put("flags", opts);
+                
+                configuration.setJointCompilationOptions(compilerOptions);
+            }            
+            
+            //
+            // Load the file name list
+            
+            String[] filenames = cli.getArgs();
+            if( filenames.length == 0 ) 
+            {
+                displayHelp(options);
+                return;
+            }
+    
+            int errors = checkFiles( filenames );
+    
+            //
+            // Create and start the compiler
+            
+            if( errors == 0 ) 
+            {
+                if (jointCompilation) {
+                    File tmpDir = createTempDir();
+                    configuration.getJointCompilationOptions().put("stubDir",tmpDir);
+                }
+                FileSystemCompiler compiler = new FileSystemCompiler(configuration);
+                compiler.compile( filenames );
+            }
+        }
+        catch( Throwable e ) 
+        {
+            new ErrorReporter( e, displayStackTraceOnError ).write( System.err );
+        }
+    }
+
+    private static File createTempDir() throws IOException {
+        File tempFile = File.createTempFile("generated-", "java-source");
+        tempFile.delete();
+        tempFile.mkdirs();
+        return tempFile;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/GroovyClass.java b/groovy/src/main/org/codehaus/groovy/tools/GroovyClass.java
new file mode 100644
index 0000000..11bc987
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/GroovyClass.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools;
+
+public class GroovyClass
+{
+    public static final GroovyClass[] EMPTY_ARRAY = new GroovyClass[ 0 ];
+
+    private String name;
+    private byte[] bytes;
+
+    public GroovyClass(String name,
+                       byte[] bytes)
+    {
+        this.name  = name;
+        this.bytes = bytes;
+    }
+
+    public String getName()
+    {
+        return this.name;
+    }
+
+    public byte[] getBytes()
+    {
+        return this.bytes;
+    }
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/GroovyStarter.java b/groovy/src/main/org/codehaus/groovy/tools/GroovyStarter.java
new file mode 100644
index 0000000..9ee974b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/GroovyStarter.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools;
+
+import java.lang.reflect .*;
+import java.io.FileInputStream;
+
+
+
+/**
+ * Helper class to help classworlds to load classes. 
+ */
+public class GroovyStarter {
+
+    static void printUsage() {
+        System.out.println("possible programs are 'groovyc','groovy','console', and 'groovysh'");
+        System.exit(1);
+    }
+    
+    
+    public static void rootLoader(String args[]) {
+        String conf = System.getProperty("groovy.starter.conf",null);
+        LoaderConfiguration lc = new LoaderConfiguration();
+        
+        // evaluate parameters
+        boolean hadMain=false, hadConf=false, hadCP=false;
+        int argsOffset = 0;
+        while (args.length-argsOffset>0 && !(hadMain && hadConf && hadCP)) {
+            if (args[argsOffset].equals("--classpath")) {
+                if (hadCP) break;
+                if (args.length==argsOffset+1) {
+                    exit("classpath parameter needs argument");
+                }
+                lc.addClassPath(args[argsOffset+1]);
+                argsOffset+=2;
+            } else if (args[argsOffset].equals("--main")) {
+                if (hadMain) break;
+                if (args.length==argsOffset+1) {
+                    exit("main parameter needs argument");
+                }
+                lc.setMainClass(args[argsOffset+1]);
+                argsOffset+=2;
+            } else if (args[argsOffset].equals("--conf")) {
+                if (hadConf) break;
+                if (args.length==argsOffset+1) {
+                    exit("conf parameter needs argument");
+                }
+                conf=args[argsOffset+1];
+                argsOffset+=2;
+            } else {
+                break;
+            }            
+        }
+        
+        // we need to know the class we want to start
+        if (lc.getMainClass()==null && conf==null) {
+            exit("no configuration file or main class specified");
+        }
+        
+        // copy arguments for main class 
+        String[] newArgs = new String[args.length-argsOffset];
+        for (int i=0; i<newArgs.length; i++) {
+            newArgs[i] = args[i+argsOffset];
+        }        
+        // load configuration file
+        if (conf!=null) {
+            try {
+                lc.configure(new FileInputStream(conf));
+            } catch (Exception e) {
+                System.err.println("exception while configuring main class loader:");
+                exit(e);
+            }
+        }
+        // create loader and execute main class
+        ClassLoader loader = new RootLoader(lc);
+        Method m=null;
+        try {
+            Class c = loader.loadClass(lc.getMainClass());
+            m = c.getMethod("main", new Class[]{String[].class});
+        } catch (ClassNotFoundException e1) {
+            exit(e1);
+        } catch (SecurityException e2) {
+            exit(e2);
+        } catch (NoSuchMethodException e2) {
+            exit(e2);
+        }
+        try {
+            m.invoke(null, new Object[]{newArgs});
+        } catch (IllegalArgumentException e3) {
+            exit(e3);
+        } catch (IllegalAccessException e3) {
+            exit(e3);
+        } catch (InvocationTargetException e3) {
+            exit(e3);
+        } 
+    }
+    
+    private static void exit(Exception e) {
+        e.printStackTrace();
+        System.exit(1);
+    }
+    
+    private static void exit(String msg) {
+        System.err.println(msg);
+        System.exit(1);
+    }
+ 
+    // after migration from classworlds to the rootloader rename
+    // the rootLoader method to main and remove this method as 
+    // well as the classworlds method
+   /* public static void main(String args[],ClassWorld classWorld ) {
+        classworlds(args,classWorld);
+    }*/
+    
+    public static void main(String args[]) {
+        try {
+            rootLoader(args);
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+    /*public static void classworlds(String oldArgs[],ClassWorld classWorld ) {
+        try {
+            // Creates a realm with *just* the system classloader
+            ClassRealm system = classWorld.newRealm("system");
+     
+            // Get the groovy realm
+            ClassRealm groovy = classWorld.getRealm("groovy");
+           
+            // import everything from the system realm, because imports
+            // are searched *first* in Classworlds
+            groovy.importFrom("system", "");
+            
+            //add tools.jar to classpath
+            String tools = System.getProperty("tools.jar");
+            if (tools!=null) {
+            	URL ref = (new File(tools)).toURI().toURL();
+            	groovy.addConstituent(ref);
+            }
+        
+            if (oldArgs.length==0) {
+                printUsage();
+                System.exit(1);
+            }
+            
+            String program = oldArgs[0].toLowerCase();
+            String[] args = new String[oldArgs.length-1];
+            for (int i=0; i<args.length; i++) {
+                args[i] = oldArgs[i+1];
+            }
+            
+            if (program.equals("groovyc")) {
+                org.codehaus.groovy.tools.FileSystemCompiler.main(args);
+            } else if (program.equals("groovy")) {
+                GroovyMain.main(args);
+            } else if (program.equals("console")) {
+                // work around needed, because the console is compiled after this files
+                Class c = Class.forName("groovy.ui.Console");
+                Method m= c.getMethod("main", new Class[]{String[].class});
+                m.invoke(null, new Object[]{args});
+            } else if (program.equals("groovysh")) {
+                InteractiveShell.main(args);
+            } else {
+                System.out.println("unknown program "+program);
+                printUsage();
+                System.exit(1);
+            }
+        
+        } catch (Exception e) {
+            e.printStackTrace();
+            System.exit(1);
+        }
+        
+    }*/
+    
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/LoaderConfiguration.java b/groovy/src/main/org/codehaus/groovy/tools/LoaderConfiguration.java
new file mode 100644
index 0000000..859ef1c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/LoaderConfiguration.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools;
+
+import java.io.*;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * class used to configure a RootLoader from a stream or by using
+ * it's methods.
+ * <p/>
+ * The stream can be for example a FileInputStream from a file with
+ * the following format:
+ * <p/>
+ * <pre>
+ * # comment
+ * main is classname
+ * load path
+ * load file
+ * load pathWith${property}
+ * load pathWith!{required.property}
+ * load path/*.jar
+ * load path/&#42;&#42;/&#42;.jar
+ * </pre>
+ * <ul>
+ * <li>All lines starting with "#" are ignored.</li>
+ * <li>The "main is" part may only be once in the file. The String
+ * afterwards is the name of a class with a main method. </li>
+ * <li>The "load" command will add the given file or path to the
+ * classpath in this configuration object. If the path does not 
+ * exist, the path will be ignored.
+ * </li>
+ * <li>properties referenced using !{x} are required.</li>
+ * <li>properties referenced using ${x} are not required. If the 
+ * property does not exist the whole load instruction line will 
+ * be ignored.</li>
+ * <li>* is used to match zero or more characters in a file.</li>
+ * <li>** is used to match zero or more directories.</li> 
+ * </ul>
+ * <p/>
+ * Defining the main class is optional if setRequireMain(boolean) was
+ * called with false, before reading the configuration.
+ * You can use the wildcard "*" to filter the path, but only for files, not
+ * directories. to match directories use "**". The  ${propertyname} is replaced by the value of the system's
+ * property name. You can use user.home here for example. If the property does
+ * not exist, an empty string will be used. If the path or file after the load
+ * command does not exist, the path will be ignored.
+ *
+ * @author Jochen Theodorou
+ * @version $Revision$
+ * @see RootLoader
+ */
+public class LoaderConfiguration {
+
+    private static final String MAIN_PREFIX = "main is", LOAD_PREFIX = "load";
+    private List classPath = new ArrayList();
+    private String main;
+    private boolean requireMain;
+    private static final char WILDCARD = '*';
+    private static final String ALL_WILDCARD = ""+WILDCARD+WILDCARD;
+    private static final String MATCH_FILE_NAME = "\\\\E[^/]+?\\\\Q";
+    private static final String MATCH_ALL = "\\\\E.+?\\\\Q";
+
+    /**
+     * creates a new loader configuration
+     */
+    public LoaderConfiguration() {
+        this.requireMain = true;
+    }
+
+    /**
+     * configures this loader with a stream
+     *
+     * @param is stream used to read the configuration
+     * @throws IOException if reading or parsing the contents of the stream fails
+     */
+    public void configure(InputStream is) throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+        int lineNumber = 0;
+
+        while (true) {
+            String line = reader.readLine();
+            if (line == null) break;
+
+            line = line.trim();
+            lineNumber++;
+
+            if (line.startsWith("#") || line.length() == 0) continue;
+
+            if (line.startsWith(LOAD_PREFIX)) {
+                String loadPath = line.substring(LOAD_PREFIX.length()).trim();
+                loadPath = assignProperties(loadPath);
+                loadFilteredPath(loadPath);
+            } else if (line.startsWith(MAIN_PREFIX)) {
+                if (main != null)
+                    throw new IOException("duplicate definition of main in line " + lineNumber + " : " + line);
+                main = line.substring(MAIN_PREFIX.length()).trim();
+            } else {
+                throw new IOException("unexpected line in " + lineNumber + " : " + line);
+            }
+        }
+
+        if (requireMain && main == null) throw new IOException("missing main class definition in config file");
+    }
+    
+    /**
+     * exapands the properties inside the given string to it's values
+     */
+    private String assignProperties(String str) {
+        int propertyIndexStart = 0, propertyIndexEnd = 0;
+        boolean requireProperty = false;
+        String result = "";
+
+        while (propertyIndexStart < str.length()) {
+            {
+                int i1 = str.indexOf("${", propertyIndexStart);
+                int i2 = str.indexOf("!{", propertyIndexStart);
+                if (i1==-1) {
+                    propertyIndexStart = i2; 
+                } else if (i2==-1) {
+                    propertyIndexStart = i1;
+                } else {
+                    propertyIndexStart = Math.min(i1,i2);
+                }
+                requireProperty=propertyIndexStart==i2;
+            }
+            if (propertyIndexStart == -1) break;
+            result += str.substring(propertyIndexEnd, propertyIndexStart);
+
+            propertyIndexEnd = str.indexOf("}", propertyIndexStart);
+            if (propertyIndexEnd == -1) break;
+
+            String propertyKey = str.substring(propertyIndexStart + 2, propertyIndexEnd);
+            String propertyValue = System.getProperty(propertyKey);
+            // assume properties contain paths
+            if (propertyValue == null) {
+                if (requireProperty) {
+                    throw new IllegalArgumentException("Variable " + propertyKey + " in groovy-starter.conf references a non-existent System property! Try passing the property to the VM using -D" + propertyKey + "=myValue in JAVA_OPTS");
+                } else {
+                    return null;
+                }
+            }
+            propertyValue = getSlashyPath(propertyValue);
+            result += propertyValue;
+
+            propertyIndexEnd++;
+            propertyIndexStart = propertyIndexEnd;
+        }
+
+        if (propertyIndexStart == -1 || propertyIndexStart >= str.length()) {
+            result += str.substring(propertyIndexEnd);
+        } else if (propertyIndexEnd == -1) {
+            result += str.substring(propertyIndexStart);
+        }
+
+        return result;
+    }
+
+
+    /**
+     * load a possible filtered path. Filters are defined
+     * by using the * wildcard like in any shell
+     */
+    private void loadFilteredPath(String filter) {
+        if (filter==null) return;
+        int starIndex = filter.indexOf(WILDCARD);
+        if (starIndex == -1) {
+            addFile(new File(filter));
+            return;
+        }
+        boolean recursive = filter.indexOf(ALL_WILDCARD) != -1;
+        
+        String startDir = filter.substring(0, starIndex - 1);
+        File root = new File(startDir);
+
+        filter = quote(filter);
+        filter = filter.replaceAll("\\"+WILDCARD+"\\"+WILDCARD, MATCH_ALL);
+        filter = filter.replaceAll("\\" + WILDCARD, MATCH_FILE_NAME);
+        Pattern pattern = Pattern.compile(filter);
+
+        final File[] files = root.listFiles();
+        if (files != null) {
+            findMatchingFiles(files, pattern, recursive);
+        }
+    }
+
+    private void findMatchingFiles(File[] files, Pattern pattern, boolean recursive) {
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            String fileString = getSlashyPath(file.getPath());
+            Matcher m = pattern.matcher(fileString);
+            if (m.matches() && file.isFile()) {
+                addFile(file);
+            }
+            if (file.isDirectory() && recursive) {
+                final File[] dirFiles = file.listFiles();
+                if (dirFiles != null) {
+                    findMatchingFiles(dirFiles, pattern, true);
+                }
+            }
+        }
+    }
+
+    // change path representation to something more system independent.
+    // This solution is based on an absolute path
+    private String getSlashyPath(final String path) {
+        String changedPath = path;
+        if (File.separatorChar != '/')
+            changedPath = changedPath.replace(File.separatorChar, '/');
+
+        return changedPath;
+    }
+
+    /**
+     * return true if the parent of the path inside the given
+     * string does exist
+     */
+    private boolean parentPathDoesExist(String path) {
+        File dir = new File(path).getParentFile();
+        return dir.exists();
+    }
+
+    /**
+     * seperates the given path at the last '/'
+     */
+    private String getParentPath(String filter) {
+        int index = filter.lastIndexOf('/');
+        if (index == -1) return "";
+        return filter.substring(index + 1);
+    }
+
+    /**
+     * adds a file to the classpath if it does exist
+     */
+    public void addFile(File f) {
+        if (f != null && f.exists()) {
+            try {
+                classPath.add(f.toURI().toURL());
+            } catch (MalformedURLException e) {
+                throw new AssertionError("converting an existing file to an url should have never thrown an exception!");
+            }
+        }
+    }
+
+    /**
+     * adds a file to the classpath if it does exist
+     */
+    public void addFile(String s) {
+        if (s != null) addFile(new File(s));
+    }
+
+    /**
+     * adds a classpath to this configuration. It expects a string
+     * with multiple paths, seperated by the system dependent
+     *
+     * @see java.io.File#pathSeparator
+     */
+    public void addClassPath(String path) {
+        String[] paths = path.split(File.pathSeparator);
+        for (int i = 0; i < paths.length; i++) {
+            addFile(new File(paths[i]));
+        }
+    }
+
+    /**
+     * gets a classpath as URL[] from this configuration.
+     * This can be used to construct a @see java.net.URLClassLoader
+     */
+    public URL[] getClassPathUrls() {
+        return (URL[]) classPath.toArray(new URL[classPath.size()]);
+    }
+
+    /**
+     * returns the main class or null is no is defined
+     */
+    public String getMainClass() {
+        return main;
+    }
+
+    /**
+     * sets the main class. If there is already a main class
+     * it is overwritten. Calling @see #configure(InputStream)
+     * after calling this method does not require a main class
+     * definition inside the stream
+     */
+    public void setMainClass(String clazz) {
+        main = clazz;
+        requireMain = false;
+    }
+
+    /**
+     * if set to false no main class is required when calling
+     *
+     * @see #configure(InputStream)
+     */
+    public void setRequireMain(boolean requireMain) {
+        this.requireMain = requireMain;
+    }
+
+    // Replacement for Pattern.quote from JDK 1.5
+    private static String quote(String s) {
+        StringBuffer sb = new StringBuffer(s.length() * 2);
+        int eIndex = s.indexOf("\\E");
+        if (eIndex == -1)
+            return sb.append("\\Q").append(s).append("\\E").toString();
+
+        sb.append("\\Q");
+        eIndex = 0;
+        int cur = 0;
+        while ((eIndex = s.indexOf("\\E", cur)) != -1) {
+            sb.append(s.substring(cur, eIndex)).append("\\E\\\\E\\Q");
+            cur = eIndex + 2;
+        }
+        return sb.append(s.substring(cur, s.length())).append("\\E").toString();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/RootLoader.java b/groovy/src/main/org/codehaus/groovy/tools/RootLoader.java
new file mode 100644
index 0000000..49608e9
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/RootLoader.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * This ClassLoader should be used as root of class loaders. Any
+ * RootLoader does have it's own classpath. When searching for a
+ * class or resource this classpath will be used. Parent
+ * Classloaders are ignored first. If a class or resource
+ * can't be found in the classpath of the RootLoader, then parent is
+ * checked.
+ * <p/>
+ * <b>Note:</b> this is very against the normal behavior of
+ * classloaders. Normal is to first check parent and then look in
+ * the resources you gave this classloader.
+ * <p/>
+ * It's possible to add urls to the classpath at runtime through
+ * @see #addURL(URL)
+ *      <p/>
+ *      <b>Why using RootLoader?</b>
+ *      If you have to load classes with multiple classloaders and a
+ *      classloader does know a class which depends on a class only
+ *      a child of this loader does know, then you won't be able to
+ *      load the class. To load the class the child is not allowed
+ *      to redirect it's search for the class to the parent first.
+ *      That way the child can load the class. If the child does not
+ *      have all classes to do this, this fails of course.
+ *      <p/>
+ *      For example:
+ *      <p/>
+ *      <pre>
+ *       parentLoader   (has classpath: a.jar;c.jar)
+ *           |
+ *           |
+ *       childLoader    (has classpath: a.jar;b.jar;c.jar)
+ *       </pre>
+ *      <p/>
+ *      class C (from c.jar) extends B (from b.jar)
+ *      <p/>
+ *      childLoader.find("C")
+ *      --> parentLoader does know C.class, try to load it
+ *      --> to load C.class it has to load B.class
+ *      --> parentLoader is unable to find B.class in a.jar or c.jar
+ *      --> NoClassDefFoundException!
+ *      <p/>
+ *      if childLoader had tried to load the class by itself, there
+ *      would be no problem. Changing childLoader to be a RootLoader
+ *      instance will solve that problem.
+ *
+ * @author Jochen Theodorou
+ */
+public class RootLoader extends URLClassLoader {
+
+    /**
+     * constructs a new RootLoader without classpath
+     *
+     * @param parent the parent Loader
+     */
+    private RootLoader(ClassLoader parent) {
+        this(new URL[0], parent);
+    }
+
+    /**
+     * constructs a new RootLoader with a parent loader and an
+     * array of URLs as classpath
+     */
+    public RootLoader(URL[] urls, ClassLoader parent) {
+        super(urls, parent);
+    }
+
+    private static ClassLoader chooseParent() {
+        ClassLoader cl = RootLoader.class.getClassLoader();
+        if (cl != null) return cl;
+        return ClassLoader.getSystemClassLoader();
+    }
+
+    /**
+     * constructs a new RootLoader with a @see LoaderConfiguration
+     * object which holds the classpath
+     */
+    public RootLoader(LoaderConfiguration lc) {
+        this(chooseParent());
+        Thread.currentThread().setContextClassLoader(this);
+        URL[] urls = lc.getClassPathUrls();
+        for (int i = 0; i < urls.length; i++) {
+            addURL(urls[i]);
+        }
+    }
+
+    /**
+     * loads a class using the name of the class
+     */
+    protected Class loadClass(final String name, boolean resolve) throws ClassNotFoundException {
+        Class c = this.findLoadedClass(name);
+        if (c != null) return c;
+
+        try {
+            c = oldFindClass(name);
+        } catch (ClassNotFoundException cnfe) {
+            // IGNORE
+        }
+        if (c == null) c = super.loadClass(name, resolve);
+
+        if (resolve) resolveClass(c);
+
+        return c;
+    }
+
+    /**
+     * returns the URL of a resource, or null if it is not found
+     */
+    public URL getResource(String name) {
+        URL url = findResource(name);
+        if (url == null) url = super.getResource(name);
+        return url;
+    }
+
+    /**
+     * adds an url to the classpath of this classloader
+     */
+    public void addURL(URL url) {
+        super.addURL(url);
+    }
+
+    private Class oldFindClass(String name) throws ClassNotFoundException {
+        return super.findClass(name);
+    }
+
+    protected Class findClass(String name) throws ClassNotFoundException {
+        throw new ClassNotFoundException(name);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/StringHelper.java b/groovy/src/main/org/codehaus/groovy/tools/StringHelper.java
new file mode 100644
index 0000000..8d91024
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/StringHelper.java
@@ -0,0 +1,67 @@
+package org.codehaus.groovy.tools;

+

+import java.util.LinkedList;

+import java.util.List;

+

+public class StringHelper {

+    private static final char 

+        SPACE = ' ', SINGLE_QUOTE = '\'', DOUBLE_QUOTE = '"';

+    

+    /**

+     * This method tokenizes a string by space characters, 

+     * but ignores spaces in quoted parts,that are parts in 

+     * '' or "". The method does allows the usage of "" in '' 

+     * and '' in "". The space character between tokens is not 

+     * returned. 

+     * 

+     * @param s the string to tokenize

+     * @return the tokens

+     */

+    public static String[] tokenizeUnquoted(String s) {

+        List tokens = new LinkedList();

+        int first = 0;

+        while (first < s.length()) {

+            first = skipWhitespace(s, first);

+            int last = scanToken(s, first);

+            if (first < last) {

+                tokens.add(s.substring(first, last));

+            }

+            first = last;

+        }

+        return (String[])tokens.toArray(new String[0]);

+    }

+

+    private static int scanToken(String s, int pos0) {

+        int pos = pos0;

+        while (pos < s.length()) {

+            char c = s.charAt(pos);

+            if (SPACE==c) break;

+            pos++;

+            if (SINGLE_QUOTE == c) {

+                pos = scanQuoted(s, pos, SINGLE_QUOTE);

+            } else if (DOUBLE_QUOTE == c) {

+                pos = scanQuoted(s, pos, DOUBLE_QUOTE);

+            }

+        }

+        return pos;

+    }

+

+    private static int scanQuoted(String s, int pos0, char quote) {

+        int pos = pos0;

+        while (pos < s.length()) {

+            char c = s.charAt(pos++);

+            if (quote == c) break;

+        }

+        return pos;

+    }

+

+    private static int skipWhitespace(String s, int pos0) {

+        int pos = pos0;

+        while (pos < s.length()) {

+            char c = s.charAt(pos);

+            if (SPACE!=c) break;

+            pos++;

+        }

+        return pos;

+    } 

+}

diff --git a/groovy/src/main/org/codehaus/groovy/tools/Utilities.java b/groovy/src/main/org/codehaus/groovy/tools/Utilities.java
new file mode 100644
index 0000000..d49238d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/Utilities.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools;
+
+/**
+ *  Various utility functions for use in the compiler.
+ */
+
+public abstract class Utilities
+{
+   /**
+    *  Returns a string made up of repetitions of the specified string.
+    */
+
+    public static String repeatString( String pattern, int repeats )
+    {
+        StringBuffer buffer = new StringBuffer( pattern.length() * repeats );
+        for( int i = 0; i < repeats; i++ )
+        {
+            buffer.append( pattern );
+        }
+
+        return new String( buffer );
+    }
+
+
+   /**
+    *  Returns the end-of-line marker.
+    */
+
+    public static String eol()
+    {
+        return eol;
+    }
+    
+    private static String eol = System.getProperty( "line.separator", "\n" ); 
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/ClasspathResourceManager.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/ClasspathResourceManager.java
new file mode 100644
index 0000000..c90f8e9
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/ClasspathResourceManager.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+public class ClasspathResourceManager implements ResourceManager {
+	ClassLoader classLoader;
+	public ClasspathResourceManager() {
+		classLoader = getClass().getClassLoader();
+	}
+	
+	public ClasspathResourceManager(ClassLoader classLoader) {
+		this.classLoader = classLoader;
+	}
+	
+	public Reader getReader(String resourceName) throws IOException {
+        InputStream stream = classLoader.getResourceAsStream(resourceName);
+        return DefaultGroovyMethods.newReader(stream);
+	}
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/FileOutputTool.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/FileOutputTool.java
new file mode 100644
index 0000000..53b8f97
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/FileOutputTool.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.io.File;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+public class FileOutputTool implements OutputTool {
+	public void makeOutputArea(String filename) {
+		File dir = new File(filename);
+		dir.mkdirs();
+	}
+
+	public void writeToOutput(String fileName, String text) throws Exception {
+        File file = new File(fileName);
+        file.getParentFile().mkdirs();
+        DefaultGroovyMethods.write(file, text);
+	}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/FileSystemResourceManager.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/FileSystemResourceManager.java
new file mode 100644
index 0000000..e8a015e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/FileSystemResourceManager.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.Reader;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+public class FileSystemResourceManager implements ResourceManager {
+	private String basedir;
+    private static final String FS = "/";
+	
+	public FileSystemResourceManager() {
+		basedir = "";
+	}
+	
+	public FileSystemResourceManager(String basedir) {
+		this.basedir = basedir + FS;
+	}
+	public Reader getReader(String resourceName) throws IOException {
+		return DefaultGroovyMethods.newReader(new File(basedir + resourceName));
+	}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyDocTemplateEngine.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyDocTemplateEngine.java
new file mode 100644
index 0000000..9475a3b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyDocTemplateEngine.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.groovy.groovydoc.GroovyClassDoc;
+import org.codehaus.groovy.groovydoc.GroovyPackageDoc;
+import org.codehaus.groovy.groovydoc.GroovyRootDoc;
+
+import groovy.text.GStringTemplateEngine;
+import groovy.text.Template;
+import groovy.text.TemplateEngine;
+
+/*
+ * todo
+ *  comma at the end of method parameters
+ *  add comments
+ *  static modifier
+ *  order methods alphabetically (implement compareTo enough?)
+ *  provide links to other html files (e.g. return type of a method)
+ */
+public class GroovyDocTemplateEngine {
+	private TemplateEngine engine;
+	private GroovyDocTool tool;
+	private ResourceManager resourceManager;
+//	private String relativeTemplatePath;
+	private Map docTemplates; // cache
+	private List docTemplatePaths; // once per documentation set
+	private Map packageTemplates; // cache
+	private List packageTemplatePaths; // once per package
+	private Map classTemplates; // cache
+	private List classTemplatePaths; // once per class
+	
+	
+	public GroovyDocTemplateEngine(GroovyDocTool tool, ResourceManager resourceManager, String classTemplate) {
+		this(tool, resourceManager, new String[]{}, new String[]{}, new String[] {classTemplate});
+	}
+
+	public GroovyDocTemplateEngine(GroovyDocTool tool, ResourceManager resourceManager,
+			String[] docTemplates, 
+			String[] packageTemplates, 
+			String[] classTemplates) {
+		this.tool = tool;
+		this.resourceManager = resourceManager;
+		this.docTemplatePaths = Arrays.asList(docTemplates);
+		this.packageTemplatePaths = Arrays.asList(packageTemplates);
+		this.classTemplatePaths = Arrays.asList(classTemplates);
+		this.docTemplates = new HashMap();
+		this.packageTemplates = new HashMap();
+		this.classTemplates = new HashMap();
+		engine = new GStringTemplateEngine();
+		
+	}
+	
+	String applyClassTemplates(GroovyClassDoc classDoc) {
+		String templatePath = (String) classTemplatePaths.get(0); // todo (iterate)
+			
+		String templateWithBindingApplied = "";
+		try {
+			Template t = (Template) classTemplates.get(templatePath);
+			if (t == null) {
+				t = engine.createTemplate(resourceManager.getReader(templatePath));
+				classTemplates.put(templatePath, t);
+			}
+			Map binding = new HashMap();
+	        binding.put("classDoc", classDoc);
+	        
+	        templateWithBindingApplied = t.make(binding).toString();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return templateWithBindingApplied;
+	}
+
+	String applyPackageTemplate(String template, GroovyPackageDoc packageDoc) {
+		String templatePath = template;
+			
+		String templateWithBindingApplied = "";
+		try {
+			Template t = (Template) packageTemplates.get(templatePath);
+			if (t == null) {
+				t = engine.createTemplate(resourceManager.getReader(templatePath));
+				packageTemplates.put(templatePath, t);
+			}
+
+			Map binding = new HashMap();
+	        binding.put("packageDoc", packageDoc);
+	        
+	        templateWithBindingApplied = t.make(binding).toString();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return templateWithBindingApplied;
+	}
+
+	String applyRootDocTemplate(String template, GroovyRootDoc rootDoc) {
+		String templatePath = template;
+			
+		String templateWithBindingApplied = "";
+		try {
+			Template t = (Template) docTemplates.get(templatePath);
+			if (t == null) {
+				t = engine.createTemplate(resourceManager.getReader(templatePath));
+				docTemplates.put(templatePath, t);
+			}
+
+			Map binding = new HashMap();
+	        binding.put("rootDoc", rootDoc);
+	        
+	        templateWithBindingApplied = t.make(binding).toString();
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return templateWithBindingApplied;
+	}
+
+	Iterator classTemplatesIterator() {
+		return classTemplatePaths.iterator();
+	}
+	Iterator packageTemplatesIterator() {
+		return packageTemplatePaths.iterator();
+	}
+	Iterator docTemplatesIterator() {
+		return docTemplatePaths.iterator();
+	}
+
+/*
+	String applyClassTemplatesWithVelocity(GroovyClassDoc classDoc) {
+//		Iterator templates = classTemplates.iterator();
+//		while (templates.hasNext)
+		String templatePath = (String) classTemplates.get(0); // todo (iterate)
+			
+		String templateWithBindingApplied = "";
+		try {
+//			Template t = new GStringTemplateEngine().createTemplate(template);
+			VelocityTemplateEngine t = new VelocityTemplateEngine(new File(".").getAbsolutePath());
+
+			Map binding = new HashMap();
+	        binding.put("classDoc", classDoc);
+	        
+//	        templateWithBindingApplied = t.make(binding).toString();
+	        templateWithBindingApplied = t.apply(templatePath,binding);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+		return templateWithBindingApplied;
+	}
+*/
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyDocTool.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyDocTool.java
new file mode 100644
index 0000000..9068b86
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyDocTool.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2007 Jeremy Rayner
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+import org.codehaus.groovy.groovydoc.GroovyRootDoc;
+
+import java.io.File;
+import java.io.IOException;
+
+public class GroovyDocTool {
+
+    /**
+     * Constructor for use by people who only want to interact with the Groovy Doclet Tree (rootDoc)
+     * @param sourcepath where the sources to be added can be found
+     */
+    public GroovyDocTool(String sourcepath) {
+        this(null,sourcepath,null);
+    }
+
+    public GroovyDocTool(ResourceManager resourceManager, String sourcepath, String classTemplate) {
+		this(resourceManager, sourcepath, new String[]{}, new String[]{}, new String[] {classTemplate});
+	}
+
+	public GroovyDocTool(ResourceManager resourceManager, String sourcepath, String[] docTemplates, String[] packageTemplates, String[] classTemplates) {
+		rootDocBuilder = new GroovyRootDocBuilder(this, sourcepath);
+		if (resourceManager == null) {
+            templateEngine = null;
+        } else {
+            templateEngine = new GroovyDocTemplateEngine(this, resourceManager, docTemplates, packageTemplates, classTemplates);
+        }
+	}
+	
+	public void add(String filename) throws RecognitionException, TokenStreamException, IOException {
+		if (templateEngine != null) {
+            // only print out if we are being used for template generation
+            System.out.println("Loading source files for " + filename);
+        }
+		rootDocBuilder.buildTree(filename);
+	}
+	
+	public GroovyRootDoc getRootDoc() {
+		return rootDocBuilder.getRootDoc();
+	}
+
+	public void renderToOutput(OutputTool output, String destdir) throws Exception {
+		if (templateEngine != null) {
+            GroovyDocWriter writer = new GroovyDocWriter(this, output, templateEngine);
+            GroovyRootDoc rootDoc = rootDocBuilder.getRootDoc();
+            writer.writeRoot(rootDoc, destdir);
+            writer.writePackages(rootDoc, destdir);
+            writer.writeClasses(rootDoc, destdir);
+        } else {
+            throw new UnsupportedOperationException("No template engine was found");
+        }
+	}
+	
+	private final GroovyRootDocBuilder rootDocBuilder;
+	private final GroovyDocTemplateEngine templateEngine;
+
+	String getPath(String filename) {
+		String path = new File(filename).getParent();
+		// path length of 1 indicates that probably is 'default package' i.e. "/"
+		if (path == null || path.length() == 1) {
+			path = "DefaultPackage"; // "DefaultPackage" for 'default package' path, rather than null...
+		}
+		return path;
+	}
+	String getFile(String filename) {
+        return new File(filename).getName();        
+	}
+		
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyDocWriter.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyDocWriter.java
new file mode 100644
index 0000000..6b7646a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyDocWriter.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+import org.codehaus.groovy.groovydoc.GroovyClassDoc;
+import org.codehaus.groovy.groovydoc.GroovyPackageDoc;
+import org.codehaus.groovy.groovydoc.GroovyRootDoc;
+
+/*
+ * todo
+ *  comma at the end of method parameters
+ *  static modifier
+ *  order methods alphabetically (implement compareTo enough?)
+ *  provide links to other html files (e.g. return type of a method)
+ */
+public class GroovyDocWriter {
+	private GroovyDocTool tool;
+	private OutputTool output;
+	private GroovyDocTemplateEngine templateEngine;
+    private static final String FS = "/";
+
+    public GroovyDocWriter(GroovyDocTool tool, OutputTool output, GroovyDocTemplateEngine templateEngine) {
+		this.tool = tool;
+		this.output = output;
+		this.templateEngine = templateEngine;
+	}
+
+	public void writeClasses(GroovyRootDoc rootDoc, String destdir) throws Exception {
+		Iterator classDocs = Arrays.asList(rootDoc.classes()).iterator();
+		while (classDocs.hasNext()) {
+			GroovyClassDoc classDoc = (GroovyClassDoc) classDocs.next();
+			writeClassToOutput(classDoc, destdir);
+		}
+	}
+
+	public void writeRoot(GroovyRootDoc rootDoc, String destdir) throws Exception {
+		output.makeOutputArea(destdir);
+		writeRootDocToOutput(rootDoc, destdir);
+	}
+
+	public void writeClassToOutput(GroovyClassDoc classDoc, String destdir) throws Exception {
+		String destFileName = destdir + FS + classDoc.getFullPathName() + ".html";
+		System.out.println("Generating " + destFileName);
+		String renderedSrc = templateEngine.applyClassTemplates(classDoc);// todo		
+		output.writeToOutput(destFileName, renderedSrc);
+	}	
+
+	public void writePackages(GroovyRootDoc rootDoc, String destdir) throws Exception {
+		Iterator packageDocs = Arrays.asList(rootDoc.specifiedPackages()).iterator();
+		while (packageDocs.hasNext()) {
+			GroovyPackageDoc packageDoc = (GroovyPackageDoc) packageDocs.next();
+			output.makeOutputArea(destdir + FS + packageDoc.name());
+			writePackageToOutput(packageDoc, destdir);
+		}
+	}
+
+	public void writePackageToOutput(GroovyPackageDoc packageDoc, String destdir) throws Exception {
+		Iterator templates = templateEngine.packageTemplatesIterator();
+		while (templates.hasNext()) {
+			String template = (String) templates.next();
+
+			String renderedSrc = templateEngine.applyPackageTemplate(template, packageDoc); // todo
+			
+			String destFileName = destdir + FS + packageDoc.name() + FS + tool.getFile(template);
+			System.out.println("Generating " + destFileName);
+			output.writeToOutput(destFileName, renderedSrc);
+		}
+	}	
+
+	public void writeRootDocToOutput(GroovyRootDoc rootDoc, String destdir) throws Exception {
+		Iterator templates = templateEngine.docTemplatesIterator();
+		while (templates.hasNext()) {
+			String template = (String) templates.next();
+
+			String renderedSrc = templateEngine.applyRootDocTemplate(template, rootDoc); // todo
+			
+			String destFileName = destdir + FS + tool.getFile(template);
+			System.out.println("Generating " + destFileName);
+			output.writeToOutput(destFileName, renderedSrc);
+		}
+	}	
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java
new file mode 100644
index 0000000..c7d9f21
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/GroovyRootDocBuilder.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Map;
+
+import org.codehaus.groovy.antlr.AntlrASTProcessor;
+import org.codehaus.groovy.antlr.SourceBuffer;
+import org.codehaus.groovy.antlr.UnicodeEscapingReader;
+import org.codehaus.groovy.antlr.java.Groovifier;
+import org.codehaus.groovy.antlr.java.Java2GroovyConverter;
+import org.codehaus.groovy.antlr.java.JavaLexer;
+import org.codehaus.groovy.antlr.java.JavaRecognizer;
+import org.codehaus.groovy.antlr.parser.GroovyLexer;
+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
+import org.codehaus.groovy.antlr.treewalker.PreOrderTraversal;
+import org.codehaus.groovy.antlr.treewalker.SourceCodeTraversal;
+import org.codehaus.groovy.antlr.treewalker.Visitor;
+import org.codehaus.groovy.groovydoc.GroovyRootDoc;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+
+import antlr.RecognitionException;
+import antlr.TokenStreamException;
+import antlr.collections.AST;
+
+/*
+ * todo
+ *  comma at the end of method parameters
+ *  add comments
+ *  static modifier
+ *  order methods alphabetically (implement compareTo enough?)
+ *  provide links to other html files (e.g. return type of a method)
+ */
+public class GroovyRootDocBuilder {
+	private final GroovyDocTool tool;
+	private final String sourcepath;
+	private final SimpleGroovyRootDoc rootDoc;
+	private static final char FS = '/';
+
+	
+	public GroovyRootDocBuilder(GroovyDocTool tool,
+			String sourcepath) {
+		this.tool = tool;
+		this.sourcepath = sourcepath;
+		
+		this.rootDoc = new SimpleGroovyRootDoc("root");
+	}
+
+	
+	// parsing
+	public Map getClassDocsFromSingleSource(String packagePath, String file, String src) throws RecognitionException, TokenStreamException {
+		Map classDocsFromSrc = null;
+		if (file.indexOf(".java") > 0) { // simple (for now) decision on java or groovy
+			// java
+			classDocsFromSrc = parseJava(packagePath, file, src);
+		} else if (file.indexOf(".sourcefile") > 0){
+			// java (special name used for testing)
+			classDocsFromSrc = parseJava(packagePath, file, src);
+		} else {
+			// not java, try groovy instead :-)
+			classDocsFromSrc = parseGroovy(packagePath, file, src);
+		}		
+		return classDocsFromSrc;
+	}
+
+	private Map parseJava(String packagePath, String file, String src) throws RecognitionException, TokenStreamException {
+        SourceBuffer sourceBuffer = new SourceBuffer();
+        JavaRecognizer parser = getJavaParser(src, sourceBuffer);
+        String[] tokenNames = parser.getTokenNames();
+        parser.compilationUnit();
+        AST ast = parser.getAST();
+
+        // modify the Java AST into a Groovy AST (just token types)
+		Visitor java2groovyConverter = new Java2GroovyConverter(tokenNames);
+        AntlrASTProcessor java2groovyTraverser = new PreOrderTraversal(java2groovyConverter);
+        java2groovyTraverser.process(ast);
+
+        // now mutate (groovify) the ast into groovy
+		Visitor groovifier = new Groovifier(tokenNames);
+        AntlrASTProcessor groovifierTraverser = new PreOrderTraversal(groovifier);
+        groovifierTraverser.process(ast);
+
+        
+        // now do the business     
+        Visitor visitor = new SimpleGroovyClassDocAssembler(packagePath, file, sourceBuffer);
+        AntlrASTProcessor traverser = new SourceCodeTraversal(visitor);
+
+        traverser.process(ast);
+        
+        return ((SimpleGroovyClassDocAssembler) visitor).getGroovyClassDocs();
+	}
+	
+	private Map parseGroovy(String packagePath, String file, String src) throws RecognitionException, TokenStreamException {
+        SourceBuffer sourceBuffer = new SourceBuffer();
+        GroovyRecognizer parser = getGroovyParser(src, sourceBuffer);
+        String[] tokenNames = parser.getTokenNames();
+        parser.compilationUnit();
+        AST ast = parser.getAST();
+
+        // now do the business     
+        Visitor visitor = new SimpleGroovyClassDocAssembler(packagePath, file, sourceBuffer);
+        AntlrASTProcessor traverser = new SourceCodeTraversal(visitor);
+
+        traverser.process(ast);
+        
+        return ((SimpleGroovyClassDocAssembler) visitor).getGroovyClassDocs();
+	}
+	
+	private JavaRecognizer getJavaParser(String input, SourceBuffer sourceBuffer) {
+		JavaRecognizer parser = null;
+        UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(new StringReader(input),sourceBuffer);
+        JavaLexer lexer = new JavaLexer(unicodeReader);
+        unicodeReader.setLexer(lexer);
+        parser = JavaRecognizer.make(lexer);
+        parser.setSourceBuffer(sourceBuffer);
+		return parser;
+	}
+
+	private GroovyRecognizer getGroovyParser(String input, SourceBuffer sourceBuffer) {
+		GroovyRecognizer parser = null;
+        UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(new StringReader(input),sourceBuffer);
+        GroovyLexer lexer = new GroovyLexer(unicodeReader);
+        unicodeReader.setLexer(lexer);
+        parser = GroovyRecognizer.make(lexer);
+        parser.setSourceBuffer(sourceBuffer);
+		return parser;
+	}
+
+	public void buildTree(String filename) throws IOException, RecognitionException, TokenStreamException {
+		String srcFileName = sourcepath + FS + filename;
+		String src = DefaultGroovyMethods.getText(new File(srcFileName));
+
+		String packagePath = tool.getPath(filename);
+		packagePath = packagePath.replace('\\', FS);
+		String file = tool.getFile(filename);
+		try {
+			Map classDocs = getClassDocsFromSingleSource(packagePath, file, src);
+		
+			rootDoc.putAllClasses(classDocs);
+
+			SimpleGroovyPackageDoc packageDoc = (SimpleGroovyPackageDoc) rootDoc.packageNamed(packagePath);
+			if (packageDoc == null) {
+				packageDoc = new SimpleGroovyPackageDoc(packagePath);
+			}
+			packageDoc.putAll(classDocs);		
+			rootDoc.put(packagePath, packageDoc);
+		} catch (RecognitionException e) {
+			System.out.println("ignored due to RecognitionException: " + filename);
+		} catch (TokenStreamException e) {
+			System.out.println("ignored due to TokenStreamException: " + filename);
+		}
+	}
+
+
+	public GroovyRootDoc getRootDoc() {
+		rootDoc.resolve();
+		
+		return rootDoc;
+	}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/MockOutputTool.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/MockOutputTool.java
new file mode 100644
index 0000000..0483b07
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/MockOutputTool.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+public class MockOutputTool implements OutputTool {
+	Set outputAreas; // dirs
+	Map output;
+	
+	public MockOutputTool() {
+		outputAreas = new HashSet();
+		output = new HashMap();
+	}
+	
+	public void makeOutputArea(String filename) {
+		outputAreas.add(filename);
+	}
+
+	public void writeToOutput(String fileName, String text) throws Exception {
+		output.put(fileName, text);
+	}
+	
+	public boolean isValidOutputArea(String fileName) {
+		return outputAreas.contains(fileName);
+	}
+
+	public String getText(String fileName) {
+		return (String) output.get(fileName);
+	}
+	
+	public String toString() {
+		return "dirs:" + outputAreas + ", files:" + output.keySet();
+	}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/OutputTool.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/OutputTool.java
new file mode 100644
index 0000000..599aa1a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/OutputTool.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+public interface OutputTool {
+	void makeOutputArea(String filename);
+	void writeToOutput(String fileName, String text) throws Exception;
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/ResourceManager.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/ResourceManager.java
new file mode 100644
index 0000000..22a3336
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/ResourceManager.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.io.IOException;
+import java.io.Reader;
+
+public interface ResourceManager {
+	Reader getReader(String resourceName) throws IOException;
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDoc.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDoc.java
new file mode 100644
index 0000000..9e3f2e4
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDoc.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.codehaus.groovy.groovydoc.*;
+
+public class SimpleGroovyClassDoc extends SimpleGroovyProgramElementDoc implements GroovyClassDoc {
+	private final List constructors;
+	private final List fields;
+	private final List methods;
+	private String fullPathName;
+
+	private String superClassName;
+	private GroovyClassDoc superClass;
+	
+	public SimpleGroovyClassDoc(String name) {
+		super(name);
+		constructors = new ArrayList();
+		fields = new ArrayList();
+		methods = new ArrayList();
+	}
+
+	/**
+	 * returns a sorted array of constructors
+	 */
+	public GroovyConstructorDoc[] constructors() {
+		Collections.sort(constructors); // todo - performance / maybe move into a sortMe() method
+		return (GroovyConstructorDoc[]) constructors.toArray(new GroovyConstructorDoc[constructors.size()]);
+	}
+	public boolean add(GroovyConstructorDoc constructor) {
+		return constructors.add(constructor);
+	}
+
+	/**
+	 * returns a sorted array of fields
+	 */
+	public GroovyFieldDoc[] fields() {
+		Collections.sort(fields); // todo - performance / maybe move into a sortMe() method
+		return (GroovyFieldDoc[]) fields.toArray(new GroovyFieldDoc[fields.size()]);
+	}
+	public boolean add(GroovyFieldDoc field) {
+		return fields.add(field);
+	}
+
+	/**
+	 * returns a sorted array of methods
+	 */
+	public GroovyMethodDoc[] methods() {
+		Collections.sort(methods); // todo - performance / maybe move into a sortMe() method
+		return (GroovyMethodDoc[]) methods.toArray(new GroovyMethodDoc[methods.size()]);
+	}
+	public boolean add(GroovyMethodDoc method) {
+		return methods.add(method);
+	}
+		
+	public String getFullPathName() {
+		return fullPathName;
+	}
+	public void setFullPathName(String fullPathName) {
+		this.fullPathName = fullPathName;
+	}
+	
+	public String getRelativeRootPath() {
+		StringTokenizer tokenizer = new StringTokenizer(fullPathName, "/"); // todo windows??
+		StringBuffer sb = new StringBuffer();
+		while (tokenizer.hasMoreTokens()) {
+			tokenizer.nextToken();
+			sb.append("../");
+		}
+		return sb.toString();
+	}	
+	
+	public void setSuperClassName(String className) {
+		superClassName = className;
+	}
+	public GroovyClassDoc superclass() {
+		return superClass;
+	}
+	
+	void resolve(GroovyRootDoc rootDoc) {
+		//resolve type references
+		if (superClassName != null) {
+			superClass = rootDoc.classNamed(superClassName); // todo - take package names into account ?!
+			if (superClass == null) {
+				// The superClass is not in the tree being documented
+				superClass = new SimpleGroovyClassDoc(superClassName); // dummy class with name, not to be put into main tree
+			}
+		} else {
+			superClass = new SimpleGroovyClassDoc("Object"); // dummy class representing java.lang.Object, not to be put into main tree
+		}
+	}
+	// methods from GroovyClassDoc
+	
+	public GroovyConstructorDoc[] constructors(boolean filter) {/*todo*/return null;}
+	public boolean definesSerializableFields() {/*todo*/return false;}
+	public GroovyFieldDoc[] enumConstants() {/*todo*/return null;}
+	public GroovyFieldDoc[] fields(boolean filter) {/*todo*/return null;}
+	public GroovyClassDoc findClass(String className) {/*todo*/return null;}
+	public GroovyClassDoc[] importedClasses() {/*todo*/return null;}
+	public GroovyPackageDoc[] importedPackages() {/*todo*/return null;}
+	public GroovyClassDoc[] innerClasses() {/*todo*/return null;} // not supported in groovy
+	public GroovyClassDoc[] innerClasses(boolean filter) {/*todo*/return null;} // not supported in groovy
+	public GroovyClassDoc[] interfaces() {/*todo*/return null;}
+	public GroovyType[] interfaceTypes() {/*todo*/return null;}
+	public boolean isAbstract() {/*todo*/return false;}
+	public boolean isExternalizable() {/*todo*/return false;}
+	public boolean isSerializable() {/*todo*/return false;}
+	public GroovyMethodDoc[] methods(boolean filter) {/*todo*/return null;}
+	public GroovyFieldDoc[] serializableFields() {/*todo*/return null;}
+	public GroovyMethodDoc[] serializationMethods() {/*todo*/return null;}
+	public boolean subclassOf(GroovyClassDoc gcd) {/*todo*/return false;}
+	public GroovyType superclassType() {/*todo*/return null;}
+//	public GroovyTypeVariable[] typeParameters() {/*todo*/return null;} // not supported in groovy
+//	public GroovyParamTag[] typeParamTags() {/*todo*/return null;} // not supported in groovy
+
+	
+	
+	// methods from GroovyType (todo: remove this horrible copy of SimpleGroovyType.java)
+
+//	public GroovyAnnotationTypeDoc asAnnotationTypeDoc() {/*todo*/return null;}
+	public GroovyClassDoc asClassDoc() {/*todo*/return null;}
+//	public GroovyParameterizedType asParameterizedType() {/*todo*/return null;}
+//	public GroovyTypeVariable asTypeVariable() {/*todo*/return null;}
+//	public GroovyWildcardType asWildcardType() {/*todo*/return null;}
+	public String dimension() {/*todo*/return null;}
+	public boolean isPrimitive() {/*todo*/return false;}
+	public String qualifiedTypeName() {/*todo*/return null;}
+	public String simpleTypeName() {/*todo*/return null;}
+	public String typeName() {/*todo*/return null;}
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDocAssembler.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDocAssembler.java
new file mode 100644
index 0000000..d952214
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyClassDocAssembler.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+import org.codehaus.groovy.antlr.LineColumn;
+import org.codehaus.groovy.antlr.SourceBuffer;
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+import org.codehaus.groovy.antlr.treewalker.VisitorAdapter;
+import org.codehaus.groovy.groovydoc.GroovyConstructorDoc;
+
+public class SimpleGroovyClassDocAssembler extends VisitorAdapter {
+    private Stack stack;
+	private Map classDocs;
+	private SimpleGroovyClassDoc currentClassDoc; // todo - stack?
+	private SimpleGroovyConstructorDoc currentConstructorDoc; // todo - stack?
+	private SimpleGroovyMethodDoc currentMethodDoc; // todo - stack?
+	private SourceBuffer sourceBuffer;
+	private String packagePath;
+	private Pattern previousJavaDocCommentPattern;
+	private static final String FS = "/";
+	
+	public SimpleGroovyClassDocAssembler(String packagePath, String file, SourceBuffer sourceBuffer) {
+		this.sourceBuffer = sourceBuffer;
+		this.packagePath = packagePath;		
+		
+		stack = new Stack();
+        classDocs = new HashMap();
+        String className = file;
+        if (file != null) {
+        	// todo: replace this simple idea of default class name
+        	int idx = file.lastIndexOf(".");
+        	className = file.substring(0,idx);
+        }
+		currentClassDoc = new SimpleGroovyClassDoc(className);
+		currentClassDoc.setFullPathName(packagePath + FS + className);
+		classDocs.put(currentClassDoc.getFullPathName(),currentClassDoc);
+		
+		previousJavaDocCommentPattern = Pattern.compile("(?s)/\\*\\*(.*?)\\*/");
+	}
+	
+	public Map getGroovyClassDocs() {
+		postProcessClassDocs();
+		return classDocs;
+	}
+	
+	// Step through ClassDocs and tie up loose ends
+	private void postProcessClassDocs() {
+		Iterator classDocIterator = classDocs.values().iterator();
+		while (classDocIterator.hasNext()) {
+			SimpleGroovyClassDoc classDoc = (SimpleGroovyClassDoc) classDocIterator.next();
+			
+			GroovyConstructorDoc[] constructors = classDoc.constructors();
+			if (constructors != null && constructors.length == 0) { // add default constructor to doc
+	    		// name of class for the constructor
+	    		GroovyConstructorDoc constructorDoc = new SimpleGroovyConstructorDoc(classDoc.name());
+	        	// don't forget to tell the class about this default constructor.
+	        	classDoc.add(constructorDoc);				
+			}
+		}
+	}
+	
+    public void visitExtendsClause(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+        	GroovySourceAST superClassNode = t.childOfType(GroovyTokenTypes.IDENT);
+        	if (superClassNode != null) {
+        		String superClassName = superClassNode.getText();
+        		currentClassDoc.setSuperClassName(superClassName); // un 'packaged' class name
+        	}
+        }
+    }
+	
+	public void visitClassDef(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT) {
+            // todo is this correct for java + groovy src?
+        	String className = t.childOfType(GroovyTokenTypes.IDENT).getText();
+        	currentClassDoc = (SimpleGroovyClassDoc) classDocs.get(packagePath + FS + className);
+        	if (currentClassDoc == null) {
+        		currentClassDoc = new SimpleGroovyClassDoc(className);
+        	}
+    		// comments
+    		String commentText = getJavaDocCommentsBeforeNode(t);
+    		currentClassDoc.setRawCommentText(commentText);
+
+    		currentClassDoc.setFullPathName(packagePath + FS + currentClassDoc.name());
+        	classDocs.put(currentClassDoc.getFullPathName(), currentClassDoc);
+        }
+    }
+
+	public void visitCtorIdent(GroovySourceAST t,int visit) {
+    	if (visit == OPENING_VISIT) {
+        	// now... get relevant values from the AST
+
+    		// name of class for the constructor
+    		currentConstructorDoc = new SimpleGroovyConstructorDoc(currentClassDoc.name());
+
+    		// comments
+    		String commentText = getJavaDocCommentsBeforeNode(t);
+    		currentConstructorDoc.setRawCommentText(commentText);
+    		
+    		addParametersTo(currentConstructorDoc, t, visit);
+    		
+        	// don't forget to tell the class about this constructor.
+        	currentClassDoc.add(currentConstructorDoc);
+    	}		
+	}
+
+
+	public void visitMethodDef(GroovySourceAST t, int visit) {
+    	if (visit == OPENING_VISIT) {
+        	// init
+
+        	// now... get relevant values from the AST
+
+    		// method name
+    		String methodName = t.childOfType(GroovyTokenTypes.IDENT).getText();
+    		currentMethodDoc = new SimpleGroovyMethodDoc(methodName);
+
+    		// comments
+    		String commentText = getJavaDocCommentsBeforeNode(t);
+    		currentMethodDoc.setRawCommentText(commentText);
+    		
+    		// return type
+    		String returnTypeName = getTypeNodeAsText(t.childOfType(GroovyTokenTypes.TYPE),"def");
+        	SimpleGroovyType returnType = new SimpleGroovyType(returnTypeName); // todo !!!
+        	currentMethodDoc.setReturnType(returnType);
+
+    		addParametersTo(currentMethodDoc, t, visit);
+    		
+        	// don't forget to tell the class about this method so carefully constructed.
+        	currentClassDoc.add(currentMethodDoc);
+    	}
+	}
+	
+	// todo - If no comment before node, then get comment from same node on parent class - ouch!
+	
+	private String getJavaDocCommentsBeforeNode(GroovySourceAST t) {
+		String returnValue = "";
+		
+		String text = sourceBuffer.getSnippet(new LineColumn(1,1), new LineColumn(t.getLine(), t.getColumn()));
+        if (text != null) {
+            int openBlockIndex = text.lastIndexOf("{");
+            int closingBlockIndex = text.lastIndexOf("}");
+            int lastBlockIndex = Math.max(openBlockIndex, closingBlockIndex);
+            if (lastBlockIndex > 0) {
+                text = text.substring(lastBlockIndex);
+            }
+
+            Matcher m = previousJavaDocCommentPattern.matcher(text);
+            if (m.find()) {
+                returnValue = m.group(1);
+            }
+        }
+		
+		return returnValue;
+	}
+
+	private String getText(GroovySourceAST node) {
+		String returnValue = null;
+		if (node != null) {
+			returnValue = node.getText();
+		}
+		return returnValue;
+	}
+
+	private String getTypeNodeAsText(GroovySourceAST typeNode, String defaultText) {
+		String returnValue = defaultText;
+		if (typeNode != null && 
+				typeNode.getType() == GroovyTokenTypes.TYPE && 
+				typeNode.getNumberOfChildren() > 0) {
+			GroovySourceAST child = (GroovySourceAST) typeNode.getFirstChild(); // assume type has only one child // todo type of "foo.bar.Wibble"
+			switch (child.getType()) {
+				// literals
+				case GroovyTokenTypes.LITERAL_boolean: returnValue = "boolean"; break;	
+				case GroovyTokenTypes.LITERAL_byte: returnValue = "byte"; break;	
+				case GroovyTokenTypes.LITERAL_char: returnValue = "char"; break;	
+				// note: LITERAL_def never created
+				case GroovyTokenTypes.LITERAL_double: returnValue = "double"; break;	
+				case GroovyTokenTypes.LITERAL_float: returnValue = "float"; break;	
+				case GroovyTokenTypes.LITERAL_int: returnValue = "int"; break;	
+				case GroovyTokenTypes.LITERAL_long: returnValue = "long"; break;	
+				case GroovyTokenTypes.LITERAL_short: returnValue = "short"; break;	
+				case GroovyTokenTypes.LITERAL_void: returnValue = "void"; break;	
+				
+				// identifiers
+				case GroovyTokenTypes.IDENT: returnValue = child.getText(); break;	
+			}
+		}
+		return returnValue;
+	}
+
+	
+	private void addParametersTo(SimpleGroovyExecutableMemberDoc executableMemberDoc, GroovySourceAST t,int visit) {
+		// parameters
+		GroovySourceAST parametersNode = t.childOfType(GroovyTokenTypes.PARAMETERS);
+		if (parametersNode != null && parametersNode.getNumberOfChildren() > 0) {
+			GroovySourceAST currentNode = (GroovySourceAST) parametersNode.getFirstChild();
+    		while (currentNode != null) {
+    			String parameterTypeName = getTypeNodeAsText(currentNode.childOfType(GroovyTokenTypes.TYPE),"def");
+        		String parameterName = getText(currentNode.childOfType(GroovyTokenTypes.IDENT));
+        		SimpleGroovyParameter parameter = new SimpleGroovyParameter(parameterName);
+        		parameter.setTypeName(parameterTypeName);
+        		
+        		executableMemberDoc.add(parameter);
+        		
+        		currentNode = (GroovySourceAST)currentNode.getNextSibling();
+    		}
+		}
+	}
+
+	
+	
+	public void push(GroovySourceAST t) {
+        stack.push(t);
+    }
+    public GroovySourceAST pop() {
+        if (!stack.empty()) {
+            return (GroovySourceAST) stack.pop();
+        }
+        return null;
+    }
+
+    private GroovySourceAST getParentNode() {
+    	Object parentNode = null;
+    	Object currentNode = stack.pop();
+        if (!stack.empty()) {
+        	parentNode = stack.peek();
+        }
+    	stack.push(currentNode);
+        return (GroovySourceAST) parentNode;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyConstructorDoc.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyConstructorDoc.java
new file mode 100644
index 0000000..69e236e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyConstructorDoc.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import org.codehaus.groovy.groovydoc.*;
+
+public class SimpleGroovyConstructorDoc extends SimpleGroovyExecutableMemberDoc implements GroovyConstructorDoc {
+	public SimpleGroovyConstructorDoc(String name) {
+		super(name);
+	}
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyDoc.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyDoc.java
new file mode 100644
index 0000000..6336d76
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyDoc.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.text.BreakIterator;
+import java.util.Locale;
+
+import org.codehaus.groovy.groovydoc.*;
+
+public class SimpleGroovyDoc implements GroovyDoc {
+	public SimpleGroovyDoc(String name) {
+		this.name = name;
+	}
+	private String name;
+	private String commentText;
+	private String rawCommentText;
+	private String firstSentenceCommentText;
+	public String name() {
+		return name;
+	}
+
+	public String toString() {
+		return "" + getClass() + "(" + name + ")";
+	}
+	public String commentText() {
+		return commentText; // derived from rawCommentText
+	}
+	public String getRawCommentText() {
+		return rawCommentText;
+	}
+	public String firstSentenceCommentText() {
+		return firstSentenceCommentText; // derived from rawCommentText
+	}
+
+	public void setRawCommentText(String rawCommentText) {
+		this.rawCommentText = rawCommentText;
+		
+		// remove all the * from beginning of lines
+		this.commentText = rawCommentText.replaceAll("(?m)^\\s*\\*", ""); // todo precompile regex Patterns
+
+		// Comment Summary using first sentence (Locale sensitive)
+		BreakIterator boundary = BreakIterator.getSentenceInstance(Locale.getDefault()); // todo - allow locale to be passed in
+        boundary.setText(commentText);
+        int start = boundary.first();
+        int end = boundary.next();
+        if (start > -1 && end > -1) {
+        	// need to abbreviate this comment for the summary
+        	this.firstSentenceCommentText = commentText.substring(start,end);
+        } else {
+        	this.firstSentenceCommentText = commentText;
+        }
+		// hack to reformat groovydoc tags into html (todo: tags)
+		this.commentText = this.commentText.replaceAll("(?m)@([a-z]*)\\s*(.*)$","<DL><DT><B>$1:</B></DT><DD>$2</DD></DL>");			// note: use of $ here is a reference to a subsequence (as defined in Matcher.appendReplacement())
+
+		// hack to hide groovydoc tags in summaries
+		this.firstSentenceCommentText = this.firstSentenceCommentText.replaceAll("(?m)@([a-z]*\\s*.*)$",""); // remove @return etc from summaries
+        
+	}
+
+	
+	
+	
+	// Methods from Comparable
+	public int compareTo(Object that) {
+		if (that instanceof SimpleGroovyDoc) {
+			return name.compareTo(((SimpleGroovyDoc) that).name);
+		} else {
+			throw new ClassCastException();
+		}
+	}
+
+	// Methods from GroovyDoc
+//	public GroovyTag[] firstSentenceTags() {/*todo*/return null;}
+//	public GroovyTag[] inlineTags() {/*todo*/return null;}
+	public boolean isAnnotationType() {/*todo*/return false;}
+	public boolean isAnnotationTypeElement() {/*todo*/return false;}
+	public boolean isClass() {/*todo*/return false;}
+	public boolean isConstructor() {/*todo*/return false;}
+	public boolean isEnum() {/*todo*/return false;}
+	public boolean isEnumConstant() {/*todo*/return false;}
+	public boolean isError() {/*todo*/return false;}
+	public boolean isException() {/*todo*/return false;}
+	public boolean isField() {/*todo*/return false;}
+	public boolean isIncluded() {/*todo*/return false;}
+	public boolean isInterface() {/*todo*/return false;}
+	public boolean isMethod() {/*todo*/return false;}
+	public boolean isOrdinaryClass() {/*todo*/return false;}
+//	public GroovySourcePosition position() {/*todo*/return null;}
+//	public GroovySeeTag[] seeTags() {/*todo*/return null;}
+//	public GroovyTag[] tags() {/*todo*/return null;}
+//	public GroovyTag[] tags(String arg0) {/*todo*/return null;}
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyExecutableMemberDoc.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyExecutableMemberDoc.java
new file mode 100644
index 0000000..dae20ac
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyExecutableMemberDoc.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.util.*;
+
+import org.codehaus.groovy.groovydoc.*;
+
+public class SimpleGroovyExecutableMemberDoc extends SimpleGroovyMemberDoc implements GroovyExecutableMemberDoc {
+	List parameters;
+	
+	public SimpleGroovyExecutableMemberDoc(String name) {
+		super(name);
+		parameters = new ArrayList();
+	}
+	public GroovyParameter[] parameters() {
+		return (GroovyParameter[]) parameters.toArray(new GroovyParameter[parameters.size()]);
+	}
+	public void add(GroovyParameter parameter) {
+		parameters.add(parameter);
+	}
+
+	
+	public String flatSignature() {/*todo*/return null;}
+	public boolean isNative() {/*todo*/return false;}
+	public boolean isSynchronized() {/*todo*/return false;}
+	public boolean isVarArgs() {/*todo*/return false;}
+//	public GroovyParamTag[] paramTags() {/*todo*/return null;}
+	public String signature() {/*todo*/return null;}
+	public GroovyClassDoc[] thrownExceptions() {/*todo*/return null;}
+	public GroovyType[] thrownExceptionTypes() {/*todo*/return null;}
+//	public GroovyThrowsTag[] throwsTags() {/*todo*/return null;}
+//	public GroovyTypeVariable[] typeParameters() {/*todo*/return null;}
+//	public GroovyParamTag[] typeParamTags() {/*todo*/return null;}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyFieldDoc.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyFieldDoc.java
new file mode 100644
index 0000000..af506f6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyFieldDoc.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+import org.codehaus.groovy.groovydoc.*;
+
+public class SimpleGroovyFieldDoc extends SimpleGroovyMemberDoc implements GroovyFieldDoc {
+	public SimpleGroovyFieldDoc(String name) {
+		super(name);
+	}
+	public Object constantValue() {/*todo*/return null;}
+	public String constantValueExpression() {/*todo*/return null;}
+	public boolean isTransient() {/*todo*/return false;}
+	public boolean isVolatile() {/*todo*/return false;}
+//	public GroovySerialFieldTag[] serialFieldTags() {/*todo*/return null;}
+	public GroovyType type() {/*todo*/return null;}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyMemberDoc.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyMemberDoc.java
new file mode 100644
index 0000000..62a7f99
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyMemberDoc.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+import org.codehaus.groovy.groovydoc.*;
+
+public class SimpleGroovyMemberDoc extends SimpleGroovyProgramElementDoc implements GroovyMemberDoc{
+	public SimpleGroovyMemberDoc(String name) {
+		super(name);
+	}
+	public boolean isSynthetic() {/*todo*/return false;}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyMethodDoc.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyMethodDoc.java
new file mode 100644
index 0000000..39cdfe6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyMethodDoc.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import org.codehaus.groovy.groovydoc.*;
+
+public class SimpleGroovyMethodDoc extends SimpleGroovyExecutableMemberDoc implements GroovyMethodDoc {
+	public SimpleGroovyMethodDoc(String name) {
+		super(name);
+	}
+	private GroovyType returnType;
+	public GroovyType returnType() {return returnType;}
+	public void setReturnType(GroovyType returnType) {
+		this.returnType = returnType;
+	}
+	
+
+	public boolean isAbstract() {/*todo*/return false;}
+	public GroovyClassDoc overriddenClass() {/*todo*/return null;}
+	public GroovyMethodDoc overriddenMethod() {/*todo*/return null;}
+	public GroovyType overriddenType() {/*todo*/return null;}
+	public boolean overrides(GroovyMethodDoc arg0) {/*todo*/return false;}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyPackageDoc.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyPackageDoc.java
new file mode 100644
index 0000000..1ec297c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyPackageDoc.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.codehaus.groovy.groovydoc.GroovyClassDoc;
+import org.codehaus.groovy.groovydoc.GroovyPackageDoc;
+
+public class SimpleGroovyPackageDoc extends SimpleGroovyDoc implements GroovyPackageDoc {
+	private static final char FS = '/';
+	final Map classDocs;
+	public SimpleGroovyPackageDoc(String name) {
+		super(name);
+		classDocs = new HashMap();
+	}
+	public GroovyClassDoc[] allClasses() {
+		return (GroovyClassDoc[]) classDocs.values().toArray(new GroovyClassDoc[classDocs.values().size()]); // todo performance? sorting?
+	}
+	public void putAll(Map classes) {
+		// 2 way relationship
+		// add reference to classes inside this package
+		classDocs.putAll(classes);
+		
+		// add reference to this package inside classes
+		Iterator itr = classes.values().iterator();
+		while (itr.hasNext()) {
+			SimpleGroovyProgramElementDoc programElement = (SimpleGroovyProgramElementDoc)itr.next();
+			programElement.setContainingPackage(this);
+		}
+	}
+	public String nameWithDots() {
+		return name().replace(FS, '.');
+	}
+	
+	public GroovyClassDoc[] allClasses(boolean arg0) {/*todo*/return null;}
+	public GroovyClassDoc[] enums() {/*todo*/return null;}
+	public GroovyClassDoc[] errors() {/*todo*/return null;}
+	public GroovyClassDoc[] exceptions() {/*todo*/return null;}
+	public GroovyClassDoc findClass(String arg0) {/*todo*/return null;}
+	public GroovyClassDoc[] interfaces() {/*todo*/return null;}
+	public GroovyClassDoc[] ordinaryClasses() {
+		List classDocValues = new ArrayList(classDocs.values());
+		Collections.sort(classDocValues); // todo - performance / maybe move into a sortMe() method
+		return (GroovyClassDoc[]) classDocValues.toArray(new GroovyClassDoc[classDocValues.size()]); // todo CURRENTLY ALL CLASSES!
+	}
+	public String getRelativeRootPath() {
+		StringTokenizer tokenizer = new StringTokenizer(name(), "" + FS);
+		StringBuffer sb = new StringBuffer();
+		while (tokenizer.hasMoreTokens()) {
+			tokenizer.nextToken();
+			sb.append("../");
+		}
+		return sb.toString();
+	}	
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyParameter.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyParameter.java
new file mode 100644
index 0000000..6fbe6c1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyParameter.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import org.codehaus.groovy.groovydoc.*;
+
+public class SimpleGroovyParameter implements GroovyParameter {
+	private String name;
+	private String typeName;
+
+	public SimpleGroovyParameter(String name) {
+		this.name = name;
+	}
+	public String name() {return name;}
+	public String typeName() {return typeName;}
+	
+	public void setTypeName(String typeName) {
+		this.typeName = typeName;
+	}
+
+
+	
+	
+//	public GroovyAnnotationDesc[] annotations() {/*todo*/return null;}
+	public GroovyType type() {/*todo*/return null;}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyProgramElementDoc.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyProgramElementDoc.java
new file mode 100644
index 0000000..c6d7700
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyProgramElementDoc.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+import org.codehaus.groovy.groovydoc.*;
+
+public class SimpleGroovyProgramElementDoc extends SimpleGroovyDoc implements GroovyProgramElementDoc {
+	private GroovyPackageDoc packageDoc;
+	
+	public SimpleGroovyProgramElementDoc(String name) {
+		super(name);
+	}
+	
+	public GroovyPackageDoc containingPackage() {
+		return packageDoc;
+	}
+	public void setContainingPackage(GroovyPackageDoc packageDoc) {
+		this.packageDoc = packageDoc;
+	}
+	//	public GroovyAnnotationDesc[] annotations() {/*todo*/return null;}
+	public GroovyClassDoc containingClass() {/*todo*/return null;}
+	public boolean isFinal() {/*todo*/return false;}
+	public boolean isPackagePrivate() {/*todo*/return false;}
+	public boolean isPrivate() {/*todo*/return false;}
+	public boolean isProtected() {/*todo*/return false;}
+	public boolean isPublic() {/*todo*/return false;}
+	public boolean isStatic() {/*todo*/return false;}
+	public String modifiers() {/*todo*/return null;}
+	public int modifierSpecifier() {/*todo*/return 0;}
+	public String qualifiedName() {/*todo*/return null;}
+
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyRootDoc.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyRootDoc.java
new file mode 100644
index 0000000..5c9e8d8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyRootDoc.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.groovy.groovydoc.GroovyClassDoc;
+import org.codehaus.groovy.groovydoc.GroovyPackageDoc;
+import org.codehaus.groovy.groovydoc.GroovyRootDoc;
+
+public class SimpleGroovyRootDoc extends SimpleGroovyDoc implements GroovyRootDoc {
+	private Map packageDocs;
+	private Map classDocs;
+
+	public SimpleGroovyRootDoc(String name) {
+		super(name);
+		packageDocs = new HashMap();
+		classDocs = new HashMap();
+	}
+
+	// todo - take account of package names !
+	public GroovyClassDoc classNamed(String name) {
+		Iterator itr = classDocs.keySet().iterator();
+		while (itr.hasNext()) {
+			String key = (String) itr.next();
+			int lastSlashIdx = key.lastIndexOf('/');
+			if (lastSlashIdx > 0) {
+				String shortKey = key.substring(lastSlashIdx + 1);
+				if (shortKey.equals(name)) {
+					return (GroovyClassDoc) classDocs.get(key);
+				}
+			}
+		}		
+		return null;
+	}
+	public GroovyClassDoc[] classes() {
+		List classDocValues = new ArrayList(classDocs.values());
+		Collections.sort(classDocValues); // todo - performance / maybe move into a sortMe() method
+		return (GroovyClassDoc[]) classDocValues.toArray(new GroovyClassDoc[classDocValues.size()]);		
+	}
+	public String[][] options() {/*todo*/return null;}
+	public GroovyPackageDoc packageNamed(String packageName) {
+		return (GroovyPackageDoc) packageDocs.get(packageName);
+	}
+	public void putAllClasses(Map classes) {
+		classDocs.putAll(classes);
+	}
+	public void put(String packageName, GroovyPackageDoc packageDoc) {
+		packageDocs.put(packageName, packageDoc);
+	}
+	
+	public GroovyClassDoc[] specifiedClasses() {/*todo*/return null;}
+	public GroovyPackageDoc[] specifiedPackages() {
+		List packageDocValues = new ArrayList(packageDocs.values());
+		Collections.sort(packageDocValues);
+		return (GroovyPackageDoc[]) packageDocValues.toArray(new GroovyPackageDoc[packageDocValues.size()]);
+	}
+
+	
+	
+// GroovyDocErrorReporter interface
+	public void printError(String arg0) {/*todo*/}
+//	public void printError(GroovySourcePosition arg0, String arg1) {/*todo*/}
+	public void printNotice(String arg0) {/*todo*/}
+//	public void printNotice(GroovySourcePosition arg0, String arg1) {/*todo*/}
+	public void printWarning(String arg0) {/*todo*/}
+//	public void printWarning(GroovySourcePosition arg0, String arg1) {/*todo*/}
+
+	public void resolve() {
+		//resolve class names at the end of adding all files to the tree
+		Iterator itr = classDocs.values().iterator();
+		while (itr.hasNext()) {
+			SimpleGroovyClassDoc classDoc = (SimpleGroovyClassDoc) itr.next();
+			classDoc.resolve(this);
+		}
+		
+	}
+
+
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyType.java b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyType.java
new file mode 100644
index 0000000..4631ef9
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/SimpleGroovyType.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.groovydoc;
+import org.codehaus.groovy.groovydoc.*;
+
+public class SimpleGroovyType implements GroovyType {
+	public SimpleGroovyType(String typeName) {
+		this.typeName = typeName;
+	}
+	private String typeName;
+	public String typeName() {return typeName;}
+	
+//	public GroovyAnnotationTypeDoc asAnnotationTypeDoc() {/*todo*/return null;}
+	public GroovyClassDoc asClassDoc() {/*todo*/return null;}
+//	public GroovyParameterizedType asParameterizedType() {/*todo*/return null;}
+//	public GroovyTypeVariable asTypeVariable() {/*todo*/return null;}
+//	public GroovyWildcardType asWildcardType() {/*todo*/return null;}
+	public String dimension() {/*todo*/return null;}
+	public boolean isPrimitive() {/*todo*/return false;}
+	public String qualifiedTypeName() {/*todo*/return null;}
+	public String simpleTypeName() {/*todo*/return null;}
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/class-level/classDocName.html b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/class-level/classDocName.html
new file mode 100644
index 0000000..a8540b1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/class-level/classDocName.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<!--NewPage-->
+<!-- **************************************** -->
+<!-- *                                      * -->
+<!-- *  PLEASE KEEP COMPLICATED EXPRESSIONS * -->
+<!-- *  OUT OF THESE TEMPLATES.             * -->
+<!-- *  i.e. only iterate & print data      * -->
+<!-- *  Thanks                              * -->
+<!-- *  Jez.                                * -->
+<!-- *                                      * -->
+<!-- **************************************** -->
+<HTML>
+<HEAD>
+<!-- Generated by groovydoc (Jeremy Rayner 2007) -->
+<TITLE>
+${classDoc.name()}
+</TITLE>
+
+<META NAME="keywords" CONTENT="getTimezoneOffset()">
+
+<!-- todo stylesheet relative path -->
+<LINK REL ="stylesheet" TYPE="text/css" HREF="${classDoc.getRelativeRootPath()}stylesheet.css" TITLE="Style">
+
+<SCRIPT type="text/javascript">
+function windowTitle()
+{
+    parent.document.title="${classDoc.name()}";
+}
+</SCRIPT>
+<NOSCRIPT>
+</NOSCRIPT>
+
+</HEAD>
+
+<BODY BGCOLOR="white" onload="windowTitle();">
+
+
+
+<HR>
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+<FONT SIZE="-1">
+${classDoc.containingPackage().nameWithDots()}</FONT>
+<BR>
+<FONT CLASS="ClassTitleFont">Class ${classDoc.name()}</FONT></H2>
+<P>
+${classDoc.commentText()}
+</P>
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+
+<A NAME="constructor_summary"><!-- --></A>
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
+<TR BGCOLOR="#D5D5FF" CLASS="TableHeadingColor">
+<TD COLSPAN=2><FONT SIZE="+2">
+<B>Constructor Summary</B></FONT></TD>
+
+</TR>
+<% for (constructor in classDoc.constructors()) { %>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD><CODE><B><a href="">${constructor.name()}</a></B>(<%= constructor.parameters().collect{ param -> "${param.typeName()} ${param.name()}" }.join(", ") %>)</CODE>
+
+<BR>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${constructor.firstSentenceCommentText()}</TD>
+</TR>
+<% } %>
+</TABLE>
+&nbsp;
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
+<TR BGCOLOR="#D5D5FF" CLASS="TableHeadingColor">
+<TH ALIGN="left" COLSPAN="2"><FONT SIZE="+2" CLASS="ClassHeadingFont">
+<B>Method Summary</B></FONT></TH>
+</TR>
+
+<% for (method in classDoc.methods()) { %>
+
+  <TR BGCOLOR="white" CLASS="TableRowColor">
+    <TD ALIGN="right" VALIGN="top" WIDTH="1%"><FONT SIZE="-1" CLASS="ClassItemFont">
+    <CODE>&nbsp;${method.returnType().typeName()}</CODE></FONT></TD>
+    <TD><FONT CLASS="ClassItemFont"><CODE><B><a href="">${method.name()}</a></B>(<%= method.parameters().collect{ param -> "${param.typeName()} ${param.name()}" }.join(", ") %>)</CODE></FONT>
+
+    <BR>
+    <FONT CLASS="ClassItemFont">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;${method.firstSentenceCommentText()}</FONT>
+    </TD>
+  </TR>
+
+<% } %>
+
+
+
+
+
+</TABLE>
+&nbsp;
+<P>
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+
+<A NAME="constructor_detail"><!-- --></A>
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TH ALIGN="left" COLSPAN="1"><FONT SIZE="+2">
+<B>Constructor Detail</B></FONT></TH>
+</TR>
+</TABLE>
+<% for (constructor in classDoc.constructors()) { %>
+<A NAME="QName(java.lang.String)"><!-- --></A><H3>
+${constructor.name()}</H3>
+<PRE>
+<!-- todo - public--> <B>${constructor.name()}</B>(<%= constructor.parameters().collect{ param -> "${param.typeName()} ${param.name()}" }.join(", ") %>)</PRE>
+<DL>
+<DD>${constructor.commentText()}
+<P>
+<!--<DL>
+<DT> todo <B>Parameters:</B><DD><CODE>localPart</CODE> - Local part of the QName</DL>-->
+
+</DL>
+<HR>
+<% } %>
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TH ALIGN="left" COLSPAN="1"><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TH>
+</TR>
+</TABLE>
+<% for (method in classDoc.methods()) { %>
+<A NAME="${method.name()}"><!-- --></A><H3>
+${method.name()}</H3>
+<PRE>
+<!-- todo - public--> ${method.returnType().typeName()} <B>${method.name()}</B>(<%= method.parameters().collect{ param -> "${param.typeName()} ${param.name()}" }.join(", ") %>)</PRE>
+
+<DL>
+<DD>${method.commentText()}
+<P>
+<DD><DL>
+</DL>
+</DD>
+<DD><DL>
+
+<DT><B><!--todo Returns:--></B><DD><!-- todo --></DL>
+</DD>
+</DL>
+<HR>
+<% } %>
+	
+<!-- ========= END OF CLASS DATA ========= -->
+<HR>
+
+
+</BODY>
+</HTML>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/class-level/classDocStructuredData.xml b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/class-level/classDocStructuredData.xml
new file mode 100644
index 0000000..c69ee99
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/class-level/classDocStructuredData.xml
@@ -0,0 +1,24 @@
+<class name="${classDoc.name()}">
+  <containingPackage name="${classDoc.containingPackage().name()}">${classDoc.containingPackage().nameWithDots()}</containingPackage>
+  <extends>${classDoc.superclass().name()}</extends>
+  <comment>${classDoc.commentText()}</comment>
+  <constructors><% for (constructor in classDoc.constructors()) { %>
+    <constructor name="${constructor.name()}">
+      <parameters><% for (parameter in constructor.parameters()) { %>
+        <parameter type="${parameter.typeName()}" name="${parameter.name()}" /><% } %>
+      </parameters>
+      <comment>${constructor.commentText()}</comment>
+    </constructor>
+<% } %>
+  </constructors>
+
+  <methods><% for (method in classDoc.methods()) { %>
+    <method returns="${method.returnType().typeName()}" name="${method.name()}">
+      <parameters><% for (parameter in method.parameters()) { %>
+        <parameter type="${parameter.typeName()}" name="${parameter.name()}" /><% } %>
+      </parameters>
+      <comment>${method.commentText()}</comment>
+    </method>
+<% } %>
+  </methods>
+</class>
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/package-frame.html b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/package-frame.html
new file mode 100644
index 0000000..9bd99b5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/package-frame.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>
+${packageDoc.nameWithDots()}
+</TITLE>
+
+<META NAME="keywords" CONTENT="${packageDoc.name()} package">
+
+<LINK REL ="stylesheet" TYPE="text/css" HREF="${packageDoc.getRelativeRootPath()}stylesheet.css" TITLE="Style">
+
+
+</HEAD>
+
+<BODY BGCOLOR="white">
+<FONT size="+1" CLASS="FrameTitleFont">
+<A HREF="package-summary.html" target="classFrame">${packageDoc.nameWithDots()}</A></FONT>
+<TABLE BORDER="0" WIDTH="100%" SUMMARY="">
+<TR>
+<TD NOWRAP><FONT size="+1" CLASS="FrameHeadingFont">
+Interfaces</FONT>&nbsp;
+<FONT CLASS="FrameItemFont">
+<BR>
+<!--<A HREF="Pausable.html" title="interface in com.javanicus.redbook" target="classFrame"><I>Pausable</I></A>-->
+</FONT></TD>
+</TR>
+</TABLE>
+
+
+<TABLE BORDER="0" WIDTH="100%" SUMMARY="">
+<TR>
+<TD NOWRAP><FONT size="+1" CLASS="FrameHeadingFont">
+Classes</FONT>&nbsp;
+<FONT CLASS="FrameItemFont">
+<!--<BR>
+<A HREF="SimpleFrame.html" title="class in com.javanicus.redbook" target="classFrame">SimpleFrame</A>-->
+<% for (classDoc in packageDoc.ordinaryClasses()) { %>
+<BR>
+<A HREF="${classDoc.name()}.html" title="class in ${packageDoc.name()}" target="classFrame">${classDoc.name()}</A>
+<% } %>
+</TR>
+</TABLE>
+
+
+</BODY>
+</HTML>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/package-summary.html b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/package-summary.html
new file mode 100644
index 0000000..860de31
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/package-summary.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>
+${packageDoc.nameWithDots()} ({todo.title})
+</TITLE>
+
+<META NAME="keywords" CONTENT="${packageDoc.nameWithDots()} package">
+
+<LINK REL ="stylesheet" TYPE="text/css" HREF="${packageDoc.getRelativeRootPath()}stylesheet.css" TITLE="Style">
+
+<SCRIPT type="text/javascript">
+function windowTitle()
+{
+    parent.document.title="${packageDoc.nameWithDots()} ({todo.title})";
+}
+</SCRIPT>
+<NOSCRIPT>
+</NOSCRIPT>
+
+</HEAD>
+
+<BODY BGCOLOR="white" onload="windowTitle();">
+
+
+
+<HR>
+<H2>
+Package ${packageDoc.nameWithDots()}
+</H2>
+
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
+<TR BGCOLOR="#90DDF7" CLASS="TableHeadingColor">
+<TH ALIGN="left" COLSPAN="2"><FONT SIZE="+2">
+<B>Interface Summary</B></FONT></TH>
+</TR>
+<!--<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD WIDTH="15%"><B><A HREF="../../../com/javanicus/redbook/Pausable.html" title="interface in com.javanicus.redbook">Pausable</A></B></TD>
+<TD>&nbsp;</TD>
+</TR>-->
+</TABLE>
+&nbsp;
+
+<P>
+
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
+<TR BGCOLOR="#90DDF7" CLASS="TableHeadingColor">
+<TH ALIGN="left" COLSPAN="2"><FONT SIZE="+2">
+<B>Class Summary</B></FONT></TH>
+</TR>
+<% for (classDoc in packageDoc.ordinaryClasses()) { %>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD WIDTH="15%"><B><A HREF="${classDoc.name()}.html" title="class in ${packageDoc.name()}">${classDoc.name()}</A></B></TD>
+<TD>&nbsp;</TD>
+</TR>
+<% } %>
+</TABLE>
+&nbsp;
+
+<P>
+<DL>
+</DL>
+
+
+<HR>
+
+</BODY>
+</HTML>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/packageDocStructuredData.xml b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/packageDocStructuredData.xml
new file mode 100644
index 0000000..560c7f8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/package-level/packageDocStructuredData.xml
@@ -0,0 +1,6 @@
+<package>
+  <classes><% for (classDoc in packageDoc.ordinaryClasses()) { %>
+    <class name="${classDoc.name()}" />
+<% } %>
+  </classes>
+</package>
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/allclasses-frame.html b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/allclasses-frame.html
new file mode 100644
index 0000000..3227f69
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/allclasses-frame.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>
+All Classes
+</TITLE>
+
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+
+</HEAD>
+
+<BODY BGCOLOR="white">
+<FONT size="+1" CLASS="FrameHeadingFont">
+<B>All Classes</B></FONT>
+
+<BR>
+
+<TABLE BORDER="0" WIDTH="100%" SUMMARY="">
+<TR>
+<TD NOWRAP><FONT CLASS="FrameItemFont">
+<!--<A HREF="org/omg/CORBA/ARG_IN.html" title="interface in org.omg.CORBA" target="classFrame"><I>ARG_IN</I></A>-->
+<!--<BR>-->
+<% for (classDoc in rootDoc.classes()) { %>
+<A HREF="${classDoc.getFullPathName()}.html" title="todo classDoc.getTitle()" target="classFrame">${classDoc.name()}</A>
+<BR>
+<% } %>
+	  
+</FONT></TD>
+</TR>
+</TABLE>
+
+</BODY>
+</HTML>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/index.html b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/index.html
new file mode 100644
index 0000000..87e7fd0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/index.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
+<HTML>
+<HEAD>
+<TITLE>
+{todo.title}
+</TITLE>
+<SCRIPT type="text/javascript">
+    targetPage = "" + window.location.search;
+    if (targetPage != "" && targetPage != "undefined")
+       targetPage = targetPage.substring(1);
+    function loadFrames() {
+        if (targetPage != "" && targetPage != "undefined")
+             top.classFrame.location = top.targetPage;
+    }
+</SCRIPT>
+<NOSCRIPT>
+</NOSCRIPT>
+</HEAD>
+<FRAMESET cols="20%,80%" title="" onLoad="top.loadFrames()">
+<FRAMESET rows="30%,70%" title="" onLoad="top.loadFrames()">
+<FRAME src="overview-frame.html" name="packageListFrame" title="All Packages">
+<FRAME src="allclasses-frame.html" name="packageFrame" title="All classes and interfaces (except non-static nested types)">
+</FRAMESET>
+<FRAME src="overview-summary.html" name="classFrame" title="Package, class and interface descriptions" scrolling="yes">
+<NOFRAMES>
+<H2>
+Frame Alert</H2>
+
+<P>
+This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+<BR>
+Link to<A HREF="overview-summary.html">Non-frame version.</A>
+</NOFRAMES>
+</FRAMESET>
+</HTML>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/overview-frame.html b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/overview-frame.html
new file mode 100644
index 0000000..bc4b1a6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/overview-frame.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>
+Overview ({todo.title})
+</TITLE>
+
+<META NAME="keywords" CONTENT="Overview">
+
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+
+
+</HEAD>
+
+<BODY BGCOLOR="white">
+
+<TABLE BORDER="0" WIDTH="100%" SUMMARY="">
+<TR>
+<TH ALIGN="left" NOWRAP><FONT size="+1" CLASS="FrameTitleFont">
+<B></B></FONT></TH>
+</TR>
+</TABLE>
+
+<TABLE BORDER="0" WIDTH="100%" SUMMARY="">
+<TR>
+<TD NOWRAP><FONT CLASS="FrameItemFont"><A HREF="allclasses-frame.html" target="packageFrame">All Classes</A></FONT>
+<P>
+<FONT size="+1" CLASS="FrameHeadingFont">
+Packages</FONT>
+<BR>
+<!--<FONT CLASS="FrameItemFont"><A HREF="com/javanicus/redbook/decorator/package-frame.html" target="packageFrame">com.javanicus.redbook.decorator</A></FONT>-->
+<% for (packageDoc in rootDoc.specifiedPackages()) { %>
+<FONT CLASS="FrameItemFont"><A HREF="${packageDoc.name()}/package-frame.html" target="packageFrame">${packageDoc.nameWithDots()}</A></FONT>
+<BR>
+<% } %>
+</TD>
+</TR>
+</TABLE>
+
+<P>
+&nbsp;
+</BODY>
+</HTML>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/overview-summary.html b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/overview-summary.html
new file mode 100644
index 0000000..b5b1253
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/overview-summary.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>
+Overview ({todo.title})
+</TITLE>
+
+<META NAME="keywords" CONTENT="Overview">
+
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+
+<SCRIPT type="text/javascript">
+function windowTitle()
+{
+    parent.document.title="Overview ({todo.title})";
+}
+</SCRIPT>
+<NOSCRIPT>
+</NOSCRIPT>
+
+</HEAD>
+
+<BODY BGCOLOR="white" onload="windowTitle();">
+
+
+
+<HR>
+
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
+<TR BGCOLOR="#90DDF7" CLASS="TableHeadingColor">
+<TH ALIGN="left" COLSPAN="2"><FONT SIZE="+2">
+<B>Packages</B></FONT></TH>
+</TR>
+<% for (packageDoc in rootDoc.specifiedPackages()) { %>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD WIDTH="20%"><B><A HREF="${packageDoc.name()}/package-summary.html">${packageDoc.name()}</A></B></TD>
+<TD>&nbsp;</TD>
+</TR>
+<% } %>
+</TABLE>
+
+<P>
+&nbsp;
+
+
+
+<HR>
+
+</BODY>
+</HTML>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/rootDocStructuredData.xml b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/rootDocStructuredData.xml
new file mode 100644
index 0000000..bb36eac
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/rootDocStructuredData.xml
@@ -0,0 +1,10 @@
+<root>
+  <packages><% for (packageDoc in rootDoc.specifiedPackages()) { %>
+  <package name="${packageDoc.name()}" />
+<% } %>
+<packages>
+  <classes><% for (classDoc in rootDoc.classes()) { %>
+    <class path="${classDoc.getFullPathName()}" name="${classDoc.name()}" />
+<% } %>
+  </classes>
+</root>
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/stylesheet.css b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/stylesheet.css
new file mode 100644
index 0000000..c119827
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/gstring-templates/top-level/stylesheet.css
@@ -0,0 +1,34 @@
+/* Groovydoc style sheet */
+
+/* Define colors, fonts and other style attributes here to override the defaults */
+
+/* Page background color */
+body { background-color: #FFFFFF }
+
+/* Headings */
+h1 { font-size: 145% }
+
+/* Table colors */
+.TableHeadingColor     { background: #6398AA } /* Dark cyan */
+.TableSubHeadingColor  { background: #90DDF7 } /* Light cyan */
+.TableRowColor         { background: #FFFFFF } /* White */
+
+/* Font used in left-hand frame lists */
+.FrameTitleFont   { font-size: 100%; font-family: Helvetica, Arial, sans-serif }
+.FrameHeadingFont { font-size:  90%; font-family: Helvetica, Arial, sans-serif }
+.FrameItemFont    { font-size:  90%; font-family: Helvetica, Arial, sans-serif }
+
+/* Font used in class detail pane */
+.ClassTitleFont   { font-family: Arial, Helvetica, sans-serif }
+.ClassHeadingFont { font-family: Arial, Helvetica, sans-serif }
+.ClassItemFont    { font-family: Arial, Helvetica, sans-serif }
+
+/* Navigation bar fonts and colors */
+.NavBarCell1    { background-color:#90DDF7;} /* Light cyan */
+.NavBarCell1Rev { background-color:#00008B;} /* Dark Blue */
+.NavBarFont1    { font-family: Arial, Helvetica, sans-serif; color:#000000;}
+.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}
+
+.NavBarCell2    { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
+.NavBarCell3    { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF;}
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/groovydoc/package.html b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/package.html
new file mode 100644
index 0000000..ec53050
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/groovydoc/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.tools.groovydoc.*</title>
+  </head>
+  <body>
+    <p>GroovyDoc tool.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/javac/JavaAwareCompilationUnit.java b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaAwareCompilationUnit.java
new file mode 100644
index 0000000..4352568
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaAwareCompilationUnit.java
@@ -0,0 +1,140 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.tools.javac;

+

+import groovy.lang.GroovyClassLoader;

+

+import java.util.LinkedList;

+import java.util.Iterator;

+import java.io.File;

+import java.io.FileNotFoundException;

+

+import org.codehaus.groovy.ast.ClassNode;

+import org.codehaus.groovy.ast.ModuleNode;

+import org.codehaus.groovy.classgen.GeneratorContext;

+import org.codehaus.groovy.control.CompilationFailedException;

+import org.codehaus.groovy.control.CompilationUnit;

+import org.codehaus.groovy.control.CompilerConfiguration;

+import org.codehaus.groovy.control.Phases;

+import org.codehaus.groovy.control.SourceUnit;

+

+/**

+ * Created by IntelliJ IDEA. 

+ * User: Alex.Tkachman 

+ * Date: May 31, 2007 Time: 6:48:28 PM 

+ */

+public class JavaAwareCompilationUnit extends CompilationUnit {

+    private LinkedList javaSources; // java sources

+    private JavaStubGenerator stubGenerator;

+    private JavaCompilerFactory compilerFactory = new JavacCompilerFactory();

+    private File generationGoal;

+    

+    public JavaAwareCompilationUnit(CompilerConfiguration configuration) {

+        this(configuration,null);

+    }

+    

+    public JavaAwareCompilationUnit(CompilerConfiguration configuration, GroovyClassLoader groovyClassLoader) {

+        super(configuration,null,groovyClassLoader);

+        javaSources = new LinkedList();

+        generationGoal = (File) configuration.getJointCompilationOptions().get("stubDir");

+        boolean useJava5 = configuration.getTargetBytecode().equals(CompilerConfiguration.POST_JDK5);

+        stubGenerator = new JavaStubGenerator(generationGoal,false,useJava5);

+        

+        addPhaseOperation(new PrimaryClassNodeOperation() {

+            public void call(SourceUnit source, GeneratorContext context, ClassNode node) throws CompilationFailedException {

+                if (javaSources.size() != 0) new JavaAwareResolveVisitor(JavaAwareCompilationUnit.this).startResolving(node,source);

+            }

+        },Phases.CONVERSION);

+

+        addPhaseOperation(new PrimaryClassNodeOperation() {

+            public void call(SourceUnit source, GeneratorContext context, ClassNode classNode) throws CompilationFailedException {

+                try {

+                    if (javaSources.size() != 0) stubGenerator.generateClass(classNode);

+                } catch (FileNotFoundException fnfe) {

+                    source.addException(fnfe);

+                }

+            }

+        },Phases.CONVERSION);

+    }

+

+    public void gotoPhase(int phase) throws CompilationFailedException {

+        super.gotoPhase(phase);

+        // compile Java and clean up

+        if (phase==Phases.SEMANTIC_ANALYSIS && javaSources.size()>0) {

+            Iterator modules = getAST().getModules().iterator();

+            while (modules.hasNext()) {

+                ModuleNode module = (ModuleNode) modules.next();

+                module.setImportsResolved(false);

+            }

+            try {

+                JavaCompiler compiler = compilerFactory.createCompiler(getConfiguration());

+                compiler.compile(javaSources, this);

+            } finally {

+                stubGenerator.clean();

+                javaSources.clear();

+            }

+        }

+    }

+    

+    public void configure(CompilerConfiguration configuration) {

+        super.configure(configuration);

+        // GroovyClassLoader should be able to find classes compiled from java

+        // sources

+        File targetDir = configuration.getTargetDirectory();

+        if (targetDir != null) {

+            final String classOutput = targetDir.getAbsolutePath();

+            getClassLoader().addClasspath(classOutput);

+        }

+    }

+

+    private void addJavaSource(File file) {

+        String path = file.getAbsolutePath();

+        for (Iterator iter = javaSources.iterator(); iter.hasNext();) {

+            String su = (String) iter.next();

+            if (path.equals(su))

+                return;

+        }

+        javaSources.add(path);

+    }

+

+    public void addSources(String[] paths) {

+        for (int i = 0; i < paths.length; i++) {

+            File file = new File(paths[i]);

+            if (file.getName().endsWith(".java"))

+                addJavaSource(file);

+            else

+                addSource(file);

+        }

+    }

+

+    public void addSources(File[] files) {

+        for (int i = 0; i < files.length; i++) {

+            if (files[i].getName().endsWith(".java"))

+                addJavaSource(files[i]);

+            else

+                addSource(files[i]);

+        }

+    }

+

+    public JavaCompilerFactory getCompilerFactory() {

+        return compilerFactory;

+    }

+

+    public void setCompilerFactory(JavaCompilerFactory compilerFactory) {

+        this.compilerFactory = compilerFactory;

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/javac/JavaAwareResolveVisitor.java b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaAwareResolveVisitor.java
new file mode 100644
index 0000000..a4e8eab
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaAwareResolveVisitor.java
@@ -0,0 +1,37 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.tools.javac;

+

+import org.codehaus.groovy.ast.ASTNode;

+import org.codehaus.groovy.ast.stmt.Statement;

+import org.codehaus.groovy.control.CompilationUnit;

+import org.codehaus.groovy.control.ResolveVisitor;

+

+public class JavaAwareResolveVisitor extends ResolveVisitor {

+

+    public JavaAwareResolveVisitor(CompilationUnit cu) {

+        super(cu);

+    }

+    

+    protected void visitClassCodeContainer(Statement code) {

+        // do nothing here, leave it to the normal resolving

+    }

+    

+    protected void addError(String msg, ASTNode expr) {

+        // do nothing here, leave it to the normal resolving        

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/tools/javac/JavaCompiler.java b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaCompiler.java
new file mode 100644
index 0000000..3cad323
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaCompiler.java
@@ -0,0 +1,28 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.tools.javac;

+

+import java.util.List;

+

+import org.codehaus.groovy.control.CompilationUnit;

+

+/**

+ * @author Alex.Tkachman

+ */

+public interface JavaCompiler {

+    void compile(List files, CompilationUnit cu);

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/javac/JavaCompilerFactory.java b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaCompilerFactory.java
new file mode 100644
index 0000000..307e1c0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaCompilerFactory.java
@@ -0,0 +1,23 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.tools.javac;

+

+import org.codehaus.groovy.control.CompilerConfiguration;

+

+public interface JavaCompilerFactory {

+    JavaCompiler createCompiler(CompilerConfiguration config);

+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/javac/JavaStubCompilationUnit.java b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaStubCompilationUnit.java
new file mode 100644
index 0000000..caa68bd
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaStubCompilationUnit.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.javac;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import groovy.lang.GroovyClassLoader;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.Phases;
+import org.codehaus.groovy.control.ResolveVisitor;
+import org.codehaus.groovy.control.SourceUnit;
+
+/**
+ * Compilation unit to <em>only</em> generate Java stubs for Groovy sources.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class JavaStubCompilationUnit
+    extends CompilationUnit
+{
+    private final List javaSources = new LinkedList();
+
+    public JavaStubCompilationUnit(final CompilerConfiguration config, final GroovyClassLoader classLoader, final File outputDirectory) {
+        super(config, null, classLoader);
+
+        addPhaseOperation(new JavaResolverOperation(), Phases.CONVERSION);
+        addPhaseOperation(new StubGeneratorOperation(outputDirectory), Phases.CONVERSION);
+    }
+
+    public void gotoPhase(final int phase) throws CompilationFailedException {
+        super.gotoPhase(phase);
+
+        if (phase == Phases.SEMANTIC_ANALYSIS) {
+            javaSources.clear();
+        }
+    }
+
+    public void addSourceFile(final File file) {
+        if (file.getName().endsWith(".java")) {
+            addJavaSource(file);
+        }
+        else {
+            addSource(file);
+        }
+    }
+
+    private void addJavaSource(final File file) {
+        //
+        // FIXME: Um... not really sure what this is doing...
+        //        So either document what its job is... or whack it ;-)
+        //
+
+        String path = file.getAbsolutePath();
+        Iterator iter = javaSources.iterator();
+
+        while (iter.hasNext()) {
+            if (path.equals(iter.next())) {
+                return;
+            }
+        }
+
+        javaSources.add(path);
+    }
+
+    private boolean haveJavaSources() {
+        return !javaSources.isEmpty();
+    }
+
+    //
+    // Custom Operations
+    //
+
+    /**
+     * Operation to resolve Java sources.
+     */
+    private class JavaResolverOperation
+        extends PrimaryClassNodeOperation
+    {
+        public void call(final SourceUnit source, final GeneratorContext context, final ClassNode node) throws CompilationFailedException {
+            if (haveJavaSources()) {
+                ResolveVisitor v = new JavaAwareResolveVisitor(JavaStubCompilationUnit.this);
+                v.startResolving(node, source);
+            }
+        }
+    }
+
+    /**
+     * Operation to generate Java stubs from Groovy sources.
+     */
+    private class StubGeneratorOperation
+        extends PrimaryClassNodeOperation
+    {
+        private final JavaStubGenerator generator;
+
+        public StubGeneratorOperation(final File outputDirectory) {
+            outputDirectory.mkdirs();
+
+            boolean java5 = false;
+            String target = JavaStubCompilationUnit.this.getConfiguration().getTargetBytecode();
+            
+            // Enable java5 mode if the configuration lets us
+            if (target != null && target.trim().equals("1.5")) {
+                java5 = true;
+            }
+
+            generator = new JavaStubGenerator(outputDirectory, true, java5);
+        }
+
+        public void call(final SourceUnit source, final GeneratorContext context, final ClassNode node) throws CompilationFailedException {
+            if (haveJavaSources()) {
+                try {
+                    generator.generateClass(node);
+                }
+                catch (Exception e) {
+                    source.addException(e);
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/javac/JavaStubGenerator.java b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
new file mode 100644
index 0000000..ed2e473
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/javac/JavaStubGenerator.java
@@ -0,0 +1,624 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.tools.javac;

+

+import org.codehaus.groovy.ast.*;

+import org.codehaus.groovy.ast.expr.ArgumentListExpression;

+import org.codehaus.groovy.ast.expr.ConstantExpression;

+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;

+import org.codehaus.groovy.ast.expr.Expression;

+import org.codehaus.groovy.ast.stmt.BlockStatement;

+import org.codehaus.groovy.ast.stmt.ExpressionStatement;

+import org.codehaus.groovy.ast.stmt.Statement;

+import org.codehaus.groovy.classgen.Verifier;

+import org.codehaus.groovy.control.ResolveVisitor;

+import org.objectweb.asm.Opcodes;

+

+import java.io.*;

+import java.lang.reflect.Constructor;

+import java.lang.reflect.Modifier;

+import java.util.*;

+

+public class JavaStubGenerator

+{

+    private boolean java5 = false;

+    private boolean requireSuperResolved = false;

+    private File outputPath;

+    private List toCompile = new ArrayList();

+

+    public JavaStubGenerator(final File outputPath, final boolean requireSuperResolved, final boolean java5) {

+        this.outputPath = outputPath;

+        this.requireSuperResolved = requireSuperResolved;

+        this.java5 = java5;

+    }

+

+    public JavaStubGenerator(final File outputPath) {

+        this(outputPath, false, false);

+    }

+    

+    private void mkdirs(File parent, String relativeFile) {

+        int index = relativeFile.lastIndexOf('/');

+        if (index==-1) return;

+        File dir = new File(parent,relativeFile.substring(0,index));

+        dir.mkdirs();

+    }

+    

+    public void generateClass(ClassNode classNode) throws FileNotFoundException {

+        // Only attempt to render our self if our super-class is resolved, else wait for it

+        if (requireSuperResolved && !classNode.getSuperClass().isResolved()) {

+            return;

+        }

+

+        String fileName = classNode.getName().replace('.', '/');

+        mkdirs(outputPath,fileName);

+        toCompile.add(fileName);

+

+        File file = new File(outputPath, fileName + ".java");

+        FileOutputStream fos = new FileOutputStream(file);

+        PrintWriter out = new PrintWriter(fos);

+

+        Verifier verifier = new Verifier() {

+            public void addCovariantMethods(ClassNode cn) {}

+            protected void addTimeStamp(ClassNode node) {}

+        };

+        verifier.visitClass(classNode);

+        

+        try {

+            String packageName = classNode.getPackageName();

+            if (packageName != null) {

+                out.println("package " + packageName + ";\n");

+            }

+

+            genImports(classNode, out);

+

+            boolean isInterface = classNode.isInterface();

+            boolean isEnum = (classNode.getModifiers() & Opcodes.ACC_ENUM) !=0;

+            printModifiers(out, classNode.getModifiers()

+                    & ~(isInterface ? Opcodes.ACC_ABSTRACT : 0));

+            

+            if (isInterface) {

+                out.print ("interface ");

+            } else if (isEnum) {

+                out.print ("enum ");

+            } else {

+                out.print ("class ");

+            }

+            out.println(classNode.getNameWithoutPackage());

+            writeGenericsBounds(out, classNode, true);

+

+            ClassNode superClass = classNode.getUnresolvedSuperClass(false);

+

+            if (!isInterface && !isEnum) {

+                out.print("  extends ");

+                printType(superClass,out);

+            } 

+

+            ClassNode[] interfaces = classNode.getInterfaces();

+            if (interfaces != null && interfaces.length > 0) {

+                if (isInterface) {

+                    out.println("  extends");

+                } else {

+                    out.println("  implements");

+                }

+                for (int i = 0; i < interfaces.length - 1; ++i) {

+                    out.print("    ");

+                    printType(interfaces[i], out);

+                    out.print(",");

+                }

+                out.print("    ");

+                printType(interfaces[interfaces.length - 1],out);

+            }

+            out.println(" {");

+

+            genFields(classNode, out, isEnum);

+            genMethods(classNode, out, isEnum);

+            genProps(classNode, out);

+

+            out.println("}");

+        } finally {

+            try {

+                out.close();

+            } catch (Exception e) {

+                // ignore

+            }

+            try {

+                fos.close();

+            } catch (IOException e) {

+                // ignore

+            }

+        }

+    }

+

+    private void genMethods(ClassNode classNode, PrintWriter out, boolean isEnum) {

+        if (!isEnum) getConstructors(classNode, out);

+

+        List methods = classNode.getMethods();

+        if (methods != null)

+            for (Iterator it = methods.iterator(); it.hasNext();) {

+                MethodNode methodNode = (MethodNode) it.next();

+                if(isEnum && methodNode.isSynthetic()) {

+                    // skip values() method and valueOf(String)

+                    String name = methodNode.getName();

+                    Parameter[] params = methodNode.getParameters();

+                    if (name.equals("values") && params.length==0) continue;

+                    if (name.equals("valueOf") && 

+                        params.length==1 &&

+                        params[0].getType().equals(ClassHelper.STRING_TYPE))

+                    {

+                        continue;

+                    }

+                }

+                genMethod(classNode, methodNode, out);

+            }

+    }

+

+    private void getConstructors(ClassNode classNode, PrintWriter out) {

+        List constrs = classNode.getDeclaredConstructors();

+        if (constrs != null)

+            for (Iterator it = constrs.iterator(); it.hasNext();) {

+                ConstructorNode constrNode = (ConstructorNode) it.next();

+                genConstructor(classNode, constrNode, out);

+            }

+    }

+

+    private void genFields(ClassNode classNode, PrintWriter out, boolean isEnum) {

+        List fields = classNode.getFields();

+        if (fields == null) return;

+        ArrayList enumFields = new ArrayList(fields.size());

+        ArrayList normalFields = new ArrayList(fields.size());

+        for (Iterator it = fields.iterator(); it.hasNext();) {

+            FieldNode fieldNode = (FieldNode) it.next();

+            boolean isEnumField = (fieldNode.getModifiers() & Opcodes.ACC_ENUM) !=0;

+            boolean isSynthetic = (fieldNode.getModifiers() & Opcodes.ACC_SYNTHETIC) !=0;

+            if (isEnumField) {

+                enumFields.add(fieldNode);

+            } else if (!isSynthetic) {

+                normalFields.add(fieldNode);

+            }

+        }

+        genEnumFields(enumFields, out);

+        for (Iterator iterator = normalFields.iterator(); iterator.hasNext();) {

+            FieldNode fieldNode = (FieldNode) iterator.next();

+            genField(fieldNode, out);            

+        } 

+    }

+

+    private void genProps(ClassNode classNode, PrintWriter out) {

+        List props = classNode.getProperties();

+        if (props != null)

+            for (Iterator it = props.iterator(); it.hasNext();) {

+                PropertyNode propNode = (PropertyNode) it.next();

+                genProp(propNode, out);

+            }

+    }

+

+    private void genProp(PropertyNode propNode, PrintWriter out) {

+        String name = propNode.getName().substring(0, 1).toUpperCase()

+                + propNode.getName().substring(1);

+

+        String getterName = "get" + name;

+

+        boolean skipGetter = false;

+        List getterCandidates = propNode.getField().getOwner().getMethods(getterName);

+        if (getterCandidates != null)

+            for (Iterator it = getterCandidates.iterator(); it.hasNext();) {

+                MethodNode method = (MethodNode) it.next();

+                if (method.getParameters().length == 0) {

+                    skipGetter = true;

+                }

+            }

+

+        if (!skipGetter) {

+            printModifiers(out, propNode.getModifiers());

+

+            printType(propNode.getType(), out);

+            out.print(" ");

+            out.print(getterName);

+            out.print("() { ");

+

+            printReturn(out, propNode.getType());

+

+            out.println(" }");

+        }

+

+        String setterName = "set" + name;

+

+        boolean skipSetter = false;

+        List setterCandidates = propNode.getField().getOwner().getMethods( setterName);

+        if (setterCandidates != null)

+            for (Iterator it = setterCandidates.iterator(); it.hasNext();) {

+                MethodNode method = (MethodNode) it.next();

+                if (method.getParameters().length == 1) {

+                    skipSetter = true;

+                }

+            }

+

+        if (!skipSetter) {

+            printModifiers(out, propNode.getModifiers());

+            out.print("void ");

+            out.print(setterName);

+            out.print("(");

+            printType(propNode.getType(), out);

+            out.println(" value) {}");

+        }

+    }

+    

+    private void genEnumFields(List fields, PrintWriter out) {

+        if (fields.size()==0) return;

+        boolean first = true;

+        for (Iterator iterator = fields.iterator(); iterator.hasNext();) {

+            FieldNode fieldNode = (FieldNode) iterator.next();

+            if (!first) {

+                out.print(", ");

+            } else {

+                first = false;

+            }

+            out.print(fieldNode.getName());            

+        }

+        out.println(";");

+    }

+

+    private void genField(FieldNode fieldNode, PrintWriter out) {

+        if ((fieldNode.getModifiers()&Opcodes.ACC_PRIVATE)!=0) return;

+        printModifiers(out, fieldNode.getModifiers());

+

+        printType(fieldNode.getType(), out);

+

+        out.print(" ");

+        out.print(fieldNode.getName());

+        out.println(";");

+    }

+

+    private ConstructorCallExpression getConstructorCallExpression(

+            ConstructorNode constructorNode) {

+        Statement code = constructorNode.getCode();

+        if (!(code instanceof BlockStatement))

+            return null;

+

+        BlockStatement block = (BlockStatement) code;

+        List stats = block.getStatements();

+        if (stats == null || stats.size() == 0)

+            return null;

+

+        Statement stat = (Statement) stats.get(0);

+        if (!(stat instanceof ExpressionStatement))

+            return null;

+

+        Expression expr = ((ExpressionStatement) stat).getExpression();

+        if (!(expr instanceof ConstructorCallExpression))

+            return null;

+

+        return (ConstructorCallExpression) expr;

+    }

+

+    private void genConstructor(ClassNode clazz, ConstructorNode constructorNode, PrintWriter out) {

+        // printModifiers(out, constructorNode.getModifiers());

+

+        out.print("public "); // temporary hack

+        out.print(clazz.getNameWithoutPackage());

+

+        printParams(constructorNode, out);

+

+        ConstructorCallExpression constrCall = getConstructorCallExpression(constructorNode);

+        if (constrCall == null || !constrCall.isSpecialCall()) {

+            out.println(" {}");

+        }

+        else {

+            out.println(" {");

+

+            genSpecialConstructorArgs(out, constructorNode, constrCall);

+

+            out.println("}");

+        }

+    }

+

+    private ConstructorNode selectAccessibleConstructorFromSuper(ConstructorNode node) {

+        ClassNode type = node.getDeclaringClass();

+        ClassNode superType = type.getSuperClass();

+

+        for (Iterator iter = superType.getDeclaredConstructors().iterator(); iter.hasNext();) {

+            ConstructorNode c = (ConstructorNode)iter.next();

+

+            // Only look at things we can actually call

+            if (c.isPublic() || c.isProtected()) {

+                return c;

+            }

+        }

+

+        if (!superType.isResolved()) {

+            throw new Error("Super-class (" + superType.getName() + ")should have been resolved already for type: " + type.getName());

+        }

+

+        Constructor[] constructors = superType.getTypeClass().getDeclaredConstructors();

+

+        for (int i=0; i<constructors.length; i++) {

+            int mod = constructors[i].getModifiers();

+

+            // Only look at things we can actualy call

+            if (Modifier.isPublic(mod) || Modifier.isProtected(mod)) {

+                Class[] types = constructors[i].getParameterTypes();

+                Parameter[] params = new Parameter[types.length];

+                for (int j=0; j<types.length; j++) {

+                    ClassNode ptype = ClassHelper.make(types[j]);

+                    params[j] = new Parameter(ptype, types[j].getName());

+                }

+

+                return new ConstructorNode(mod, params, null, null);

+            }

+        }

+        

+        return null;

+    }

+

+    private void genSpecialConstructorArgs(PrintWriter out, ConstructorNode node, ConstructorCallExpression constrCall) {

+        // Select a constructor from our class, or super-class which is legal to call,

+        // then write out an invoke w/nulls using casts to avoid abigous crapo

+

+        ConstructorNode c = selectAccessibleConstructorFromSuper(node);

+        if (c != null) {

+            out.print("super (");

+

+            Parameter[] params = c.getParameters();

+            for (int i=0; i<params.length; i++) {

+                printDefaultValue(out, params[i].getType());

+                if (i + 1 < params.length) {

+                    out.print(", ");

+                }

+            }

+            

+            out.println(");");

+            return;

+        }

+

+        // Otherwise try the older method based on the constructor's call expression

+        Expression arguments = constrCall.getArguments();

+

+        if (constrCall.isSuperCall()) {

+            out.print("super(");

+        }

+        else {

+            out.print("this(");

+        }

+

+        // Else try to render some arguments

+        if (arguments instanceof ArgumentListExpression) {

+            ArgumentListExpression argumentListExpression = (ArgumentListExpression) arguments;

+            List args = argumentListExpression.getExpressions();

+

+            for (Iterator it = args.iterator(); it.hasNext();) {

+                Expression arg = (Expression) it.next();

+

+                if (arg instanceof ConstantExpression) {

+                    ConstantExpression expression = (ConstantExpression) arg;

+                    Object o = expression.getValue();

+

+                    if (o instanceof String) {

+                        out.print("(String)null");

+                    }

+                    else {

+                        out.print(expression.getText());

+                    }

+                }

+                else {

+                    printDefaultValue(out, arg.getType());

+                }

+

+                if (arg != args.get(args.size() - 1)) {

+                    out.print(", ");

+                }

+            }

+        }

+

+        out.println(");");

+    }

+

+    private void genMethod(ClassNode clazz, MethodNode methodNode, PrintWriter out) {

+        if (methodNode.getName().equals("<clinit>")) return;

+        

+        if (!clazz.isInterface()) printModifiers(out, methodNode.getModifiers());

+

+        printType(methodNode.getReturnType(), out);

+        out.print(" ");

+        out.print(methodNode.getName());

+

+        printParams(methodNode, out);

+

+        if ((methodNode.getModifiers() & Opcodes.ACC_ABSTRACT) != 0) {

+            out.println(";");

+        } else {

+            out.print(" { ");

+            ClassNode retType = methodNode.getReturnType();

+            printReturn(out, retType);

+            out.println("}");

+        }

+    }

+

+    private void printReturn(PrintWriter out, ClassNode retType) {

+        String retName = retType.getName();

+        if (!retName.equals("void")) {

+            out.print("return ");

+

+            printDefaultValue(out, retType);

+

+            out.print(";");

+        }

+    }

+

+    private void printDefaultValue(PrintWriter out, ClassNode type) {

+        out.print("(");

+        printType(type,out);

+        out.print(")");

+

+        if (ClassHelper.isPrimitiveType(type)) {

+            if (type==ClassHelper.boolean_TYPE){

+                out.print("false");

+            } else {

+                out.print("0");

+            }

+        } else {

+            out.print("null");

+        }

+    }

+

+    private void printType(ClassNode type, PrintWriter out) {

+        if (type.isArray()) {

+            printType(type.getComponentType(),out);

+            out.print("[]");

+        } else {

+            writeGenericsBounds(out,type,false);

+        }

+    }

+

+    private void printTypeName(ClassNode type, PrintWriter out) {

+        if (ClassHelper.isPrimitiveType(type)) {

+            if (type==ClassHelper.boolean_TYPE) {

+                out.print("boolean");

+            } else if (type==ClassHelper.char_TYPE) {

+                out.print("char");

+            } else if (type==ClassHelper.int_TYPE) {

+                out.print("int");

+            } else if (type==ClassHelper.short_TYPE) {

+                out.print("short");

+            } else if (type==ClassHelper.long_TYPE) {

+                out.print("long");

+            } else if (type==ClassHelper.float_TYPE) {

+                out.print("float");

+            } else if (type==ClassHelper.double_TYPE) {

+                out.print("double");

+            } else if (type==ClassHelper.byte_TYPE) {

+                out.print("byte");

+            } else {

+                out.print("void");

+            }

+        } else {

+            out.print(type.redirect().getName().replace('$', '.'));

+        }

+    }

+    

+    

+    private void writeGenericsBounds(PrintWriter out, ClassNode type, boolean skipName) {

+        if (!skipName) printTypeName(type,out);

+        if (java5 && !type.isGenericsPlaceHolder()) writeGenericsBounds(out,type.getGenericsTypes());

+    }

+    

+    private void writeGenericsBounds(PrintWriter out, GenericsType[] genericsTypes) {

+        if (genericsTypes==null || genericsTypes.length==0) return;

+        out.print('<');

+        for (int i = 0; i < genericsTypes.length; i++) {

+            if (i!=0) out.print(", ");

+            writeGenericsBounds(out,genericsTypes[i]);

+        }

+        out.print('>');

+    }

+    

+    private void writeGenericsBounds(PrintWriter out, GenericsType genericsType) {

+        if (genericsType.isPlaceholder()) {

+            out.print(genericsType.getName());

+        } else {

+            printTypeName(genericsType.getType(), out);

+            ClassNode[] upperBounds = genericsType.getUpperBounds();

+            ClassNode lowerBound = genericsType.getLowerBound();

+            if (upperBounds!=null) {

+                out.print(" extends ");

+                for (int i = 0; i < upperBounds.length; i++) {

+                    printType(upperBounds[i],out);

+                    if (i+1<upperBounds.length) out.print(" & ");

+                }

+            } else if (lowerBound!=null) {

+                out.print(" super ");

+                printType(lowerBound,out);

+            }       

+        }

+    }

+

+    private void printParams(MethodNode methodNode, PrintWriter out) {

+        out.print("(");

+        Parameter[] parameters = methodNode.getParameters();

+

+        if (parameters != null && parameters.length != 0) {

+            for (int i = 0; i != parameters.length; ++i) {

+                printType(parameters[i].getType(), out);

+                

+                out.print(" ");

+                out.print(parameters[i].getName());

+

+                if (i + 1 < parameters.length) {

+                    out.print(", ");

+                }

+            }

+        }

+        

+        out.print(")");

+    }

+

+    private void printModifiers(PrintWriter out, int modifiers) {

+        if ((modifiers & Opcodes.ACC_PUBLIC) != 0)

+            out.print("public ");

+

+        if ((modifiers & Opcodes.ACC_PROTECTED) != 0)

+            out.print("protected ");

+

+        if ((modifiers & Opcodes.ACC_PRIVATE) != 0)

+            out.print("private ");

+        

+        if ((modifiers & Opcodes.ACC_STATIC) != 0)

+            out.print("static ");

+

+        if ((modifiers & Opcodes.ACC_SYNCHRONIZED) != 0)

+            out.print("synchronized ");

+

+        if ((modifiers & Opcodes.ACC_ABSTRACT) != 0)

+            out.print("abstract ");

+    }

+

+    private void genImports(ClassNode classNode, PrintWriter out) {

+        Set imports = new HashSet();

+

+        //

+        // HACK: Add the default imports... since things like Closure and GroovyObject seem to parse out w/o fully qualified classnames.

+        //

+        imports.addAll(Arrays.asList(ResolveVisitor.DEFAULT_IMPORTS));

+        

+        ModuleNode moduleNode = classNode.getModule();

+        for (Iterator it = moduleNode.getImportPackages().iterator(); it.hasNext();) {

+            imports.add(it.next());

+        }

+

+        for (Iterator it = moduleNode.getImports().iterator(); it.hasNext();) {

+            ImportNode imp = (ImportNode) it.next();

+            String name = imp.getType().getName();

+            int lastDot = name.lastIndexOf('.');

+            if (lastDot != -1)

+                imports.add(name.substring(0, lastDot + 1));

+        }

+

+        for (Iterator it = imports.iterator(); it.hasNext();) {

+            String imp = (String) it.next();

+            out.print("import ");

+            out.print(imp);

+            out.println("*;");

+        }

+        out.println();

+    }

+

+    public void clean() {

+        for (Iterator it = toCompile.iterator(); it.hasNext();) {

+            String path = (String) it.next();

+            new File(outputPath, path + ".java").delete();

+        }

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/tools/javac/JavacCompilerFactory.java b/groovy/src/main/org/codehaus/groovy/tools/javac/JavacCompilerFactory.java
new file mode 100644
index 0000000..dcb86e6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/javac/JavacCompilerFactory.java
@@ -0,0 +1,25 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.tools.javac;

+

+import org.codehaus.groovy.control.CompilerConfiguration;

+

+public class JavacCompilerFactory implements JavaCompilerFactory {

+    public JavaCompiler createCompiler(CompilerConfiguration config) {

+        return new JavacJavaCompiler(config);

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/tools/javac/JavacJavaCompiler.java b/groovy/src/main/org/codehaus/groovy/tools/javac/JavacJavaCompiler.java
new file mode 100644
index 0000000..f67a2ba
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/javac/JavacJavaCompiler.java
@@ -0,0 +1,172 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.tools.javac;

+

+import groovy.lang.GroovyClassLoader;

+

+import java.io.File;

+import java.io.PrintWriter;

+import java.io.StringWriter;

+import java.lang.reflect.Method;

+import java.util.LinkedList;

+import java.util.List;

+import java.util.Locale;

+import java.util.Map;

+

+import org.codehaus.groovy.control.CompilationUnit;

+import org.codehaus.groovy.control.CompilerConfiguration;

+import org.codehaus.groovy.control.messages.ExceptionMessage;

+import org.codehaus.groovy.control.messages.SimpleMessage;

+import org.codehaus.groovy.runtime.DefaultGroovyMethods;

+

+public class JavacJavaCompiler implements JavaCompiler {

+    private CompilerConfiguration config;

+

+    public JavacJavaCompiler(CompilerConfiguration config) {

+        this.config = config;

+    }

+

+    public void compile(List files, CompilationUnit cu) {

+        String[] javacParameters = makeParameters(files);

+        StringWriter javacOutput=null;

+        int javacReturnValue = 0;

+        try {

+            Class javac = findJavac(cu);

+            Method method=null;

+            try {

+                method = javac.getMethod("compile", new Class[]{String[].class, PrintWriter.class});

+                javacOutput = new StringWriter();

+                PrintWriter writer = new PrintWriter(javacOutput);

+                Object ret = method.invoke(null, new Object[]{javacParameters,writer});

+                javacReturnValue = ((Integer) ret).intValue();

+            } catch (NoSuchMethodException e) {}

+            if (method==null) {

+                method = javac.getMethod("compile", new Class[]{String[].class});

+                Object ret = method.invoke(null, new Object[]{javacParameters});

+                javacReturnValue = ((Integer) ret).intValue();

+            }

+            cu.getConfiguration().getOutput();

+        } catch (Exception e) {

+            cu.getErrorCollector().addFatalError(new ExceptionMessage(e, true, cu));

+        }

+        if (javacReturnValue!=0) {

+            switch (javacReturnValue) {

+                case 1: addJavacError("Compile error during compilation with javac.",cu,javacOutput); break;

+                case 2: addJavacError("Invalid commandline usage for javac.",cu,javacOutput); break;

+                case 3: addJavacError("System error during compilation with javac.",cu,javacOutput); break;

+                case 4: addJavacError("Abnormal termination of javac.",cu,javacOutput); break;

+                default: addJavacError("unexpected return value by javac.",cu,javacOutput); break;

+            }

+        }        

+    }

+    

+    private void addJavacError(String header, CompilationUnit cu, StringWriter msg) {

+        if (msg!=null)  {

+            header = header+"\n"+msg.getBuffer().toString();

+        } else {

+            header = header+

+            "\nThis javac version does not support compile(String[],PrintWriter), "+

+            "so no further details of the error are available. The message error text "+

+            "should be found on System.err.\n";

+        }

+        cu.getErrorCollector().addFatalError(new SimpleMessage(header,cu));

+    }

+    

+

+    private String[] makeParameters(List files) {

+        Map options = config.getJointCompilationOptions();

+        LinkedList paras = new LinkedList();

+

+        File target = config.getTargetDirectory();

+        if (target == null) target = new File(".");

+

+        // defaults

+        paras.add("-d");

+        paras.add(target.getAbsolutePath());

+        paras.add("-sourcepath");

+        paras.add(((File) options.get("stubDir")).getAbsolutePath());

+        

+        // add flags

+        String[] flags = (String[]) options.get("flags");

+        if (flags != null) {

+            for (int i = 0; i < flags.length; i++) {

+                paras.add('-' + flags[i]);

+            }

+        }

+

+        boolean hadClasspath=false;

+        // add namedValues

+        String[] namedValues = (String[]) options.get("namedValues");

+        if (namedValues != null) {

+            for (int i = 0; i < namedValues.length; i += 2) {

+                String name = namedValues[i];

+                if (name.equals("classpath")) hadClasspath = true;

+                paras.add('-' + name);

+                paras.add(namedValues[i + 1]);

+            }

+        }

+        

+        // append classpath if not already defined

+        if (!hadClasspath) {

+            paras.add("-classpath");

+            List classpath = config.getClasspath();

+            String resultPath = DefaultGroovyMethods.join(classpath, File.pathSeparator);

+            paras.add(resultPath);

+        }

+        

+        // files to compile

+        paras.addAll(files);

+

+        return (String[]) paras.toArray(new String[paras.size()]);

+    }

+

+    private Class findJavac(CompilationUnit cu) throws ClassNotFoundException {

+        String main = "com.sun.tools.javac.Main";

+        try {

+            return Class.forName(main);

+        } catch (ClassNotFoundException e) {}

+            

+        try {

+            ClassLoader cl = this.getClass().getClassLoader();

+            return cl.loadClass(main);

+        } catch (ClassNotFoundException e) {}

+        

+        try {

+            return ClassLoader.getSystemClassLoader().loadClass(main);

+        } catch (ClassNotFoundException e) {}

+        

+        try {

+            return cu.getClassLoader().getParent().loadClass(main);

+        } catch (ClassNotFoundException e3) {}

+        

+        

+        // couldn't find compiler - try to find tools.jar

+        // based on java.home setting

+        String javaHome = System.getProperty("java.home");

+        if (javaHome.toLowerCase(Locale.US).endsWith("jre")) {

+            javaHome = javaHome.substring(0, javaHome.length() - 4);

+        }

+        File toolsJar = new File((javaHome + "/lib/tools.jar"));

+        if (toolsJar.exists()) {

+            GroovyClassLoader loader = cu.getClassLoader();

+            loader.addClasspath(toolsJar.getAbsolutePath());

+            return loader.loadClass(main);

+        }

+        

+        throw new ClassNotFoundException("unable to locate the java compiler com.sun.tools.javac.Main, please change your classloader settings");

+    }

+}

diff --git a/groovy/src/main/org/codehaus/groovy/tools/javac/package.html b/groovy/src/main/org/codehaus/groovy/tools/javac/package.html
new file mode 100644
index 0000000..91321da
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/javac/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.tools.javac.*</title>
+  </head>
+  <body>
+    <p>Classes related to the joint compiler.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/package.html b/groovy/src/main/org/codehaus/groovy/tools/package.html
new file mode 100644
index 0000000..d388b5c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/package.html
@@ -0,0 +1,10 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.tools.*</title>
+  </head>
+  <body>
+    <p>
+      Compiler entry points and miscellaneous development tools.
+    </p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/BufferManager.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/BufferManager.groovy
new file mode 100644
index 0000000..9acbb2d
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/BufferManager.groovy
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+import org.codehaus.groovy.tools.shell.util.Logger
+
+/**
+ * Manages the shells buffers.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class BufferManager
+{
+    protected final Logger log = Logger.create(this.class)
+    
+    final List buffers = []
+
+    int selected
+
+    BufferManager() {
+        reset()
+    }
+    
+    void reset() {
+        buffers.clear()
+        
+        create(true)
+        
+        log.debug('Buffers reset')
+    }
+    
+    List current() {
+        assert !buffers.isEmpty()
+        
+        return buffers[selected]
+    }
+    
+    void select(final int index) {
+        assert index >= 0 && index < buffers.size()
+        
+        selected = index
+    }
+
+    int create(final boolean select) {
+        buffers << []
+
+        def i = buffers.size() - 1
+
+        if (select) {
+            select(i)
+        }
+        
+        if (log.debugEnabled) {
+            log.debug("Created new buffer with index: $i")
+        }
+        
+        return i
+    }
+
+    void delete(final int index) {
+        assert index >= 0 && index < buffers.size()
+
+        buffers.remove(index)
+        
+        if (log.debugEnabled) {
+            log.debug("Deleted buffer with index: $index")
+        }
+    }
+
+    int size() {
+        return buffers.size()
+    }
+
+    //
+    // Selected operators
+    //
+    
+    void deleteSelected() {
+        delete(selected)
+
+        def i = selected - 1
+
+        if (i < 0) {
+            select(0)
+        }
+        else {
+            select(i)
+        }
+    }
+
+    void clearSelected() {
+        current().clear()
+    }
+
+    void updateSelected(final List buffer) {
+        assert buffer != null
+        
+        buffers[selected] = buffer
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/Command.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/Command.groovy
new file mode 100644
index 0000000..9923071
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/Command.groovy
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+import jline.Completor
+
+/**
+ * Provides the interface required for command extentions.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+interface Command
+{
+    String getName()
+
+    String getShortcut()
+
+    Completor getCompletor()
+
+    String getDescription()
+
+    String getUsage()
+
+    String getHelp()
+
+    List/*<CommandAlias>*/ getAliases()
+
+    Object execute(List args)
+    
+    boolean getHidden()
+}
+
+/**
+ * Thrown to indicate a problem with command execution.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class CommandException
+    extends Exception
+{
+    final Command command
+    
+    CommandException(final Command command, final String msg) {
+        super(msg)
+        
+        assert command
+        
+        this.command = command
+    }
+    
+    CommandException(final Command command, final String msg, final Throwable cause) {
+        super(msg, cause)
+        
+        assert command
+        
+        this.command = command
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/CommandAlias.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/CommandAlias.groovy
new file mode 100644
index 0000000..fb5b151
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/CommandAlias.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+/**
+ * Provides simple command aliasing.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class CommandAlias
+    extends CommandSupport
+{
+    final String targetName
+    
+    CommandAlias(final Shell shell, final String name, final String shortcut, final String target) {
+        super(shell, name, shortcut)
+        
+        assert target
+
+        this.targetName = target
+    }
+    
+    Command getTarget() {
+        def command = registry[targetName]
+        
+        assert command != null
+        
+        return command
+    }
+    
+    protected List createCompletors() {
+        return target.createCompletors()
+    }
+    
+    String getDescription() {
+        return messages.format('info.alias_to', targetName)
+    }
+
+    String getUsage() {
+        return target.usage
+    }
+    
+    String getHelp() {
+        return target.help
+    }
+    
+    boolean getHidden() {
+        return target.hidden
+    }
+    
+    Object execute(final List args) {
+        target.execute(args)
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/CommandAlias.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/CommandAlias.properties
new file mode 100644
index 0000000..3a83883
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/CommandAlias.properties
@@ -0,0 +1,22 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+info.alias_to=Alias to: @|bold {0}|
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/CommandRegistry.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/CommandRegistry.groovy
new file mode 100644
index 0000000..fc8134c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/CommandRegistry.groovy
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+import org.codehaus.groovy.tools.shell.util.Logger
+
+/**
+ * A registry of shell {@link Command} instances which may be executed.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class CommandRegistry
+{
+    protected final Logger log = Logger.create(CommandRegistry.class)
+    
+    //
+    // TODO: Hook up support so one can for (command in registry) { }
+    //
+    
+    /** A list of all of the registered commands. */
+    final List commands = []
+
+    /** A set of all of the command names and shortcuts to ensure they are unique. */
+    private final Set names = new TreeSet()
+    
+    Command register(final Command command) {
+        assert command
+
+        // Make sure that the command name and shortcut are unique
+        assert !names.contains(command.name) : "Duplicate comamnd name: $command.name"
+        names << command.name
+        
+        assert !names.contains(command.shortcut) : "Duplicate command shortcut: $command.shortcut"
+        names << command.shortcut
+
+        // Hold on to the command in order
+        commands << command
+        
+        // Hookup context for alias commands
+        command.registry = this
+
+        // Add any standard aliases for the command if any
+        command.aliases?.each { this << it }
+        
+        if (log.debugEnabled) {
+            log.debug("Registered command: $command.name")
+        }
+        
+        return command
+    }
+
+    def leftShift(final Command command) {
+        return register(command)
+    }
+    
+    Command find(final String name) {
+        assert name
+        
+        for (c in commands) {
+            if (name in [ c.name, c.shortcut ]) {
+                return c
+            }
+        }
+        
+        return null
+    }
+    
+    void remove(final Command command) {
+        assert command
+        
+        commands.remove(command)
+        
+        names.remove(command.name)
+        names.remove(command.shortcut)
+        
+        if (log.debugEnabled) {
+            log.debug("Removed command: $command.name")
+        }
+    }
+    
+    List commands() {
+        return commands
+    }
+    
+    def getProperty(final String name) {
+        return find(name)
+    }
+    
+    Iterator iterator() {
+        return commands().iterator()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/CommandSupport.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/CommandSupport.groovy
new file mode 100644
index 0000000..47b5892
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/CommandSupport.groovy
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+import jline.Completor
+import jline.NullCompletor
+import jline.ArgumentCompletor
+import jline.History
+
+import org.codehaus.groovy.tools.shell.util.MessageSource
+import org.codehaus.groovy.tools.shell.util.Logger
+import org.codehaus.groovy.tools.shell.util.SimpleCompletor
+
+/**
+ * Support for {@link Command} instances.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+abstract class CommandSupport
+    implements Command
+{
+    protected static final String NEWLINE = System.properties['line.separator']
+    
+    /** Instance logger for the command, initialized late to include the command name. */
+    protected final Logger log
+
+    /** i18n message source for the command. */
+    protected final MessageSource messages = new MessageSource(this.class, CommandSupport.class)
+
+    /** The name of the command. */
+    final String name
+
+    /** The shortcut switch */
+    final String shortcut
+    
+    /** The owning shell. */
+    protected final Shell shell
+
+    /** The I/O container for the command to spit stuff out. */
+    protected final IO io
+
+    /** Provides the command instance with the registry, for aliasing support. */
+    protected CommandRegistry registry
+    
+    /** Standard aliases for the command. */
+    final List/*<CommandAlias>*/ aliases = []
+    
+    /** Flag to indicate if the command should be hidden or not. */
+    boolean hidden = false
+    
+    protected CommandSupport(final Shell shell, final String name, final String shortcut) {
+        assert shell
+        assert name
+        assert shortcut
+        
+        this.log = Logger.create(this.class, name)
+        this.shell = shell
+        this.io = shell.io
+        this.name = name
+        this.shortcut = shortcut
+        
+        //
+        // NOTE: Registry will be added once registered.
+        //
+    }
+
+    String getDescription() {
+        return messages['command.description']
+    }
+
+    String getUsage() {
+        return messages['command.usage']
+    }
+
+    String getHelp() {
+        return messages['command.help']
+    }
+
+    /**
+     * Override to provide custom completion semantics for the command.
+     */
+    protected List createCompletors() {
+        return null
+    }
+
+    /**
+     * Setup the completor for the command.
+     */
+    Completor getCompletor() {
+        if (hidden) {
+            return null
+        }
+        
+        def list = [ new SimpleCompletor(name, shortcut) ]
+
+        def completors = createCompletors()
+        
+        if (completors) {
+            completors.each {
+                if (it) {
+                    list << it
+                }
+                else {
+                    list << new NullCompletor()
+                }
+            }
+        }
+        else {
+            list << new NullCompletor()
+        }
+
+        return new ArgumentCompletor(list)
+    }
+    
+    //
+    // Helpers
+    //
+
+    protected void alias(final String name, final String shortcut) {
+        aliases << new CommandAlias(shell, name, shortcut, this.name)
+    }
+
+    protected void fail(final String msg) {
+        throw new CommandException(this, msg)
+    }
+    
+    protected void fail(final String msg, final Throwable cause) {
+        throw new CommandException(this, msg, cause)
+    }
+    
+    protected void assertNoArguments(final List args) {
+        assert args != null
+        
+        if (args.size() > 0) {
+            fail(messages.format('error.unexpected_args', args.join(' ')))
+        }
+    }
+    
+    //
+    // Shell access helpers
+    //
+
+    protected BufferManager getBuffers() {
+        return shell.buffers
+    }
+
+    protected List getBuffer() {
+        return shell.buffers.current()
+    }
+
+    protected List getImports() {
+        return shell.imports
+    }
+    
+    protected Binding getBinding() {
+        return shell.interp.context
+    }
+    
+    protected Map getVariables() {
+        return binding.variables
+    }
+    
+    protected History getHistory() {
+        return shell.history
+    }
+    
+    protected GroovyClassLoader getClassLoader() {
+        return shell.interp.classLoader
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/CommandSupport.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/CommandSupport.properties
new file mode 100644
index 0000000..e4e6441
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/CommandSupport.properties
@@ -0,0 +1,22 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+error.unexpected_args=Unexpected arguments: {0}
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/ComplexCommandSupport.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/ComplexCommandSupport.groovy
new file mode 100644
index 0000000..a591d43
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/ComplexCommandSupport.groovy
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+import org.codehaus.groovy.tools.shell.util.SimpleCompletor
+
+/**
+ * Support for more complex commands.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+abstract class ComplexCommandSupport
+    extends CommandSupport
+{
+    protected List/*<String>*/ functions
+    
+    protected String defaultFunction
+    
+    ComplexCommandSupport(final Shell shell, final String name, final String shortcut) {
+        super(shell, name, shortcut)
+    }
+    
+    protected List createCompletors() {
+        def c = new SimpleCompletor()
+        
+        functions.each { c.add(it) }
+        
+        return [ c, null ]
+    }
+    
+    Object execute(List args) {
+        assert args != null
+        
+        if (args.size() == 0) {
+            if (defaultFunction) {
+                args = [ defaultFunction ]
+            }
+            else {
+                fail("Command '$name' requires at least one argument")
+            }
+        }
+        
+        return executeFunction(args)
+    }
+    
+    protected executeFunction(List args) {
+        assert args != null
+        
+        assert functions
+        
+        def fname = args[0]
+        
+        if (args.size() > 1) {
+            args = args[1..-1]
+        }
+        else {
+            args = []
+        }
+        
+        if (fname in functions) {
+            def func = loadFunction(fname)
+            
+            log.debug("Invoking function '$fname' w/args: $args")
+            
+            return func.call(args)
+        }
+        else {
+            fail("Unknown function name: $fname")
+        }
+    }
+    
+    protected Closure loadFunction(final String name) {
+        assert name
+        
+        try {
+            return this."do_${name}"
+        }
+        catch (MissingPropertyException e) {
+            fail("Failed to load delgate function: $e")
+        }
+    }
+    
+    def do_all = {
+        functions.each { fname ->
+            if (fname != 'all') {
+                executeFunction([ fname ])
+            }
+        }
+    }
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/ExitNotification.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/ExitNotification.groovy
new file mode 100644
index 0000000..4ecb3d2
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/ExitNotification.groovy
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+/**
+ * Notification to signal the shell to exit.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ExitNotification
+    extends Error
+{
+    /** The exit code. */
+    final int code
+
+    ExitNotification(final int code) {
+        this.code = code
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/Groovysh.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/Groovysh.groovy
new file mode 100644
index 0000000..0026235
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/Groovysh.groovy
@@ -0,0 +1,600 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+import java.lang.reflect.Method
+
+import jline.Terminal
+import jline.History
+
+import org.codehaus.groovy.runtime.InvokerHelper
+import org.codehaus.groovy.runtime.MethodClosure
+
+import org.codehaus.groovy.control.SourceUnit
+import org.codehaus.groovy.control.CompilationFailedException
+
+import org.codehaus.groovy.tools.shell.util.MessageSource
+import org.codehaus.groovy.tools.shell.util.ANSI.Renderer as AnsiRenderer
+import org.codehaus.groovy.tools.shell.util.XmlCommandRegistrar
+import org.codehaus.groovy.runtime.StackTraceUtils
+import org.codehaus.groovy.tools.shell.util.Preferences
+
+/**
+ * An interactive shell for evaluating Groovy code from the command-line (aka. groovysh).
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class Groovysh
+    extends Shell
+{
+    private static final String NEWLINE = System.properties['line.separator']
+    
+    private static final MessageSource messages = new MessageSource(Groovysh.class)
+
+    private final BufferManager buffers = new BufferManager()
+
+    private final GroovyShell interp
+    
+    private final List imports = []
+    
+    private InteractiveShellRunner runner
+    
+    private History history
+    
+    Groovysh(final ClassLoader classLoader, final Binding binding, final IO io) {
+        super(io)
+        
+        assert classLoader
+        assert binding
+        
+        interp = new GroovyShell(classLoader, binding)
+
+        //
+        // TODO: Change this to be more embed/test friendly
+        //
+        
+        def registrar = new XmlCommandRegistrar(this, classLoader)
+        registrar.register(getClass().getResource('commands.xml'))
+    }
+
+    Groovysh(final Binding binding, final IO io) {
+        this(Thread.currentThread().contextClassLoader, binding, io)
+    }
+
+    Groovysh(final IO io) {
+        this(new Binding(), io)
+    }
+    
+    Groovysh() {
+        this(new IO())
+    }
+
+    //
+    // Execution
+    //
+
+    /**
+     * Execute a single line, where the line may be a command or Groovy code (complete or incomplete).
+     */
+    Object execute(final String line) {
+        assert line != null
+        
+        // Ignore empty lines
+        if (line.trim().size() == 0) {
+            return null
+        }
+
+        maybeRecordInput(line)
+
+        def result
+        
+        // First try normal command execution
+        if (isExecutable(line)) {
+            result = executeCommand(line)
+            
+            // For commands, only set the last result when its non-null/true
+            if (result) {
+                lastResult = result
+            }
+            
+            return result
+        }
+        
+        // Otherwise treat the line as Groovy
+        def current = []
+        current += buffers.current()
+
+        // Append the line to the current buffer
+        current << line
+
+        // Attempt to parse the current buffer
+        def status = parse(current, 1)
+
+        switch (status.code) {
+            case ParseCode.COMPLETE:
+                // Evaluate the current buffer
+                lastResult = result = evaluate(current)
+                buffers.clearSelected()
+                break
+
+            case ParseCode.INCOMPLETE:
+                // Save the current buffer so user can build up complex muli-line code blocks
+                buffers.updateSelected(current)
+                break
+
+            case ParseCode.ERROR:
+                throw status.cause
+
+            default:
+                // Should never happen
+                throw new Error("Invalid parse status: $status.code")
+        }
+        
+        return result
+    }
+
+    protected Object executeCommand(final String line) {
+        return super.execute(line)
+    }
+
+    /**
+     * Attempt to parse the given buffer.
+     */
+    private ParseStatus parse(final List buffer, final int tolerance) {
+        assert buffer
+
+        String source = (imports + buffer).join(NEWLINE)
+
+        log.debug("Parsing: $source")
+
+        SourceUnit parser
+        Throwable error
+
+        try {
+            parser = SourceUnit.create('groovysh_parse', source, tolerance)
+            parser.parse()
+
+            log.debug('Parse complete')
+
+            return new ParseStatus(ParseCode.COMPLETE)
+        }
+        catch (CompilationFailedException e) {
+            //
+            // FIXME: Seems like failedWithUnexpectedEOF() is not always set as expected, as in:
+            //
+            // class a {               <--- is true here
+            //    def b() {            <--- is false here :-(
+            //
+            
+            if (parser.errorCollector.errorCount > 1 || !parser.failedWithUnexpectedEOF()) {
+                //
+                // HACK: Super insane hack... if we detect a syntax error, but the last line of the
+                //       buffer ends with a '{' or '[' then ignore... and pretend its okay, cause it might be...
+                //
+                //       This seems to get around the problem with things like:
+                //
+                //       class a { def b() {
+                //
+
+                if (buffer[-1].trim().endsWith('{')) {
+                    // ignore, this blows
+                }
+                else if (buffer[-1].trim().endsWith('[')) {
+                    // ignore, this blows
+                }
+                else {
+                    error = e
+                }
+            }
+        }
+        catch (Throwable e) {
+            error = e
+        }
+
+        if (error) {
+            log.debug("Parse error: $error")
+
+            return new ParseStatus(error)
+        }
+        else {
+            log.debug('Parse incomplete')
+
+            return new ParseStatus(ParseCode.INCOMPLETE)
+        }
+    }
+
+    private static final String EVAL_SCRIPT_FILENAME = 'groovysh_evaluate'
+
+    /**
+     * Evaluate the given buffer.  The buffer is assumed to be complete.
+     */
+    private Object evaluate(final List buffer) {
+        assert buffer
+        
+        log.debug("Evaluating buffer...")
+
+        if (io.verbose) {
+            displayBuffer(buffer)
+        }
+
+        //
+        // HACK: Fix for GROOVY-2213.  Insert a runnable statement (ie. 'true') after imports so that we can
+        //       always run the buffer and get any class/enum/whatever defs defined.
+        //
+
+        def source = (imports + [ 'true' ] + buffer).join(NEWLINE)
+        def result
+
+        Class type
+        try {
+            Script script = interp.parse(source, EVAL_SCRIPT_FILENAME)
+            type = script.getClass()
+
+            log.debug("Compiled script: $script")
+
+            if (type.declaredMethods.any { it.name == 'main' }) {
+                result = script.run()
+            }
+
+            // Need to use String.valueOf() here to avoid icky exceptions causes by GString coercion
+            log.debug("Evaluation result: ${String.valueOf(result)} (${result?.getClass()})")
+
+            // Keep only the methods that have been defined in the script
+            type.declaredMethods.each { Method m ->
+                if (!(m.name in [ 'main', 'run' ] || m.name.startsWith('super$') || m.name.startsWith('class$'))) {
+                    log.debug("Saving method definition: $m")
+                    interp.context["${m.name}"] = new MethodClosure(type.newInstance(), m.name)
+                }
+            }
+        }
+        finally {
+            def cache = interp.classLoader.classCache
+            
+            // Remove the script class generated
+            cache.remove(type?.name)
+
+            // Remove the inline closures from the cache as well
+            cache.remove('$_run_closure')
+        }
+        
+        return result
+    }
+
+    /**
+     * Display the given buffer.
+     */
+    private void displayBuffer(final List buffer) {
+        assert buffer
+
+        buffer.eachWithIndex { line, index ->
+            def lineNum = formatLineNumber(index + 1)
+            
+            io.out.println(" ${lineNum}@|bold >| $line")
+        }
+    }
+
+    //
+    // Prompt
+    //
+
+    private AnsiRenderer prompt = new AnsiRenderer()
+
+    private String renderPrompt() {
+        def lineNum = formatLineNumber(buffers.current().size())
+
+        return prompt.render("@|bold groovy:|${lineNum}@|bold >| ")
+    }
+
+    /**
+     * Format the given number suitable for rendering as a line number column.
+     */
+    private String formatLineNumber(final int num) {
+        assert num >= 0
+
+        // Make a %03d-like string for the line number
+        return num.toString().padLeft(3, '0')
+    }
+
+    //
+    // User Profile Scripts
+    //
+
+    File getUserStateDirectory() {
+        def userHome = new File(System.getProperty('user.home'))
+        def dir = new File(userHome, '.groovy')
+        return dir.canonicalFile
+    }
+
+    private void loadUserScript(final String filename) {
+        assert filename
+        
+        def file = new File(userStateDirectory, filename)
+        
+        if (file.exists()) {
+            def command = registry['load']
+
+            if (command) {
+                log.debug("Loading user-script: $file")
+
+                // Disable showLastResult for profile scripts
+                boolean tmp = Preferences.showLastResult
+                Preferences.showLastResult = false
+
+                try {
+                    command.load(file.toURI().toURL())
+                }
+                finally {
+                    Preferences.showLastResult = tmp
+                }
+            }
+            else {
+                log.error("Unable to load user-script, missing 'load' command")
+            }
+        }
+    }
+
+    //
+    // Recording
+    //
+
+    private void maybeRecordInput(final String line) {
+        def record = registry['record']
+
+        if (record != null) {
+            record.recordInput(line)
+        }
+    }
+
+    private void maybeRecordResult(final Object result) {
+        def record = registry['record']
+
+        if (record != null) {
+            record.recordResult(result)
+        }
+    }
+
+    private void maybeRecordError(Throwable cause) {
+        def record = registry['record']
+
+        if (record != null) {
+            boolean sanitize = Preferences.sanitizeStackTrace
+
+            if (sanitize) {
+                cause = StackTraceUtils.deepSanitize(cause);
+            }
+
+            record.recordError(cause)
+        }
+    }
+    
+    //
+    // Hooks
+    //
+
+    final Closure defaultResultHook = { result ->
+        boolean showLastResult = !io.quiet && (io.verbose || Preferences.showLastResult)
+
+        if (showLastResult) {
+            // Need to use String.valueOf() here to avoid icky exceptions causes by GString coercion
+            io.out.println("@|bold ===>| ${String.valueOf(result)}")
+        }
+    }
+
+    Closure resultHook = defaultResultHook
+
+    private void setLastResult(final Object result) {
+        if (resultHook == null) {
+            throw new IllegalStateException("Result hook is not set")
+        }
+
+        resultHook.call(result)
+
+        interp.context['_'] = result
+
+        maybeRecordResult(result)
+    }
+
+    private Object getLastResult() {
+        return interp.context['_']
+    }
+
+    final Closure defaultErrorHook = { Throwable cause ->
+        assert cause != null
+
+        io.err.println("@|bold,red ERROR| ${cause.class.name}: @|bold,red ${cause.message}|")
+
+        maybeRecordError(cause)
+
+        if (log.debug) {
+            // If we have debug enabled then skip the fancy bits below
+            log.debug(cause)
+        }
+        else {
+            boolean sanitize = Preferences.sanitizeStackTrace
+
+            // Sanitize the stack trace unless we are inverbose mode, or the user has request otherwise
+            if (!io.verbose && sanitize) {
+                cause = StackTraceUtils.deepSanitize(cause);
+            }
+
+            def trace = cause.stackTrace
+
+            def buff = new StringBuffer()
+
+            for (e in trace) {
+                buff << "        @|bold at| ${e.className}.${e.methodName} (@|bold "
+
+                buff << (e.nativeMethod ? 'Native Method' :
+                            (e.fileName != null && e.lineNumber != -1 ? "${e.fileName}:${e.lineNumber}" :
+                                (e.fileName != null ? e.fileName : 'Unknown Source')))
+
+                buff << '|)'
+
+                io.err.println(buff)
+
+                buff.setLength(0) // Reset the buffer
+
+                // Stop the trace once we find the root of the evaluated script
+                if (e.className == EVAL_SCRIPT_FILENAME && e.methodName == 'run') {
+                    io.err.println('        @|bold ...|')
+                    break
+                }
+            }
+        }
+    }
+
+    Closure errorHook = defaultErrorHook
+
+    private void displayError(final Throwable cause) {
+        if (errorHook == null) {
+            throw new IllegalStateException("Error hook is not set")
+        }
+
+        errorHook.call(cause)
+    }
+
+    //
+    // Interactive Shell
+    //
+
+    int run(final String[] args) {
+        String commandLine = null
+
+        if (args != null && args.length > 0) {
+            commandLine = args.join(' ')
+        }
+
+        return run(commandLine as String)
+    }
+
+    int run(final String commandLine) {
+        def term = Terminal.terminal
+
+        if (log.debug) {
+            log.debug("Terminal ($term)")
+            log.debug("    Supported:  $term.supported")
+            log.debug("    ECHO:       $term.echo (enabled: $term.echoEnabled)")
+            log.debug("    H x W:      $term.terminalHeight x $term.terminalWidth")
+            log.debug("    ANSI:       ${term.isANSISupported()}")
+
+            if (term instanceof jline.WindowsTerminal) {
+                log.debug("    Direct:     ${term.directConsole}")
+            }
+        }
+
+        def code
+
+        try {
+            loadUserScript('groovysh.profile')
+
+            if (commandLine != null) {
+                // Run the given commands
+                execute(commandLine)
+            }
+            else {
+                loadUserScript('groovysh.rc')
+
+                // Setup the interactive runner
+                runner = new InteractiveShellRunner(this, this.&renderPrompt as Closure)
+
+                // Setup the history
+                runner.history = history = new History()
+                runner.historyFile = new File(userStateDirectory, 'groovysh.history')
+
+                // Setup the error handler
+                runner.errorHandler = this.&displayError
+
+                //
+                // TODO: See if we want to add any more language specific completions, like for println for example?
+                //
+
+                // Display the welcome banner
+                if (!io.quiet) {
+                    def width = term.terminalWidth
+
+                    // If we can't tell, or have something bogus then use a reasonable default
+                    if (width < 1) {
+                        width = 80
+                    }
+
+                    io.out.println(messages.format('startup_banner.0', InvokerHelper.version, System.properties['java.vm.version']))
+                    io.out.println(messages['startup_banner.1'])
+                    io.out.println('-' * (width - 1))
+                }
+
+                // And let 'er rip... :-)
+                runner.run()
+            }
+
+            code = 0
+        }
+        catch (ExitNotification n) {
+            log.debug("Exiting w/code: ${n.code}")
+
+            code = n.code
+        }
+        catch (Throwable t) {
+            io.err.println(messages.format('info.fatal', t))
+            t.printStackTrace(io.err)
+
+            code = 1
+        }
+
+        assert code != null // This should never happen
+
+        return code
+    }
+}
+
+/**
+ * Container for the parse code.
+ */
+class ParseCode {
+    static final ParseCode COMPLETE = new ParseCode(code: 0)
+    static final ParseCode INCOMPLETE = new ParseCode(code: 1)
+    static final ParseCode ERROR = new ParseCode(code: 2)
+
+    int code
+
+    String toString() {
+        return code
+    }
+}
+
+/**
+ * Container for parse status details.
+ */
+class ParseStatus
+{
+    final ParseCode code
+
+    final Throwable cause
+
+    ParseStatus(final ParseCode code, final Throwable cause) {
+        this.code = code
+        this.cause = cause
+    }
+
+    ParseStatus(final ParseCode code) {
+        this(code, null)
+    }
+
+    ParseStatus(final Throwable cause) {
+        this(ParseCode.ERROR, cause)
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/Groovysh.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/Groovysh.properties
new file mode 100644
index 0000000..5f18fb6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/Groovysh.properties
@@ -0,0 +1,35 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+#
+# Informational and error messages
+#
+
+info.error=ERROR: {0}
+
+info.fatal=FATAL: {0}
+
+#
+# Misc messages
+#
+
+startup_banner.0=@|green Groovy Shell| ({0}, JVM: {1})
+
+startup_banner.1=Type '@|bold help|' or '@|bold \\h|' for help.
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/IO.java b/groovy/src/main/org/codehaus/groovy/tools/shell/IO.java
new file mode 100644
index 0000000..60c4305
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/IO.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+
+import org.codehaus.groovy.tools.shell.util.ANSI.RenderWriter;
+import org.codehaus.groovy.tools.shell.util.Preferences;
+
+/**
+ * Container for input/output handles.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class IO
+{
+    /** Raw input stream. */
+    public final InputStream inputStream;
+
+    /** Raw output stream. */
+    public final OutputStream outputStream;
+
+    /** Raw error output stream. */
+    public final OutputStream errorStream;
+
+    /** Prefered input reader. */
+    public final Reader in;
+
+    /** Prefered output writer. */
+    public final PrintWriter out;
+
+    /** Prefered error output writer. */
+    public final PrintWriter err;
+
+    /**
+     * Construct a new IO container.
+     */
+    public IO(final InputStream inputStream, final OutputStream outputStream, final OutputStream errorStream) {
+        assert inputStream != null;
+        assert outputStream != null;
+        assert errorStream != null;
+
+        this.inputStream = inputStream;
+        this.outputStream = outputStream;
+        this.errorStream = errorStream;
+
+        this.in = new InputStreamReader(inputStream);
+
+        //
+        // TODO: Once all user output is in i18n, then it would be more efficent to have the MessageSource
+        //       be ANSI-aware instead of this...
+        //
+
+        this.out = new RenderWriter(outputStream, true);
+        this.err = new RenderWriter(errorStream, true);
+    }
+
+    /**
+     * Construct a new IO container using system streams.
+     */
+    public IO() {
+        this(System.in, System.out, System.err);
+    }
+
+    /**
+     * Set the verbosity level.
+     *
+     * @param verbosity
+     */
+    public void setVerbosity(final Verbosity verbosity) {
+        assert verbosity != null;
+
+        Preferences.verbosity = verbosity;
+    }
+
+    /**
+     * Returns the verbosity level.
+     */
+    public Verbosity getVerbosity() {
+        return Preferences.verbosity;
+    }
+
+    /**
+     * Check if the verbosity level is set to {@link Verbosity#QUIET}.
+     */
+    public boolean isQuiet() {
+        return getVerbosity() == Verbosity.QUIET;
+    }
+
+    /**
+     * Check if the verbosity level is set to {@link Verbosity#INFO}.
+     */
+    public boolean isInfo() {
+        return getVerbosity() == Verbosity.INFO;
+    }
+
+    /**
+     * Check if the verbosity level is set to {@link Verbosity#VERBOSE}.
+     */
+    public boolean isVerbose() {
+        return getVerbosity() == Verbosity.VERBOSE;
+    }
+
+    /**
+     * Check if the verbosity level is set to {@link Verbosity#DEBUG}.
+     *
+     * <p>For generaly usage, when debug output is required, it is better
+     * to use the logging facility instead.
+     */
+    public boolean isDebug() {
+        return getVerbosity() == Verbosity.DEBUG;
+    }
+
+    /**
+     * Flush both output streams.
+     */
+    public void flush() throws IOException {
+        out.flush();
+        err.flush();
+    }
+
+    /**
+     * Close all streams.
+     */
+    public void close() throws IOException {
+        in.close();
+        out.close();
+        err.close();
+    }
+
+    //
+    // Verbosity
+    //
+
+    public static final class Verbosity
+    {
+        public static final Verbosity QUIET = new Verbosity("QUIET");
+
+        public static final Verbosity INFO = new Verbosity("INFO");
+
+        public static final Verbosity VERBOSE = new Verbosity("VERBOSE");
+
+        public static final Verbosity DEBUG = new Verbosity("DEBUG");
+
+        public final String name;
+
+        private Verbosity(final String name) {
+            this.name = name;
+        }
+
+        public String toString() {
+            return name;
+        }
+
+        public static Verbosity forName(final String name) {
+            assert name != null;
+
+            if (QUIET.name.equalsIgnoreCase(name)) {
+                return QUIET;
+            }
+            if (INFO.name.equalsIgnoreCase(name)) {
+                return INFO;
+            }
+            if (VERBOSE.name.equalsIgnoreCase(name)) {
+                return VERBOSE;
+            }
+            if (DEBUG.name.equalsIgnoreCase(name)) {
+                return DEBUG;
+            }
+
+            throw new IllegalArgumentException("Invalid verbosity name: " + name);
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/InteractiveShellRunner.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/InteractiveShellRunner.groovy
new file mode 100644
index 0000000..f5f6481
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/InteractiveShellRunner.groovy
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+import jline.ConsoleReader
+import jline.MultiCompletor
+import jline.History
+import jline.Completor
+import jline.MultiCompletor
+
+import org.codehaus.groovy.tools.shell.util.Logger
+
+/**
+ * Support for running a {@link Shell} interactivly using the JLine library.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class InteractiveShellRunner
+    extends ShellRunner
+    implements Runnable
+{
+    final ConsoleReader reader
+    
+    final Closure prompt
+    
+    final CommandsMultiCompletor completor
+    
+    InteractiveShellRunner(final Shell shell, final Closure prompt) {
+        super(shell)
+        
+        this.prompt = prompt
+        
+        this.reader = new ConsoleReader(shell.io.inputStream, new PrintWriter(shell.io.outputStream, true))
+        
+        this.completor = new CommandsMultiCompletor()
+        
+        reader.addCompletor(completor)
+    }
+    
+    void run() {
+        for (command in shell.registry) {
+            completor << command
+        }
+
+        // Force things to become clean
+        completor.refresh()
+
+        // And then actually run
+        super.run()
+    }
+    
+    void setHistory(final History history) {
+        reader.history = history
+    }
+    
+    void setHistoryFile(final File file) {
+        def dir = file.parentFile
+        
+        if (!dir.exists()) {
+            dir.mkdirs()
+            
+            log.debug("Created base directory for history file: $dir")
+        }
+        
+        log.debug("Using history file: $file")
+        
+        reader.history.historyFile = file
+    }
+    
+    protected String readLine() {
+        try {
+            return reader.readLine(prompt.call())
+        }
+        catch (StringIndexOutOfBoundsException e) {
+            log.debug("HACK: Try and work around GROOVY-2152 for now", e)
+
+            return "";
+        }
+    }
+}
+
+/**
+ * Completor for interactive shells.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class CommandsMultiCompletor
+    extends MultiCompletor
+{
+    protected final Logger log = Logger.create(this.class)
+    
+    List/*<Completor>*/ list = []
+    
+    private boolean dirty = false
+    
+    def leftShift(final Command command) {
+        assert command
+        
+        //
+        // FIXME: Need to handle completor removal when things like aliases are rebound
+        //
+        
+        def c = command.completor
+        
+        if (c) {
+            list << c
+            
+            log.debug("Added completor[${list.size()}] for command: $command.name")
+            
+            dirty = true
+        }
+    }
+
+    void refresh() {
+        log.debug("Refreshing the completor list")
+
+        completors = list as Completor[]
+        dirty = false
+    }
+
+    int complete(final String buffer, final int pos, final List cand) {
+        assert buffer != null
+        
+        //
+        // FIXME: This is a bit of a hack, I'm too lazy to rewrite a more efficent
+        //        completor impl that is more dynamic than the jline.MultiCompletor version
+        //        so just re-use it and reset the list as needed
+        //
+
+        if (dirty) {
+            refresh()
+        }
+        
+        return super.complete(buffer, pos, cand)
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/Main.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/Main.groovy
new file mode 100644
index 0000000..78479d1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/Main.groovy
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+import org.codehaus.groovy.runtime.InvokerHelper
+import org.codehaus.groovy.tools.shell.util.ANSI
+import org.codehaus.groovy.tools.shell.util.HelpFormatter
+import org.codehaus.groovy.tools.shell.util.Logger
+import org.codehaus.groovy.tools.shell.util.MessageSource
+import org.codehaus.groovy.tools.shell.util.NoExitSecurityManager
+
+/**
+ * Main CLI entry-point for <tt>groovysh</tt>.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class Main
+{
+    private static final MessageSource messages = new MessageSource(Main.class)
+
+    static void main(final String[] args) {
+        IO io = new IO()
+        Logger.io = io
+
+        def cli = new CliBuilder(usage : 'groovysh [options] [...]', formatter: new HelpFormatter(), writer: io.out)
+
+        cli.h(longOpt: 'help', messages['cli.option.help.description'])
+        cli.V(longOpt: 'version', messages['cli.option.version.description'])
+        cli.v(longOpt: 'verbose', messages['cli.option.verbose.description'])
+        cli.q(longOpt: 'quiet', messages['cli.option.quiet.description'])
+        cli.d(longOpt: 'debug', messages['cli.option.debug.description'])
+        cli.C(longOpt: 'color', args: 1, argName: 'FLAG', optionalArg: true, messages['cli.option.color.description'])
+        cli.D(longOpt: 'define', args: 1, argName: 'NAME=VALUE', messages['cli.option.define.description'])
+        cli.T(longOpt: 'terminal', args: 1, argName: 'TYPE', messages['cli.option.terminal.description'])
+
+        def options = cli.parse(args)
+
+        if (options.h) {
+            cli.usage()
+            System.exit(0)
+        }
+
+        if (options.V) {
+            io.out.println(messages.format('cli.info.version', InvokerHelper.version))
+            System.exit(0)
+        }
+
+        if (options.hasOption('T')) {
+            def type = options.getOptionValue('T')
+            setTerminalType(type)
+        }
+
+        if (options.hasOption('D')) {
+            def values = options.getOptionValues('D')
+
+            values.each {
+                setSystemProperty(it as String)
+            }
+        }
+
+        if (options.v) {
+            io.verbosity = IO.Verbosity.VERBOSE
+        }
+
+        if (options.d) {
+            io.verbosity = IO.Verbosity.DEBUG
+        }
+
+        if (options.q) {
+            io.verbosity = IO.Verbosity.QUIET
+        }
+
+        if (options.hasOption('C')) {
+            def value = options.getOptionValue('C')
+            setColor(value)
+        }
+
+        def code
+
+        // Add a hook to display some status when shutting down...
+        addShutdownHook {
+            //
+            // FIXME: We need to configure JLine to catch CTRL-C for us... if that is possible
+            //
+
+            if (code == null) {
+                // Give the user a warning when the JVM shutdown abnormally, normal shutdown
+                // will set an exit code through the proper channels
+
+                io.err.println()
+                io.err.println('@|red WARNING:| Abnormal JVM shutdown detected')
+            }
+
+            io.flush()
+        }
+
+        // Boot up the shell... :-)
+        Groovysh shell = new Groovysh(io)
+
+        SecurityManager psm = System.getSecurityManager()
+        System.setSecurityManager(new NoExitSecurityManager())
+
+        try {
+            code = shell.run(options.arguments() as String[])
+        }
+        finally {
+            System.setSecurityManager(psm)
+        }
+
+        // Force the JVM to exit at this point, since shell could have created threads or
+        // popped up Swing components that will cause the JVM to linger after we have been
+        // asked to shutdown
+
+        System.exit(code)
+    }
+
+    static void setTerminalType(String type) {
+        assert type != null
+        
+        type = type.toLowerCase();
+
+        switch (type) {
+            case 'auto':
+                type = null;
+                break;
+
+            case 'unix':
+                type = 'jline.UnixTerminal'
+                break
+
+            case 'win':
+            case 'windows':
+                type = 'jline.WindowsTerminal'
+                break
+
+            case 'false':
+            case 'off':
+            case 'none':
+                type = 'jline.UnsupportedTerminal'
+                // Disable ANSI, for some reason UnsupportedTerminal reports ANSI as enabled, when it shouldn't
+                ANSI.enabled = false
+                break;
+        }
+
+        if (type != null) {
+            System.setProperty('jline.terminal', type)
+        }
+    }
+
+    static void setColor(value) {
+        if (value == null) {
+            value = true // --color is the same as --color=true
+        }
+        else {
+            value = Boolean.valueOf(value).booleanValue(); // For JDK 1.4 compat
+        }
+
+        ANSI.enabled = value
+    }
+
+    static void setSystemProperty(final String nameValue) {
+        String name
+        String value
+
+        if (nameValue.indexOf('=') > 0) {
+            def tmp = nameValue.split('=', 2)
+            name = tmp[0]
+            value = tmp[1]
+        }
+        else {
+            name = nameValue
+            value = Boolean.TRUE.toString()
+        }
+
+        System.setProperty(name, value)
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/Main.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/Main.properties
new file mode 100644
index 0000000..fc1d588
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/Main.properties
@@ -0,0 +1,41 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+#
+# CLI messages
+#
+
+cli.option.help.description=Display this help message
+
+cli.option.version.description=Display the version
+
+cli.option.verbose.description=Enable verbose output
+
+cli.option.quiet.description=Suppress superfluous output
+
+cli.option.debug.description=Enable debug output
+
+cli.option.color.description=Enable or disable use of ANSI colors
+
+cli.option.define.description=Define a system property
+
+cli.option.terminal.description=Specify the terminal TYPE to use
+
+cli.info.version=@|green Groovy Shell| {0}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/Shell.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/Shell.groovy
new file mode 100644
index 0000000..c545dca
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/Shell.groovy
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+import org.codehaus.groovy.tools.shell.util.Logger
+
+/**
+ * A simple shell for invoking commands from a command-line.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class Shell
+{
+    protected final Logger log = Logger.create(this.class)
+
+    final CommandRegistry registry = new CommandRegistry()
+
+    final IO io
+
+    Shell(final IO io) {
+        assert io
+        
+        this.io = io
+    }
+    
+    Shell() {
+        this(new IO())
+    }
+    
+    protected List parseLine(final String line) {
+        assert line != null
+        
+        return line.trim().tokenize()
+    }
+    
+    Command findCommand(final String line) {
+        assert line
+        
+        //
+        // TODO: Introduce something like 'boolean Command.accepts(String)' to ask
+        //       commands if they can take the line?
+        //
+        //       Would like to get '!66' to invoke the 'history recall' bits, but currently has
+        //       to be '! 66' for it to work with an alias like:
+        //
+        //           alias ! history recall
+        //
+        //       Or maybe allow commands to register specific syntax hacks into the registry?
+        //       then ask the registry for the command for a given line?
+        //
+        
+        def args = parseLine(line)
+        
+        assert args.size() > 0
+        
+        def name = args[0]
+        
+        def command = registry[name]
+        
+        return command
+    }
+    
+    boolean isExecutable(final String line) {
+        return findCommand(line) != null
+    }
+    
+    Object execute(final String line) {
+        assert line
+        
+        def command = findCommand(line)
+        
+        def result = null
+        
+        if (command) {
+            def args = parseLine(line)
+            
+            if (args.size() == 1) {
+                args = []
+            }
+            else {
+                args = args[1..-1]
+            }
+            
+            log.debug("Executing command($command.name): $command; w/args: $args")
+            
+            result = command.execute(args)
+                
+            log.debug("Result: ${String.valueOf(result)}")
+        }
+        
+        return result
+    }
+    
+    Command register(final Command command) {
+        return registry << command
+    }
+    
+    def leftShift(final String line) {
+        return execute(line)
+    }
+    
+    
+    def leftShift(final Command command) {
+        return register(command)
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/ShellRunner.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/ShellRunner.groovy
new file mode 100644
index 0000000..5e17625
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/ShellRunner.groovy
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell
+
+import org.codehaus.groovy.tools.shell.util.Logger
+
+/**
+ * Support for running a {@link Shell}.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+abstract class ShellRunner
+    implements Runnable
+{
+    protected final Logger log = Logger.create(this.class)
+    
+    final Shell shell
+    
+    boolean running = false
+    
+    boolean breakOnNull = true
+    
+    Closure errorHandler = { e ->
+        log.debug(e)
+        
+        running = false
+    }
+    
+    ShellRunner(final Shell shell) {
+        assert shell
+        
+        this.shell = shell
+    }
+    
+    void run() {
+        log.debug('Running')
+        
+        running = true
+        
+        while (running) {
+            try {
+                running = work()
+            }
+            catch (ExitNotification n) {
+                throw n
+            }
+            catch (Throwable t) {
+                log.debug("Work failed: $t")
+                
+                if (errorHandler) {
+                    errorHandler.call(t)
+                }
+            }
+        }
+        
+        log.debug('Finished')
+    }
+    
+    protected boolean work() {
+        def line = readLine()
+        
+        if (log.debugEnabled) {
+            log.debug("Read line: $line")
+        }
+        
+        // Stop on null (maybe)
+        if (line == null && breakOnNull) {
+            return false // stop the loop
+        }
+        
+        // Ingore empty lines
+        if (line.trim().size() > 0) {
+            def result = shell << line
+        }
+        
+        return true
+    }
+    
+    protected abstract String readLine()
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands.xml b/groovy/src/main/org/codehaus/groovy/tools/shell/commands.xml
new file mode 100644
index 0000000..4781fb6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Copyright (C) 2003-2007 the original author or authors.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!-- $Id$ -->
+
+<commands>
+    <command>org.codehaus.groovy.tools.shell.commands.HelpCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.ExitCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.ImportCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.DisplayCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.ClearCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.ShowCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.InspectCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.PurgeCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.EditCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.LoadCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.SaveCommand</command>
+
+    <command>org.codehaus.groovy.tools.shell.commands.RecordCommand</command>
+
+    <command>org.codehaus.groovy.tools.shell.commands.HistoryCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.AliasCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.SetCommand</command>
+    
+    <command>org.codehaus.groovy.tools.shell.commands.ShadowCommand</command>
+</commands>
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/AliasCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/AliasCommand.groovy
new file mode 100644
index 0000000..49c6ba1
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/AliasCommand.groovy
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+
+/**
+ * The 'alias' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class AliasCommand
+    extends CommandSupport
+{
+    AliasCommand(final Shell shell) {
+        super(shell, 'alias', '\\a')
+    }
+
+    Object execute(final List args) {
+        assert args != null
+        
+        if (args.size() < 2) {
+            fail("Command 'alias' requires at least 2 arguments") // TODO: i18n
+        }
+        
+        String name = args[0]
+        List target = args[1..-1]
+        
+        def command = registry[name]
+        
+        if (command) {
+            if (command instanceof AliasTargetProxyCommand) {
+                log.debug("Rebinding alias: $name")
+                
+                registry.remove(command)
+            }
+            else {
+                fail("Can not rebind non-user aliased command: ${command.name}") // TODO: i18n
+            }
+        }
+        
+        log.debug("Creating alias '$name' to: $target")
+        
+        // Register the command
+        command = shell << new AliasTargetProxyCommand(shell, name, target)
+        
+        //
+        // TODO: Should this be here... or should this be in the Shell's impl?
+        //
+        
+        // Try to install the completor
+        if (shell.runner) {
+            shell.runner.completor << command
+        }
+    }
+}
+
+class AliasTargetProxyCommand
+    extends CommandSupport
+{
+    private static int counter = 0
+    
+    final List args
+    
+    AliasTargetProxyCommand(final Shell shell, final String name, final List args) {
+        super(shell, name, '\\a' + counter++)
+        
+        assert args
+        
+        this.args = args
+    }
+    
+    String getDescription() {
+        return "User defined alias to: @|bold ${args.join(' ')}|"
+    }
+
+    String getUsage() {
+        return ''
+    }
+    
+    String getHelp() {
+        return description
+    }
+    
+    Object execute(List args) {
+        args = this.args + args
+        
+        log.debug("Executing with args: $args")
+        
+        //
+        // FIXME: Should go back through shell.execute() to allow aliases to groovy snips too
+        //
+        
+        return shell.executeCommand(args.join(' '))
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/AliasCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/AliasCommand.properties
new file mode 100644
index 0000000..3a6d4fa
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/AliasCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Create an alias
+command.usage=
+command.help=Create an alias.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ClearCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ClearCommand.groovy
new file mode 100644
index 0000000..1fe825f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ClearCommand.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+
+/**
+ * The 'clear' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ClearCommand
+    extends CommandSupport
+{
+    ClearCommand(final Shell shell) {
+        super(shell, 'clear', '\\c')
+    }
+    
+    Object execute(final List args) {
+        assertNoArguments(args)
+        
+        buffer.clear()
+
+        if (io.verbose) {
+            io.out.println('Buffer cleared') //  TODO: i18n
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ClearCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ClearCommand.properties
new file mode 100644
index 0000000..b575e5f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ClearCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Clear the buffer
+command.usage=
+command.help=Clear the current buffer.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/DisplayCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/DisplayCommand.groovy
new file mode 100644
index 0000000..4b785f5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/DisplayCommand.groovy
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+
+/**
+ * The 'display' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class DisplayCommand
+    extends CommandSupport
+{
+    DisplayCommand(final Shell shell) {
+        super(shell, 'display', '\\d')
+    }
+
+    Object execute(final List args) {
+        assertNoArguments(args)
+        
+        if (buffer.isEmpty()) {
+            io.out.println('Buffer is empty') // TODO: i18n
+        }
+        else {
+            shell.displayBuffer(buffer)
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/DisplayCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/DisplayCommand.properties
new file mode 100644
index 0000000..74deedf
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/DisplayCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Display the current buffer
+command.usage=
+command.help=Display the contents of the current buffer.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/EditCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/EditCommand.groovy
new file mode 100644
index 0000000..efa033a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/EditCommand.groovy
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+
+/**
+ * The 'edit' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class EditCommand
+    extends CommandSupport
+{
+    EditCommand(final Shell shell) {
+        super(shell, 'edit', '\\e')
+    }
+    
+    private String getEditorCommand() {
+        def editor = System.getenv('EDITOR')
+        
+        if (!editor) {
+            //
+            // TODO: Maybe popup a Swing editor here?  Or look for other env vars?  Or use notepad on winblows?
+            //
+            
+            fail("Unable to determine which editor to use; check \$EDITOR") // TODO: i18n
+        }
+        
+        return editor
+    }
+    
+    Object execute(final List args) {
+        assertNoArguments(args)
+        
+        def file = File.createTempFile('groovysh-buffer', '.groovy')
+        file.deleteOnExit()
+        
+        try {
+            // Write the current buffer to a tmp file
+            file.write(buffer.join(NEWLINE))
+            
+            // Try to launch the editor
+            def cmd = "$editorCommand $file"
+            log.debug("Executing: $cmd")
+            def p = cmd.execute()
+            
+            // Wait for it to finishe
+            log.debug("Waiting for process: $p")
+            p.waitFor()
+            
+            // Load the new lines...
+            file.eachLine {
+                shell << it
+            }
+        }
+        finally {
+            file.delete()
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/EditCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/EditCommand.properties
new file mode 100644
index 0000000..f1597b7
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/EditCommand.properties
@@ -0,0 +1,23 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Edit the current buffer
+command.usage=
+command.help=Edit the current buffer.
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ExitCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ExitCommand.groovy
new file mode 100644
index 0000000..631e556
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ExitCommand.groovy
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+import org.codehaus.groovy.tools.shell.ExitNotification
+
+/**
+ * The 'exit' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ExitCommand
+    extends CommandSupport
+{
+    ExitCommand(final Shell shell) {
+        super(shell, 'exit', '\\x')
+
+        alias('quit', '\\q')
+    }
+
+    Object execute(final List args) {
+        assertNoArguments(args)
+        
+        //
+        // TODO: Maybe support a single arg for the code?
+        //
+        
+        if (io.verbose) {
+            io.out.println(messages['info.bye'])
+        }
+        
+        throw new ExitNotification(0)
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ExitCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ExitCommand.properties
new file mode 100644
index 0000000..1d9462b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ExitCommand.properties
@@ -0,0 +1,26 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Exit the shell
+command.usage=
+command.help=Exit the shell.
+
+info.bye=Bye!
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HelpCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HelpCommand.groovy
new file mode 100644
index 0000000..a6ccf4f
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HelpCommand.groovy
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Command
+import org.codehaus.groovy.tools.shell.Shell
+import org.codehaus.groovy.tools.shell.CommandRegistry
+import org.codehaus.groovy.tools.shell.util.SimpleCompletor
+
+/**
+ * The 'help' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class HelpCommand
+    extends CommandSupport
+{
+    HelpCommand(final Shell shell) {
+        super(shell, 'help', '\\h')
+
+        alias('?', '\\?')
+    }
+
+    protected List createCompletors() {
+        return [
+            new HelpCommandCompletor(registry),
+            null
+        ]
+    }
+
+    Object execute(final List args) {
+        assert args != null
+
+        if (args.size() > 1) {
+            fail(messages.format('error.unexpected_args', args.join(' ')))
+        }
+        
+        if (args.size() == 1) {
+            help(args[0])
+        }
+        else {
+            list()
+        }
+    }
+
+    private void help(final String name) {
+        assert name
+        
+        Command command = registry[name]
+        if (!command) {
+            fail("No such command: $name") // TODO: i18n
+        }
+        
+        io.out.println()
+        io.out.println("usage: @|bold ${command.name}| $command.usage") // TODO: i18n
+        io.out.println()
+        io.out.println(command.help)
+        io.out.println()
+    }
+
+    private void list() {
+        // Figure out the max command name and shortcut length dynamically
+        int maxName = 0
+        int maxShortcut
+        
+        for (command in registry) {
+            if (command.hidden) {
+                continue
+            }
+            
+            if (command.name.size() > maxName) {
+                maxName = command.name.size()
+            }
+            
+            if (command.shortcut.size() > maxShortcut) {
+                maxShortcut = command.shortcut.size()
+            }
+        }
+        
+        io.out.println()
+        io.out.println('For information about @|green Groovy|, visit:') // TODO: i18n
+        io.out.println('    @|cyan http://groovy.codehaus.org| ') // FIXME: parsing freaks out if end tok is at the last char...
+        io.out.println()
+
+        // List the commands we know about
+        io.out.println('Available commands:') // TODO: i18n
+
+        for (command in registry) {
+            if (command.hidden) {
+                continue
+            }
+            
+            def n = command.name.padRight(maxName, ' ')
+            def s = command.shortcut.padRight(maxShortcut, ' ')
+            
+            //
+            // TODO: Wrap description if needed
+            //
+            
+            def d = command.description
+            
+            io.out.println("  @|bold ${n}|  (@|bold ${s}|) $d")
+        }
+        
+        io.out.println()
+        io.out.println('For help on a specific command type:') // TODO: i18n
+        io.out.println('    help @|bold command| ')
+        io.out.println()
+    }
+}
+
+/**
+ * Completor for the 'help' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class HelpCommandCompletor
+    extends SimpleCompletor
+{
+    private final CommandRegistry registry
+
+    HelpCommandCompletor(final CommandRegistry registry) {
+        assert registry
+
+        this.registry = registry
+    }
+
+    SortedSet getCandidates() {
+        def set = new TreeSet()
+
+        for (command in registry) {
+            if (command.hidden) {
+                continue
+            }
+            
+            set << command.name
+            set << command.shortcut
+        }
+
+        return set
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HelpCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HelpCommand.properties
new file mode 100644
index 0000000..1edcdd8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HelpCommand.properties
@@ -0,0 +1,23 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Display this help message
+command.usage=[<command>]
+command.help=Display the list of commands or the help text for @|BOLD command|.
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HistoryCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HistoryCommand.groovy
new file mode 100644
index 0000000..8c15985
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HistoryCommand.groovy
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.ComplexCommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+
+import org.codehaus.groovy.tools.shell.util.SimpleCompletor
+
+/**
+ * The 'history' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class HistoryCommand
+    extends ComplexCommandSupport
+{
+    HistoryCommand(final Shell shell) {
+        super(shell, 'history', '\\H')
+        
+        this.functions = [ 'show', 'clear', 'flush', 'recall' ]
+        
+        this.defaultFunction = 'show'
+    }
+    
+    protected List createCompletors() {
+        def loader = {
+            def list = []
+            
+            functions.each { list << it }
+            
+            return list
+        }
+        
+        return [
+            new SimpleCompletor(loader),
+            null
+        ]
+    }
+    
+    Object execute(List args) {
+        if (!history) {
+            fail("Shell does not appear to be interactive; Can not query history")
+        }
+        
+        super.execute(args)
+
+        // Don't return anything
+        return null
+    }
+    
+    def do_show = {
+        history.historyList.eachWithIndex { item, i ->
+            i = i.toString().padLeft(3, ' ')
+            
+            io.out.println(" @|bold $i|  $item")
+        }
+    }
+    
+    def do_clear = {
+        history.clear()
+        
+        if (io.verbose) {
+            io.out.println('History cleared')
+        }
+    }
+    
+    def do_flush = {
+        history.flushBuffer()
+        
+        if (io.verbose) {
+            io.out.println('History flushed')
+        }
+    }
+    
+    def do_recall = { args ->
+        def line
+        
+        if (args.size() != 1) {
+            fail("History recall requires a single history identifer")
+        }
+        
+        def id = args[0]
+
+        //
+        // FIXME: This won't work as desired because the history shifts when we run recall and could internally shift more from alias redirection
+        //
+        
+        try {
+            id = Integer.parseInt(id)
+            
+            line = history.historyList[id]
+        }
+        catch (Exception e) {
+            fail("Invalid history identifier: $id", e)
+        }
+        
+        log.debug("Recalling history item #$id: $line")
+        
+        return shell.execute(line)
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HistoryCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HistoryCommand.properties
new file mode 100644
index 0000000..a8fbf8a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/HistoryCommand.properties
@@ -0,0 +1,23 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Display, manage and recall edit-line history
+command.usage=[show|clear|flush|recall <id>]
+command.help=Display, manage and recall edit-line history.
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ImportCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ImportCommand.groovy
new file mode 100644
index 0000000..51522f4
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ImportCommand.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import jline.ArgumentCompletor
+import jline.NullCompletor
+
+import org.codehaus.groovy.control.CompilationFailedException
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+import org.codehaus.groovy.tools.shell.util.SimpleCompletor
+import org.codehaus.groovy.tools.shell.util.ClassNameCompletor
+
+/**
+ * The 'import' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ImportCommand
+    extends CommandSupport
+{
+    ImportCommand(final Shell shell) {
+        super(shell, 'import', '\\i')
+    }
+    
+    protected List createCompletors() {
+        return [
+            new ImportCommandCompletor(shell.interp.classLoader),
+            null
+        ]
+    }
+    
+    Object execute(final List args) {
+        assert args != null
+
+        if (args.isEmpty()) {
+            fail("Command 'import' requires one or more arguments") // TODO: i18n
+        }
+
+        def buff = [ 'import ' + args.join(' ') ]
+        buff << 'def dummp = false'
+        
+        def type
+        try {
+            type = classLoader.parseClass(buff.join(NEWLINE))
+            
+            // No need to keep duplicates, but order may be important so remove the previous def, since
+            // the last defined import will win anyways
+            
+            if (imports.remove(buff[0])) {
+                log.debug("Removed duplicate import from list")
+            }
+            
+            log.debug("Adding import: ${buff[0]}")
+            
+            imports << buff[0]
+        }
+        catch (CompilationFailedException e) {
+            def msg = "Invalid import definition: '${buff[0]}'; reason: $e.message" // TODO: i18n
+            log.debug(msg, e)
+            fail(msg)
+        }
+        finally {
+            // Remove the class generated while testing the import syntax
+            classLoader.classCache.remove(type?.name)
+        }
+    }
+}
+
+/**
+ * Completor for the 'import' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ImportCommandCompletor
+    extends ArgumentCompletor
+{
+    ImportCommandCompletor(final GroovyClassLoader classLoader) {
+        super([
+            new ClassNameCompletor(classLoader),
+            new SimpleCompletor('as'),
+            new NullCompletor()
+        ])
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ImportCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ImportCommand.properties
new file mode 100644
index 0000000..41206c0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ImportCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Import a class into the namespace
+command.usage=[static] <classname> [as <name>]
+command.help=Add a custom import which will be included for all shell evaluations.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/InspectCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/InspectCommand.groovy
new file mode 100644
index 0000000..e18fa50
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/InspectCommand.groovy
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import groovy.inspect.swingui.ObjectBrowser
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+import org.codehaus.groovy.tools.shell.util.SimpleCompletor
+
+/**
+ * The 'inspect' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class InspectCommand
+    extends CommandSupport
+{
+    InspectCommand(final Shell shell) {
+        super(shell, 'inspect', '\\n')
+    }
+    
+    protected List createCompletors() {
+        return [
+            new InspectCommandCompletor(binding),
+            null
+        ]
+    }
+
+    Object execute(final List args) {
+        assert args != null
+        
+        log.debug("Inspecting w/args: $args")
+        
+        if (args.size() > 1) {
+            fail(messages.format('error.unexpected_args', args.join(' ')))
+        }
+        
+        def subject
+        
+        if (args.size() == 1) {
+            subject = binding.variables[args[0]]
+        }
+        else {
+            subject = binding.variables['_']
+        }
+
+        if (!subject) {
+            io.out.println('Subject is null; nothing to inspect') // TODO: i18n
+        }
+        else {
+            if (io.verbose) {
+                io.out.println("Launching object browser to inspect: $subject") // TODO: i18n
+            }
+            
+            ObjectBrowser.inspect(subject)
+        }
+    }
+}
+
+/**
+ * Completor for the 'inspect' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class InspectCommandCompletor
+    extends SimpleCompletor
+{
+    private final Binding binding
+    
+    InspectCommandCompletor(final Binding binding) {
+        assert binding
+
+        this.binding = binding
+    }
+
+    SortedSet getCandidates() {
+        def set = new TreeSet()
+
+        binding.variables.keySet().each {
+            set << it
+        }
+
+        return set
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/InspectCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/InspectCommand.properties
new file mode 100644
index 0000000..5158e84
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/InspectCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Inspect a variable or the last result with the GUI object browser
+command.usage=[<variable>]
+command.help=Opens the GUI object browser to inspect a variable or the result of the last evaluation.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/LoadCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/LoadCommand.groovy
new file mode 100644
index 0000000..bc93cbb
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/LoadCommand.groovy
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import jline.FileNameCompletor
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+
+/**
+ * The 'load' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class LoadCommand
+    extends CommandSupport
+{
+    LoadCommand(final Shell shell) {
+        super(shell, 'load', '\\l')
+
+        alias('.', '\\.')
+    }
+
+    protected List createCompletors() {
+        return [ new FileNameCompletor() ]
+    }
+
+    Object execute(final List args) {
+        assert args != null
+        
+        if (args.size() == 0) {
+            fail("Command 'load' requires at least one argument") // TODO: i18n
+        }
+
+        for (source in args) {
+            URL url
+            
+            log.debug("Attempting to load: $url")
+            
+            try {
+                url = new URL("$source")
+            }
+            catch (MalformedURLException e) {
+                def file = new File("$source")
+                
+                if (!file.exists()) {
+                    fail("File not found: $file") // TODO: i18n
+                }
+                
+                url = file.toURI().toURL()
+            }
+
+            load(url)
+        }
+    }
+
+    void load(final URL url) {
+        assert url != null
+
+        if (io.verbose) {
+            io.out.println("Loading: $url")
+        }
+
+        url.eachLine {
+            shell << it
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/LoadCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/LoadCommand.properties
new file mode 100644
index 0000000..d19a79c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/LoadCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Load a file or URL into the buffer
+command.usage=(<file|url>)+
+command.help=Load one or more files (or urls) into the buffer.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/PurgeCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/PurgeCommand.groovy
new file mode 100644
index 0000000..74de7a5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/PurgeCommand.groovy
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.ComplexCommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+import org.codehaus.groovy.tools.shell.util.Preferences
+
+/**
+ * The 'purge' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class PurgeCommand
+    extends ComplexCommandSupport
+{
+    PurgeCommand(final Shell shell) {
+        super(shell, 'purge', '\\p')
+        
+        this.functions = [ 'variables', 'classes', 'imports', 'preferences', 'all' ]
+    }
+    
+    def do_variables = {
+        if (variables.isEmpty()) {
+            io.out.println('No variables defined') // TODO: i18n
+        }
+        else {
+            variables.clear()
+            
+            if (io.verbose) {
+                io.out.println("Custom variables purged") // TODO: i18n
+            }
+        }
+    }
+    
+    def do_classes = {
+        if (classLoader.loadedClasses.size() == 0) {
+            io.out.println("No classes have been loaded") // TODO: i18n
+        }
+        else {
+            classLoader.clearCache()
+            
+            if (io.verbose) {
+                io.out.println('Loaded classes purged') // TODO: i18n
+            }
+        }
+    }
+    
+    def do_imports = {
+        if (imports.isEmpty()) {
+            io.out.println("No custom imports have been defined") // TODO: i18n
+        }
+        else {
+            imports.clear()
+            
+            if (io.verbose) {
+                io.out.println("Custom imports purged") // TODO: i18n
+            }
+        }
+    }
+
+    def do_preferences = {
+        Preferences.clear()
+        
+        if (io.verbose) {
+            io.out.println("Preferences purged") // TODO: i18n
+        }
+    }
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/PurgeCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/PurgeCommand.properties
new file mode 100644
index 0000000..c892c42
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/PurgeCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Purge variables, classes, imports or preferences
+command.usage=(<variables|classes|imports|preferences|all>)+
+command.help=Purges objects from the shell.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/RecordCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/RecordCommand.groovy
new file mode 100644
index 0000000..8a7bb17
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/RecordCommand.groovy
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.ComplexCommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+
+/**
+ * The 'record' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class RecordCommand
+    extends ComplexCommandSupport
+{
+    RecordCommand(final Shell shell) {
+        super(shell, 'record', '\\r')
+
+        this.functions = [ 'start', 'stop', 'status' ]
+
+        this.defaultFunction = 'status'
+
+        addShutdownHook {
+            if (recording) {
+                do_stop()
+            }
+        }
+    }
+
+    private File file
+
+    private PrintWriter writer
+
+    boolean isRecording() {
+        return file != null
+    }
+
+    def recordInput(final String line) {
+        assert line != null
+
+        if (recording) {
+            writer.println(line)
+            writer.flush()
+        }
+    }
+
+    def recordResult(final Object result) {
+        // result maybe null
+
+        if (recording) {
+            // Useing String.valueOf() to prevent crazy exceptions
+            writer.println("// RESULT: ${String.valueOf(result)}")
+            writer.flush()
+        }
+    }
+
+    def recordError(final Throwable cause) {
+        assert cause != null
+
+        if (recording) {
+            writer.println("// ERROR: $cause")
+
+            cause.stackTrace.each {
+                writer.println("//    $it")
+            }
+
+            writer.flush()
+        }
+    }
+
+    def do_start = { args ->
+        if (recording) {
+            fail("Already recording to: $file")
+        }
+
+        if (args.size() != 1) {
+            file = File.createTempFile('groovysh-', '.txt')
+        }
+        else {
+            file = new File(args[0] as String)
+        }
+
+        file.parentFile.mkdirs()
+
+        writer = file.newPrintWriter()
+        writer.println("// OPENED: " + new Date())
+        writer.flush()
+        
+        io.out.println("Recording session to: $file")
+
+        return file
+    }
+
+    def do_stop = {
+        if (!recording) {
+            fail("Not recording")
+        }
+
+        writer.println("// CLOSED: " + new Date())
+        writer.flush()
+
+        writer.close()
+        writer = null
+
+        io.out.println("Recording stopped; session saved as: $file (${file.length()} bytes)")
+
+        def tmp = file
+        file = null
+
+        return tmp
+    }
+
+    def do_status = {
+        if (!recording) {
+            io.out.println("Not recording")
+
+            return null
+        }
+
+        io.out.println("Recording to file: $file (${file.length()} bytes)")
+
+        return file
+    }
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/RecordCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/RecordCommand.properties
new file mode 100644
index 0000000..b2c91f5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/RecordCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Record the current session to a file
+command.usage=[start [<file>]|stop|status]
+command.help=Record the current session to a file.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SaveCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SaveCommand.groovy
new file mode 100644
index 0000000..7c64cde
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SaveCommand.groovy
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import jline.FileNameCompletor
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+
+/**
+ * The 'save' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class SaveCommand
+    extends CommandSupport
+{
+    SaveCommand(final Shell shell) {
+        super(shell, 'save', '\\s')
+    }
+
+    protected List createCompletors() {
+        return [
+            new FileNameCompletor(),
+            null
+        ]
+    }
+
+    Object execute(final List args) {
+        assert args != null
+        
+        if (args.size() != 1) {
+            fail("Command 'save' requires a single file argument") // TODO: i18n
+        }
+
+        if (buffer.isEmpty()) {
+            io.out.println('Buffer is empty') // TODO: i18n
+            return
+        }
+
+        //
+        // TODO: Support special '-' file to simply dump text to io.out
+        //
+        
+        def file = new File("${args[0]}")
+
+        if (io.verbose) {
+            io.out.println("Saving current buffer to file: $file") // TODO: i18n
+        }
+
+        def dir = file.parentFile
+        if (dir && !dir.exists()) {
+            log.debug("Creating parent directory path: $dir")
+            
+            dir.mkdirs()
+        }
+        
+        file.write(buffer.join(NEWLINE))
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SaveCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SaveCommand.properties
new file mode 100644
index 0000000..c63c3a4
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SaveCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Save the current buffer to a file
+command.usage=<file>
+command.help=Saves the buffer's contents to @|BOLD file|.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SetCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SetCommand.groovy
new file mode 100644
index 0000000..c15902a
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SetCommand.groovy
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+
+import org.codehaus.groovy.tools.shell.util.SimpleCompletor
+import org.codehaus.groovy.tools.shell.util.Preferences
+
+/**
+ * The 'set' command, used to set preferences.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class SetCommand
+    extends CommandSupport
+{
+    SetCommand(final Shell shell) {
+        super(shell, 'set', '\\=')
+    }
+
+    protected List createCompletors() {
+        def loader = {
+            def list = []
+
+            def keys = Preferences.keys()
+
+            keys.each { list << it }
+
+            return list
+        }
+
+        return [
+            new SimpleCompletor(loader),
+            null
+        ]
+    }
+
+    Object execute(final List args) {
+        assert args != null
+        
+        if (args.size() == 0) {
+            def keys = Preferences.keys()
+            
+            if (keys.size() == 0) {
+                io.out.println('No preferences are set')
+                return
+            }
+            else {
+                io.out.println('Preferences:')
+                keys.each {
+                    def value = Preferences.get(it, null)
+                    println("    $it=$value")
+                }
+            }
+            return
+        }
+        
+        if (args.size() > 2) {
+            fail("Command '$name' requires arguments: <name> [<value>]")
+        }
+        
+        def name = args[0]
+        def value
+        
+        if (args.size() == 1) {
+            value = true
+        }
+        else {
+            value = args[1]
+        }
+        
+        log.debug("Setting preference: $name=$value")
+        
+        Preferences.put(name, String.valueOf(value))
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SetCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SetCommand.properties
new file mode 100644
index 0000000..9ab6fae
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/SetCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Set (or list) preferences
+command.usage=[<name> [<value>]]
+command.help=Set or list preferences.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShadowCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShadowCommand.groovy
new file mode 100644
index 0000000..953bacd
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShadowCommand.groovy
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.ComplexCommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+import org.codehaus.groovy.tools.shell.util.Preferences
+
+/**
+ * The 'shadow' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ShadowCommand
+    extends ComplexCommandSupport
+{
+    ShadowCommand(final Shell shell) {
+        super(shell, 'shadow', '\\&')
+        
+        this.hidden = true
+        
+        this.functions = [ 'debug', 'verbose', 'info', 'this' ]
+    }
+    
+    def do_debug = {
+        Preferences.verbosity = IO.Verbosity.DEBUG
+    }
+    
+    def do_verbose = {
+        Preferences.verbosity = IO.Verbosity.VERBOSE
+    }
+
+    def do_info = {
+        Preferences.verbosity = IO.Verbosity.INFO
+    }
+
+    def do_this = {
+        return this
+    }
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShadowCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShadowCommand.properties
new file mode 100644
index 0000000..8e81be5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShadowCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Uber foo, muck & fluff
+command.usage=
+command.help=Uber foo, muck & fluff.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShowCommand.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShowCommand.groovy
new file mode 100644
index 0000000..f28feb6
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShowCommand.groovy
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.runtime.MethodClosure
+
+import org.codehaus.groovy.tools.shell.ComplexCommandSupport
+import org.codehaus.groovy.tools.shell.Shell
+import org.codehaus.groovy.tools.shell.util.Preferences
+
+/**
+ * The 'show' command.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ShowCommand
+    extends ComplexCommandSupport
+{
+    ShowCommand(final Shell shell) {
+        super(shell, 'show', '\\S')
+        
+        this.functions = [ 'variables', 'classes', 'imports', 'preferences', 'all' ]
+    }
+    
+    def do_variables = {
+        if (variables.isEmpty()) {
+            io.out.println('No variables defined') // TODO: i18n
+        }
+        else {
+            io.out.println('Variables:') // TODO: i18n
+            
+            variables.each { key, value ->
+                // Special handling for defined methods, just show the sig
+                if (value instanceof MethodClosure) {
+                    //
+                    // TODO: Would be nice to show the argument types it will accept...
+                    //
+                    value = "method ${value.method}()"
+                }
+
+                // Need to use String.valueOf() here to avoid icky exceptions causes by GString coercion
+                io.out.println("  $key = ${String.valueOf(value)}")
+            }
+        }
+    }
+    
+    def do_classes = {
+        def classes = classLoader.loadedClasses
+        
+        if (classes.size() == 0) {
+            io.out.println("No classes have been loaded") // TODO: i18n
+        }
+        else {
+            io.out.println('Classes:') // TODO: i18n
+            
+            classes.each {
+                io.out.println("  $it")
+            }
+        }
+    }
+    
+    def do_imports = {
+        if (imports.isEmpty()) {
+            io.out.println("No custom imports have been defined") // TODO: i18n
+        }
+        else {
+            io.out.println("Custom imports:") // TODO: i18n
+            
+            imports.each {
+                io.out.println("  $it")
+            }
+        }
+    }
+
+    def do_preferences = {
+        def keys = Preferences.keys()
+
+        if (keys.size() == 0) {
+            io.out.println('No preferences are set')
+            return
+        }
+        else {
+            io.out.println('Preferences:')
+            keys.each {
+                def value = Preferences.get(it, null)
+                println("    $it=$value")
+            }
+        }
+        return
+    }
+}
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShowCommand.properties b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShowCommand.properties
new file mode 100644
index 0000000..ae74998
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/commands/ShowCommand.properties
@@ -0,0 +1,24 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+command.description=Show variables, classes or imports
+command.usage=[<variables|classes|imports|preferences|all>]
+command.help=Show variables, classes, imports or preferences.
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/package.html b/groovy/src/main/org/codehaus/groovy/tools/shell/package.html
new file mode 100644
index 0000000..12780e5
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/package.html
@@ -0,0 +1,26 @@
+<!--
+    Copyright 2003-2007 the original author or authors.
+    
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+    
+        http://www.apache.org/licenses/LICENSE-2.0
+        
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!-- $Id$ -->
+
+<html>
+    <body>
+        <p>
+            Provides support for the Groovy Shell (aka. <tt>groovysh</tt>).
+        </p>
+    </body>
+</html>
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/util/ANSI.java b/groovy/src/main/org/codehaus/groovy/tools/shell/util/ANSI.java
new file mode 100644
index 0000000..e7e9053
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/util/ANSI.java
@@ -0,0 +1,421 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import jline.ANSIBuffer.ANSICodes;
+import jline.Terminal;
+
+/**
+ * Provides support for using ANSI color escape codes.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class ANSI
+{
+    //
+    // Detection/Enabled Muck
+    //
+
+    /**
+     * Tries to detect if the current system supports ANSI.
+     */
+    private static boolean detect() {
+        boolean enabled = Terminal.getTerminal().isANSISupported();
+
+        if (!enabled) {
+            String force = System.getProperty(ANSI.class.getName() + ".force", "false");
+            enabled = Boolean.valueOf(force).booleanValue();
+        }
+
+        return enabled;
+    }
+
+    public static boolean isDetected() {
+        return detect();
+    }
+
+    private static Boolean enabled;
+
+    public static void setEnabled(final boolean flag) {
+        enabled = Boolean.valueOf(flag);
+    }
+
+    public static boolean isEnabled() {
+        if (enabled == null) {
+            enabled = Boolean.valueOf(isDetected());
+        }
+        
+        return enabled.booleanValue();
+    }
+
+    //
+    // Code
+    //
+
+    public static class Code
+    {
+        //
+        // NOTE: Some fields duplicated from jline.ANSIBuffer.ANSICodes to change access modifiers
+        //
+        
+        public static final int OFF = 0;
+        public static final int BOLD = 1;
+        public static final int UNDERSCORE = 4;
+        public static final int BLINK = 5;
+        public static final int REVERSE = 7;
+        public static final int CONCEALED = 8;
+        
+        public static final int FG_BLACK = 30;
+        public static final int FG_RED = 31;
+        public static final int FG_GREEN = 32;
+        public static final int FG_YELLOW = 33;
+        public static final int FG_BLUE = 34;
+        public static final int FG_MAGENTA = 35;
+        public static final int FG_CYAN = 36;
+        public static final int FG_WHITE = 37;
+
+        public static final int BLACK = FG_BLACK;
+        public static final int RED = FG_RED;
+        public static final int GREEN = FG_GREEN;
+        public static final int YELLOW = FG_YELLOW;
+        public static final int BLUE = FG_BLUE;
+        public static final int MAGENTA = FG_MAGENTA;
+        public static final int CYAN = FG_CYAN;
+        public static final int WHITE = FG_WHITE;
+        
+        public static final int BG_BLACK = 40;
+        public static final int BG_RED = 41;
+        public static final int BG_GREEN = 42;
+        public static final int BG_YELLOW = 43;
+        public static final int BG_BLUE = 44;
+        public static final int BG_MAGENTA = 45;
+        public static final int BG_CYAN = 46;
+        public static final int BG_WHITE = 47;
+
+        /** A map of code names to values. */
+        private static final Map NAMES_TO_CODES;
+
+        /** A map of codes to name. */
+        private static final Map CODES_TO_NAMES;
+
+        static {
+            Field[] fields = Code.class.getDeclaredFields();
+            Map names = new HashMap(fields.length);
+            Map codes = new HashMap(fields.length);
+
+            try {
+                for (int i=0; i<fields.length; i++) {
+                    // Skip anything non-public, all public fields are codes
+                    int mods = fields[i].getModifiers();
+                    if (!Modifier.isPublic(mods)) {
+                        continue;
+                    }
+                    
+                    String name = fields[i].getName();
+                    Number code = (Number) fields[i].get(Code.class);
+                    
+                    names.put(name, code);
+                    codes.put(code, name);
+                }
+            }
+            catch (IllegalAccessException e) {
+                // This should never happen
+                throw new Error(e);
+            }
+
+            NAMES_TO_CODES = names;
+            CODES_TO_NAMES = codes;
+        }
+
+        /**
+         * Returns the ANSI code for the given symbolic name.  Supported symbolic names are all defined as
+         * fields in {@link org.codehaus.groovy.tools.shell.util.ANSI.Code} where the case is not significant.
+         */
+        public static int forName(final String name) throws IllegalArgumentException {
+            assert name != null;
+
+            // All names in the map are upper-case
+            String tmp = name.toUpperCase();
+            Number code = (Number) NAMES_TO_CODES.get(tmp);
+
+            if (code == null) {
+                throw new IllegalArgumentException("Invalid ANSI code name: " + name);
+            }
+
+            return code.intValue();
+        }
+
+        /**
+         * Returns the symbolic name for the given ANSI code.
+         */
+        public static String name(final int code) throws IllegalArgumentException {
+            String name = (String) CODES_TO_NAMES.get(new Integer(code));
+
+            if (name == null) {
+                throw new IllegalArgumentException("Invalid ANSI code: " + code);
+            }
+
+            return name;
+        }
+    }
+
+    //
+    // Buffer
+    //
+
+    public static class Buffer
+    {
+        private final StringBuffer buff = new StringBuffer();
+
+        public final boolean autoClear = true;
+
+        public String toString() {
+            try {
+                return buff.toString();
+            }
+            finally {
+                if (autoClear) clear();
+            }
+        }
+
+        public void clear() {
+            buff.setLength(0);
+        }
+
+        public int size() {
+            return buff.length();
+        }
+
+        public Buffer append(final String text) {
+            buff.append(text);
+
+            return this;
+        }
+
+        public Buffer append(final Object obj) {
+            return append(String.valueOf(obj));
+        }
+
+        public Buffer attrib(final int code) {
+            if (isEnabled()) {
+                buff.append(ANSICodes.attrib(code));
+            }
+
+            return this;
+        }
+
+        public Buffer attrib(final String text, final int code) {
+            assert text != null;
+
+            if (isEnabled()) {
+                buff.append(ANSICodes.attrib(code)).append(text).append(ANSICodes.attrib(Code.OFF));
+            }
+            else {
+                buff.append(text);
+            }
+            
+            return this;
+        }
+
+        public Buffer attrib(final String text, final String codeName) {
+            return attrib(text, Code.forName(codeName));
+        }
+    }
+
+    //
+    // Renderer
+    //
+
+    public static class Renderer
+    {
+        public static final String BEGIN_TOKEN = "@|";
+
+        private static final int BEGIN_TOKEN_SIZE = BEGIN_TOKEN.length();
+
+        public static final String END_TOKEN = "|";
+
+        private static final int END_TOKEN_SIZE = END_TOKEN.length();
+
+        public static final String CODE_TEXT_SEPARATOR  = " ";
+
+        public static final String CODE_LIST_SEPARATOR  = ",";
+
+        private final Buffer buff = new Buffer();
+
+        public String render(final String input) throws RenderException {
+            assert input != null;
+
+            // current, prefix and suffix positions
+            int c = 0, p, s;
+
+            while (c < input.length()) {
+                p = input.indexOf(BEGIN_TOKEN, c);
+                if (p < 0) { break; }
+
+                s = input.indexOf(END_TOKEN, p + BEGIN_TOKEN_SIZE);
+                if (s < 0) {
+                    throw new RenderException("Missing '" + END_TOKEN + "': " + input);
+                }
+
+                String expr = input.substring(p + BEGIN_TOKEN_SIZE, s);
+
+                buff.append(input.substring(c, p));
+
+                evaluate(expr);
+
+                c = s + END_TOKEN_SIZE;
+            }
+
+            buff.append(input.substring(c));
+
+            return buff.toString();
+        }
+
+        private void evaluate(final String input) throws RenderException {
+            assert input != null;
+
+            int i = input.indexOf(CODE_TEXT_SEPARATOR);
+            if (i < 0) {
+                throw new RenderException("Missing ANSI code/text separator '" + CODE_TEXT_SEPARATOR + "': " + input);
+            }
+
+            String tmp = input.substring(0, i);
+            String[] codes = tmp.split(CODE_LIST_SEPARATOR);
+            String text = input.substring(i + 1, input.length());
+
+            for (int j=0; j<codes.length; j++) {
+                int code = Code.forName(codes[j]);
+                buff.attrib(code);
+            }
+
+            buff.append(text);
+
+            buff.attrib(Code.OFF);
+        }
+
+        //
+        // RenderException
+        //
+
+        public static class RenderException
+            extends RuntimeException
+        {
+            public RenderException(final String msg) {
+                super(msg);
+            }
+        }
+
+        //
+        // Helpers
+        //
+
+        public static boolean test(final String text) {
+            return text != null && text.indexOf(BEGIN_TOKEN) >= 0;
+        }
+
+        public static String encode(final String text, final int code) {
+            return new StringBuffer(BEGIN_TOKEN).
+                    append(Code.name(code)).
+                    append(CODE_TEXT_SEPARATOR).
+                    append(text).
+                    append(END_TOKEN).
+                    toString();
+        }
+    }
+
+    //
+    // RenderWriter
+    //
+
+    public static class RenderWriter
+        extends PrintWriter
+    {
+        private final Renderer renderer = new Renderer();
+
+        public RenderWriter(final OutputStream out) {
+            super(out);
+        }
+
+        public RenderWriter(final OutputStream out, final boolean autoFlush) {
+            super(out, autoFlush);
+        }
+
+        public RenderWriter(final Writer out) {
+            super(out);
+        }
+
+        public RenderWriter(final Writer out, final boolean autoFlush) {
+            super(out, autoFlush);
+        }
+
+        public void write(final String s) {
+            if (Renderer.test(s)) {
+                super.write(renderer.render(s));
+            }
+            else {
+                super.write(s);
+            }
+        }
+    }
+
+    //
+    // RenderMessageSource
+    //
+
+    public static class RenderMessageSource
+        extends MessageSource
+    {
+        private final Renderer renderer = new Renderer();
+
+        public RenderMessageSource(final String[] names) {
+            super(names);
+        }
+
+        public RenderMessageSource(final String name) {
+            super(name);
+        }
+
+        public RenderMessageSource(final Class[] types) {
+            super(types);
+        }
+
+        public RenderMessageSource(final Class type) {
+            super(type);
+        }
+
+        public String getMessage(final String code) {
+            final String msg = super.getMessage(code);
+
+            if (Renderer.test(msg)) {
+                return renderer.render(msg);
+            }
+
+            return msg;
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/util/ClassNameCompletor.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/util/ClassNameCompletor.groovy
new file mode 100644
index 0000000..aabe7d4
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/util/ClassNameCompletor.groovy
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.util
+
+/**
+ * Provides completion for class names.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ClassNameCompletor
+    extends SimpleCompletor
+{
+    private final GroovyClassLoader classLoader
+
+    ClassNameCompletor(final GroovyClassLoader classLoader) {
+        assert classLoader
+
+        this.classLoader = classLoader
+        
+        delimiter = '.'
+    }
+
+    SortedSet getCandidates() {
+        def set = new TreeSet()
+
+        //
+        // TODO: Figure out what class names to include, for now just hack in some to test with
+        //
+
+        set << 'java.lang.System'
+        set << 'groovy.lang.GroovyObject'
+
+        return set
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/util/HelpFormatter.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/util/HelpFormatter.groovy
new file mode 100644
index 0000000..6befcc8
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/util/HelpFormatter.groovy
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.util
+
+import jline.Terminal
+
+import org.apache.commons.cli.Options
+
+//
+// NOTE: Some code duplicated and augumented from commons-cli (1.0) sources to
+//       properly render options w/arguments.
+//
+
+
+/**
+ * Custom CLI help formatter to render things correctly.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class HelpFormatter
+    extends org.apache.commons.cli.HelpFormatter
+{
+    HelpFormatter() {
+        defaultLeftPad = 2
+        defaultDescPad = 4
+    }
+
+    // Detect the terminal width late
+    public int getDefaultWidth() {
+        return Terminal.terminal.terminalWidth - 1
+    }
+
+    protected StringBuffer renderOptions(final StringBuffer sb, final int width, final Options options, final int leftPad, final int descPad) {
+        assert sb != null
+        assert options
+        
+        List prefixes = []
+        String lpad = ' ' * leftPad
+        
+        List opts = options.shortOpts.values().sort { a, b ->
+            return (a.opt == ' ' ? a.longOpt : a.opt) <=> (b.opt == ' ' ? b.longOpt : b.opt)
+        }
+        
+        // Render the prefixes (-X,--xxxx muck)
+        opts.each { option ->
+            def buff = new StringBuffer(8)
+            
+            if (option.opt == ' ') {
+                buff << "${lpad}    ${defaultLongOptPrefix}${option.longOpt}"
+            }
+            else {
+                buff << "${lpad}${defaultOptPrefix}${option.opt}"
+                
+                if (option.hasLongOpt()) {
+                    buff << ", ${defaultLongOptPrefix}${option.longOpt}"
+                }
+            }
+            
+            if (option.hasArg()) {
+                if (option.hasArgName()) {
+                    if (option.hasOptionalArg()) {
+                        buff << "[=${option.argName}]"
+                    }
+                    else {
+                        buff << "=${option.argName}"
+                    }
+                }
+                else {
+                    buff << ' '
+                }
+            }
+            
+            prefixes << buff
+        }
+        
+        // Figure out how long the biggest prefix is
+        int maxPrefix = prefixes.max { a, b -> a.size() <=> b.size() }.size()
+        
+        String dpad = ' ' * descPad
+        
+        // And then render each option's prefix with its description
+        opts.eachWithIndex { option, i ->
+            def buff = new StringBuffer(prefixes[i].toString())
+            
+            if (buff.size() < maxPrefix) {
+                buff << ' ' * (maxPrefix - buff.size())
+            }
+            buff << dpad
+            
+            int nextLineTabStop = maxPrefix + descPad
+            String text = buff << option.description
+            
+            renderWrappedText(sb, width, nextLineTabStop, text)
+            
+            if (i < opts.size() - 1) {
+                sb << defaultNewLine
+            }
+        }
+        
+        return sb
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/util/Logger.java b/groovy/src/main/org/codehaus/groovy/tools/shell/util/Logger.java
new file mode 100644
index 0000000..efa10a0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/util/Logger.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.util;
+
+import org.codehaus.groovy.tools.shell.IO;
+
+/**
+ * Provides a very, very basic logging API.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public final class Logger {
+    public static IO io;
+    public final String name;
+
+    private Logger(final String name) {
+        assert name != null;
+        this.name = name;
+    }
+    
+    private void log(final String level, Object msg, Throwable cause) throws Exception {
+        assert level != null;
+        assert msg != null;
+        
+        if (io == null) {
+            io = new IO();
+        }
+
+        // Allow the msg to be a Throwable, and handle it properly if no cause is given
+        if (cause == null) {
+            if (msg instanceof Throwable) {
+                cause = (Throwable) msg;
+                msg = cause.getMessage();
+            }
+        }
+
+        StringBuffer buff = new StringBuffer();
+        
+        int color = ANSI.Code.BOLD;
+        if (WARN.equals(level) || ERROR.equals(level)) {
+            color = ANSI.Code.RED;
+        }
+
+        buff.append(ANSI.Renderer.encode(level, color));
+
+        buff.append(" [");
+        buff.append(name);
+        buff.append("] ");
+        buff.append(msg);
+
+        io.out.println(buff);
+
+        if (cause != null) {
+            cause.printStackTrace(io.out);
+        }
+        
+        io.flush();
+    }
+    
+    //
+    // Level helpers
+    //
+    
+    private static final String DEBUG = "DEBUG";
+
+    public boolean isDebugEnabled() {
+        return Preferences.verbosity == IO.Verbosity.DEBUG;
+    }
+
+    public boolean isDebug() {
+        return isDebugEnabled();
+    }
+    
+    public void debug(final Object msg) throws Exception {
+        if (isDebugEnabled()) {
+            log(DEBUG, msg, null);
+        }
+    }
+    
+    public void debug(final Object msg, final Throwable cause) throws Exception {
+        if (isDebugEnabled()) {
+            log(DEBUG, msg, cause);
+        }
+    }
+
+    private static final String WARN = "WARN";
+
+    public void warn(final Object msg) throws Exception {
+        log(WARN, msg, null);
+    }
+
+    public void warn(final Object msg, final Throwable cause) throws Exception {
+        log(WARN, msg, cause);
+    }
+    
+    private static final String ERROR = "ERROR";
+
+    public void error(final Object msg) throws Exception {
+        log(ERROR, msg, null);
+    }
+
+    public void error(final Object msg, final Throwable cause) throws Exception {
+        log(ERROR, msg, cause);
+    }
+
+    //
+    // Factory access
+    //
+    
+    public static Logger create(final Class type) {
+        return new Logger(type.getName());
+    }
+
+    public static Logger create(final Class type, final String suffix) {
+        return new Logger(type.getName() + "." + suffix);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/util/MessageSource.java b/groovy/src/main/org/codehaus/groovy/tools/shell/util/MessageSource.java
new file mode 100644
index 0000000..cdbfa03
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/util/MessageSource.java
@@ -0,0 +1,145 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.util;
+
+import java.util.ResourceBundle;
+import java.util.MissingResourceException;
+
+import java.text.MessageFormat;
+
+import groovy.lang.GroovyObjectSupport;
+
+/**
+ * Message source backed up by one or more {@link java.util.ResourceBundle}
+ * instances for simple i18n support.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class MessageSource
+    extends GroovyObjectSupport
+{
+    private final String[] bundleNames;
+    
+    private ResourceBundle[] cachedBundles;
+    
+    public MessageSource(final String[] names) {
+        assert names != null;
+        assert names.length != 0;
+        
+        this.bundleNames = names;
+    }
+    
+    public MessageSource(final String name) {
+        this(new String[] { name });
+    }
+    
+    private static String[] classNames(final Class[] types) {
+        assert types != null;
+        assert types.length != 0;
+        
+        String[] names = new String[types.length];
+        
+        for (int i=0; i<types.length; i++) {
+            assert types[i] != null;
+            
+            names[i] = types[i].getName();
+        }
+        
+        return names;
+    }
+    
+    public MessageSource(final Class[] types) {
+        this(classNames(types));
+    }
+    
+    public MessageSource(final Class type) {
+        this(new String[] { type.getName() });
+    }
+    
+    private ResourceBundle[] createBundles() {
+        ResourceBundle[] bundles = new ResourceBundle[bundleNames.length];
+        
+        for (int i=0; i<bundleNames.length; i++) {
+            assert bundleNames[i] != null;
+            
+            bundles[i] = ResourceBundle.getBundle(bundleNames[i]);
+        }
+        
+        return bundles;
+    }
+    
+    private ResourceBundle[] getBundles() {
+        if (cachedBundles == null) {
+            cachedBundles = createBundles();
+        }
+        return cachedBundles;
+    }
+    
+    /**
+     * Get a raw message from the resource bundles using the given code.
+     */
+    public String getMessage(final String code) {
+        assert code != null;
+        
+        MissingResourceException error = null;
+        
+        ResourceBundle[] bundles = getBundles();
+        
+        for (int i=0; i<bundles.length; i++) {
+            try {
+                return bundles[i].getString(code);
+            }
+            catch (MissingResourceException e) {
+                //
+                // FIXME: For now just save the first error, should really roll a new message with all of the details
+                //
+                
+                if (error != null) {
+                    error = e;
+                }
+            }
+        }
+        
+        assert error != null;
+        
+        throw error;
+    }
+    
+    /**
+     * Format a message (based on {@link MessageFormat} using the message
+     * from the resource bundles using the given code as a pattern and the
+     * given objects as arguments.
+     */
+    public String format(final String code, final Object[] args) {
+        assert args != null;
+        
+        String pattern = getMessage(code);
+        
+        return MessageFormat.format(pattern, args);
+    }
+    
+    /**
+     * @see #getMessage(String)
+     */
+    public Object getProperty(final String name) {
+        return getMessage(name);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/util/NoExitSecurityManager.java b/groovy/src/main/org/codehaus/groovy/tools/shell/util/NoExitSecurityManager.java
new file mode 100644
index 0000000..bbc728e
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/util/NoExitSecurityManager.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2006-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.util;
+
+import java.security.Permission;
+
+/**
+ * Custom security manager to {@link System#exit} (and related) from being used.
+ *
+ * @version $Rev$ $Date$
+ */
+public class NoExitSecurityManager
+    extends SecurityManager
+{
+    private final SecurityManager parent;
+
+    public NoExitSecurityManager(final SecurityManager parent) {
+        assert parent != null;
+
+        this.parent = parent;
+    }
+
+    public NoExitSecurityManager() {
+        this(System.getSecurityManager());
+    }
+
+    public void checkPermission(final Permission perm) {
+        if (parent != null) {
+            parent.checkPermission(perm);
+        }
+    }
+
+    /**
+     * Always throws {@link SecurityException}.
+     */
+    public void checkExit(final int code) {
+        throw new SecurityException("Use of System.exit() if forbidden!");
+    }
+
+    /*
+    public void checkPermission(final Permission perm) {
+        assert perm != null;
+
+        if (perm.getName().equals("exitVM")) {
+            System.out.println("exitVM");
+        }
+    }
+    */
+}
\ No newline at end of file
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/util/Preferences.java b/groovy/src/main/org/codehaus/groovy/tools/shell/util/Preferences.java
new file mode 100644
index 0000000..2c695db
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/util/Preferences.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.util;
+
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.PreferenceChangeEvent;
+
+import org.codehaus.groovy.tools.shell.IO;
+
+/**
+ * Container for shell preferences.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class Preferences
+{
+    private static final java.util.prefs.Preferences STORE = java.util.prefs.Preferences.userRoot().node("/org/codehaus/groovy/tools/shell");
+
+    public static IO.Verbosity verbosity;
+
+    static {
+        String tmp = STORE.get("verbosity", IO.Verbosity.INFO.name);
+        try {
+            verbosity = IO.Verbosity.forName(tmp);
+        }
+        catch (IllegalArgumentException e) {
+            verbosity = IO.Verbosity.INFO;
+            STORE.remove("verbosity");
+        }
+
+        addChangeListener(new PreferenceChangeListener() {
+            public void preferenceChange(final PreferenceChangeEvent event) {
+                if (event.getKey().equals("verbosity")) {
+                    String name = event.getNewValue();
+
+                    if (name == null) {
+                        name = IO.Verbosity.INFO.name;
+                    }
+
+                    try {
+                        verbosity = IO.Verbosity.forName(name);
+                    }
+                    catch (Exception e) {
+                        event.getNode().put(event.getKey(), verbosity.name);
+                    }
+                }
+            }
+        });
+    }
+
+    public static boolean showLastResult = STORE.getBoolean("show-last-result", true);
+
+    public static boolean sanitizeStackTrace = STORE.getBoolean("sanitize-stack-trace", true);
+
+    public static String[] keys() throws BackingStoreException {
+        return STORE.keys();
+    }
+
+    public static String get(final String name, final String defaultValue) {
+        return STORE.get(name, defaultValue);
+    }
+
+    public static String get(final String name) {
+        return get(name, null);
+    }
+
+    public static void put(final String name, final String value) {
+        STORE.put(name, value);
+    }
+
+    public static void clear() throws BackingStoreException {
+        STORE.clear();
+    }
+
+    public static void addChangeListener(final PreferenceChangeListener listener) {
+        STORE.addPreferenceChangeListener(listener);
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/util/SimpleCompletor.java b/groovy/src/main/org/codehaus/groovy/tools/shell/util/SimpleCompletor.java
new file mode 100644
index 0000000..f40ef64
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/util/SimpleCompletor.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.util;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.SortedSet;
+
+import groovy.lang.Closure;
+
+/**
+ * Support for simple completors.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+public class SimpleCompletor
+    extends jline.SimpleCompletor
+{
+    public SimpleCompletor(final String[] candidates) {
+        super(candidates);
+    }
+    
+    public SimpleCompletor() {
+        this(new String[0]);
+    }
+    
+    public SimpleCompletor(final Closure loader) {
+        this();
+        
+        assert loader != null;
+        
+        Object obj = loader.call();
+        
+        List list = null;
+        
+        if (obj instanceof List) {
+            list = (List)obj;
+        }
+        
+        //
+        // TODO: Maybe handle arrays too?
+        //
+        
+        if (list == null) {
+            throw new IllegalStateException("The loader closure did not return a list of candicates; found: " + obj);
+        }
+
+        Iterator iter = list.iterator();
+
+        while (iter.hasNext()) {
+            add(String.valueOf(iter.next()));
+        }
+    }
+
+    public void add(final String candidate) {
+        addCandidateString(candidate);
+    }
+
+    public Object leftShift(final String s) {
+        add(s);
+        
+        return null;
+    }
+
+    //
+    // NOTE: Duplicated (and augumented) from JLine sources to make it call getCandidates() to make the list more dynamic
+    //
+
+    public int complete(final String buffer, final int cursor, final List clist) {
+        String start = (buffer == null) ? "" : buffer;
+
+        SortedSet matches = getCandidates().tailSet(start);
+
+        for (Iterator i = matches.iterator(); i.hasNext();) {
+            String can = (String) i.next();
+
+            if (!(can.startsWith(start))) {
+                break;
+            }
+            
+            String delim = getDelimiter();
+            
+            if (delim != null) {
+                int index = can.indexOf(delim, cursor);
+
+                if (index != -1) {
+                    can = can.substring(0, index + 1);
+                }
+            }
+
+            clist.add(can);
+        }
+
+        if (clist.size() == 1) {
+            clist.set(0, ((String) clist.get(0)) + " ");
+        }
+
+        // the index of the completion is always from the beginning of the buffer.
+        return (clist.size() == 0) ? (-1) : 0;
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/util/XmlCommandRegistrar.groovy b/groovy/src/main/org/codehaus/groovy/tools/shell/util/XmlCommandRegistrar.groovy
new file mode 100644
index 0000000..c3f80a4
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/util/XmlCommandRegistrar.groovy
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.util
+
+import org.codehaus.groovy.tools.shell.Shell
+import org.codehaus.groovy.tools.shell.Command
+
+/**
+ * Registers {@link Command} instances from and XML file.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class XmlCommandRegistrar
+{
+    private final Logger log = Logger.create(this.class)
+    
+    private final Shell shell
+    
+    private final ClassLoader classLoader
+    
+    XmlCommandRegistrar(final Shell shell, final ClassLoader classLoader) {
+        assert shell
+        assert classLoader
+        
+        this.shell = shell
+        this.classLoader = classLoader
+    }
+    
+    void register(final URL url) {
+        assert url
+        
+        if (log.debugEnabled) {
+            log.debug("Registering commands from: $url")
+        }
+        
+        url.withReader { reader ->
+            def doc = new XmlParser().parse(reader)
+            
+            doc.command.each { element ->
+                String classname = element.text()
+                
+                Class type = classLoader.loadClass(classname)
+                
+                Command command = type.newInstance(shell)
+                
+                if (log.debugEnabled) {
+                    log.debug("Created command '${command.name}': $command")
+                }
+                
+                shell << command
+            }
+        }
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/shell/util/package.html b/groovy/src/main/org/codehaus/groovy/tools/shell/util/package.html
new file mode 100644
index 0000000..5420727
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/shell/util/package.html
@@ -0,0 +1,26 @@
+<!--
+    Copyright 2003-2007 the original author or authors.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!-- $Id: package.html 7254 2007-08-15 22:26:39Z user57 $ -->
+
+<html>
+    <body>
+        <p>
+            Utility classes related to the Groovy Shell (aka. <tt>groovysh</tt>).
+        </p>
+    </body>
+</html>
+
diff --git a/groovy/src/main/org/codehaus/groovy/tools/xml/DomToGroovy.java b/groovy/src/main/org/codehaus/groovy/tools/xml/DomToGroovy.java
new file mode 100644
index 0000000..5883ae0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/xml/DomToGroovy.java
@@ -0,0 +1,376 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.tools.xml;
+
+import groovy.util.IndentPrinter;
+import org.w3c.dom.*;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A SAX handler for turning XML into Groovy scripts
+ * 
+ * @author James Strachan
+ * @author paulk
+ */
+public class DomToGroovy {
+
+    private IndentPrinter out;
+    private boolean inMixed = false;
+    private String qt = "'";
+    private List keywords = Arrays.asList(new String[]{
+        "import", "private", "public", "protected"
+    });
+
+    public DomToGroovy(PrintWriter out) {
+        this(new IndentPrinter(out));
+    }
+
+    // TODO allow string quoting delimiter to be specified, e.g. ' vs "
+    public DomToGroovy(IndentPrinter out) {
+        this.out = out;
+    }
+
+    public void print(Document document) {
+        printChildren(document, new HashMap());
+    }
+
+    public static void main(String[] args) {
+        if (args.length < 1) {
+            System.out.println("Usage: DomToGroovy infilename [outfilename]");
+            System.exit(1);
+        }
+        Document document = null;
+        try {
+            document = parse(args[0]);
+        } catch (Exception e) {
+            System.out.println("Unable to parse input file '" + args[0] + "': " + e.getMessage());
+            System.exit(1);
+        }
+        PrintWriter writer = null;
+        if (args.length < 2) {
+            writer = new PrintWriter(System.out);
+        } else {
+            try {
+                writer = new PrintWriter(new FileWriter(new File(args[1])));
+            } catch (IOException e) {
+                System.out.println("Unable to create output file '" + args[1] + "': " + e.getMessage());
+                System.exit(1);
+            }
+        }
+        DomToGroovy converter = new DomToGroovy(writer);
+        converter.out.incrementIndent();
+        writer.println("#!/bin/groovy");
+        writer.println();
+        writer.println("// generated from " + args[0]);
+        writer.println("System.out << new groovy.xml.StreamingMarkupBuilder().bind {");        
+        converter.print(document);
+        writer.println("}");
+        writer.close();
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+    private static Document parse(String name) throws Exception {
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+        DocumentBuilder builder = factory.newDocumentBuilder();
+        return builder.parse(new File(name));
+    }
+
+    protected void print(Node node, Map namespaces, boolean endWithComma) {
+        switch (node.getNodeType()) {
+            case Node.ELEMENT_NODE :
+                printElement((Element) node, namespaces, endWithComma);
+                break;
+            case Node.PROCESSING_INSTRUCTION_NODE :
+                printPI((ProcessingInstruction) node, endWithComma);
+                break;
+            case Node.TEXT_NODE :
+                printText((Text) node, endWithComma);
+                break;
+            case Node.COMMENT_NODE :
+                printComment((Comment) node, endWithComma);
+                break;
+        }
+    }
+
+    protected void printElement(Element element, Map namespaces, boolean endWithComma) {
+        namespaces = defineNamespaces(element, namespaces);
+
+        element.normalize();
+        printIndent();
+
+        String prefix = element.getPrefix();
+        boolean hasPrefix = prefix != null && prefix.length() > 0;
+        String localName = getLocalName(element);
+        boolean isKeyword = checkEscaping(localName);
+        if (isKeyword || hasPrefix) print(qt);
+        if (hasPrefix) {
+            print(prefix);
+            print(".");
+        }
+        print(localName);
+        if (isKeyword || hasPrefix) print(qt);
+        print("(");
+
+        boolean hasAttributes = printAttributes(element);
+
+        NodeList list = element.getChildNodes();
+        int length = list.getLength();
+        if (length == 0) {
+            printEnd(")", endWithComma);
+        } else {
+            Node node = list.item(0);
+            if (length == 1 && node instanceof Text) {
+                Text textNode = (Text) node;
+                String text = getTextNodeData(textNode);
+                if (hasAttributes) print(", ");
+                printQuoted(text);
+                printEnd(")", endWithComma);
+            } else if (mixedContent(list)) {
+                println(") {");
+                out.incrementIndent();
+                boolean oldInMixed = inMixed;
+                inMixed = true;
+                for (node = element.getFirstChild(); node != null; node = node.getNextSibling()) {
+                    print(node, namespaces, false);
+                }
+                inMixed = oldInMixed;
+                out.decrementIndent();
+                printIndent();
+                printEnd("}", endWithComma);
+            } else {
+                println(") {");
+                out.incrementIndent();
+                printChildren(element, namespaces);
+                out.decrementIndent();
+                printIndent();
+                printEnd("}", endWithComma);
+            }
+        }
+    }
+
+    private void printQuoted(String text) {
+        if (text.indexOf("\n") != -1) {
+            print("'''");
+            print(text);
+            print("'''");
+        } else {
+            print(qt);
+            print(escapeQuote(text));
+            print(qt);
+        }
+    }
+
+    protected void printPI(ProcessingInstruction instruction, boolean endWithComma) {
+        printIndent();
+        print("mkp.pi(" + qt);
+        print(instruction.getTarget());
+        print(qt + ", " + qt);
+        print(instruction.getData());
+        printEnd(qt + ");", endWithComma);
+    }
+
+    protected void printComment(Comment comment, boolean endWithComma) {
+        String text = comment.getData().trim();
+        if (text.length() >0) {
+            printIndent();
+            print("/* ");
+            print(text);
+            printEnd(" */", endWithComma);
+        }
+    }
+
+    protected void printText(Text node, boolean endWithComma) {
+        String text = getTextNodeData(node);
+        if (text.length() > 0) {
+            printIndent();
+            if (inMixed) print("mkp.yield ");
+            printQuoted(text);
+            printEnd("", endWithComma);
+        }
+    }
+
+    private String escapeQuote(String text) {
+        return text.replaceAll("\\\\", "\\\\\\\\").replaceAll(qt, "\\\\" + qt);
+    }
+
+    protected Map defineNamespaces(Element element, Map namespaces) {
+        Map answer = null;
+        String prefix = element.getPrefix();
+        if (prefix != null && prefix.length() > 0 && !namespaces.containsKey(prefix)) {
+            answer = new HashMap(namespaces);
+            defineNamespace(answer, prefix, element.getNamespaceURI());
+        }
+        NamedNodeMap attributes = element.getAttributes();
+        int length = attributes.getLength();
+        for (int i = 0; i < length; i++) {
+            Attr attribute = (Attr) attributes.item(i);
+            prefix = attribute.getPrefix();
+            if (prefix != null && prefix.length() > 0 && !namespaces.containsKey(prefix)) {
+                if (answer == null) {
+                    answer = new HashMap(namespaces);
+                }
+                defineNamespace(answer, prefix, attribute.getNamespaceURI());
+            }
+        }
+        return (answer != null) ? answer : namespaces;
+    }
+
+    protected void defineNamespace(Map namespaces, String prefix, String uri) {
+        namespaces.put(prefix, uri);
+        if (!prefix.equals("xmlns") && !prefix.equals("xml")) {
+            printIndent();
+            print("mkp.declareNamespace(");
+            print(prefix);
+            print(":" + qt);
+            print(uri);
+            println(qt + ")");
+        }
+    }
+
+    protected boolean printAttributes(Element element) {
+        boolean hasAttribute = false;
+        NamedNodeMap attributes = element.getAttributes();
+        int length = attributes.getLength();
+        if (length > 0) {
+            StringBuffer buffer = new StringBuffer();
+            for (int i = 0; i < length; i++) {
+                printAttributeWithPrefix((Attr) attributes.item(i), buffer);
+            }
+            for (int i = 0; i < length; i++) {
+                hasAttribute = printAttributeWithoutPrefix((Attr) attributes.item(i), hasAttribute);
+            }
+            if (buffer.length() > 0) {
+                if (hasAttribute) {
+                    print(", ");
+                }
+                print(buffer.toString());
+                hasAttribute = true;
+            }
+        }
+        return hasAttribute;
+    }
+
+    private void printAttributeWithPrefix(Attr attribute, StringBuffer buffer) {
+        String prefix = attribute.getPrefix();
+        if (prefix != null && prefix.length() > 0 && !prefix.equals("xmlns")) {
+            if (buffer.length() > 0) {
+                buffer.append(", ");
+            }
+            buffer.append(qt);
+            buffer.append(prefix);
+            buffer.append(".");
+            buffer.append(getLocalName(attribute));
+            buffer.append(qt + ":" + qt);
+            buffer.append(escapeQuote(getAttributeValue(attribute)));
+            buffer.append(qt);
+        }
+    }
+
+    private String getAttributeValue(Attr attribute) {
+        return attribute.getValue();
+    }
+
+    private boolean printAttributeWithoutPrefix(Attr attribute, boolean hasAttribute) {
+        String prefix = attribute.getPrefix();
+        if (prefix == null || prefix.length() == 0) {
+            if (!hasAttribute) {
+                hasAttribute = true;
+            } else {
+                print(", ");
+            }
+            String localName = getLocalName(attribute);
+            boolean needsEscaping = checkEscaping(localName);
+            if (needsEscaping) print(qt);
+            print(localName);
+            if (needsEscaping) print(qt);
+            print(":");
+            printQuoted(getAttributeValue(attribute));
+        }
+        return hasAttribute;
+    }
+
+    private boolean checkEscaping(String localName) {
+        return keywords.contains(localName) || localName.indexOf("-") != -1;
+    }
+
+    protected String getTextNodeData(Text node) {
+        return node.getData().trim();
+    }
+
+    protected boolean mixedContent(NodeList list) {
+        boolean hasText = false;
+        boolean hasElement = false;
+        for (int i = 0, size = list.getLength(); i < size; i++) {
+            Node node = list.item(i);
+            if (node instanceof Element) {
+                hasElement = true;
+            } else if (node instanceof Text) {
+                String text = getTextNodeData((Text) node);
+                if (text.length() > 0) {
+                    hasText = true;
+                }
+            }
+        }
+        return hasText && hasElement;
+    }
+
+    protected void printChildren(Node parent, Map namespaces) {
+        for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {
+            print(node, namespaces, false);
+        }
+    }
+
+    protected String getLocalName(Node node) {
+        String answer = node.getLocalName();
+        if (answer == null) {
+            answer = node.getNodeName();
+        }
+        return answer.trim();
+    }
+
+    protected void printEnd(String text, boolean endWithComma) {
+        if (endWithComma) {
+            print(text);
+            println(",");
+        } else {
+            println(text);
+        }
+    }
+
+    protected void println(String text) {
+        out.println(text);
+    }
+
+    protected void print(String text) {
+        out.print(text);
+    }
+
+    protected void printIndent() {
+        out.printIndent();
+    }
+}
diff --git a/groovy/src/main/org/codehaus/groovy/tools/xml/package.html b/groovy/src/main/org/codehaus/groovy/tools/xml/package.html
new file mode 100644
index 0000000..324792b
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/tools/xml/package.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <title>package org.codehaus.groovy.tools.xml.*</title>
+  </head>
+  <body>
+    <p>XML utilities such as for converting XML into Groovy scripts.</p>
+  </body>
+</html>
diff --git a/groovy/src/main/org/codehaus/groovy/vmplugin/VMPluginFactory.java b/groovy/src/main/org/codehaus/groovy/vmplugin/VMPluginFactory.java
new file mode 100644
index 0000000..d6d0a2c
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/vmplugin/VMPluginFactory.java
@@ -0,0 +1,45 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.vmplugin;

+

+import org.codehaus.groovy.vmplugin.v4.Java4;

+

+/**

+ * factory class to get functionlity based on the VM version.

+ * The usage of this class is not for public use, only for the

+ * runtime.

+ * @author Jochen Theodorou

+ */

+public class VMPluginFactory {

+    

+    private static final String JDK5_CLASSNAME_CHECK = "java.lang.annotation.Annotation";

+    private static final String JDK5_PLUGIN_NAME = "org.codehaus.groovy.vmplugin.v5.Java5";

+    private static VMPluging plugin;

+    static {

+        try {

+            ClassLoader.getSystemClassLoader().loadClass(JDK5_CLASSNAME_CHECK);

+            plugin = (VMPluging) VMPluginFactory.class.getClassLoader()

+                                 .loadClass(JDK5_PLUGIN_NAME).newInstance();

+        } catch(Exception ex) {

+            plugin = new Java4();

+        }

+    }

+    

+    public static VMPluging getPlugin() {

+        return plugin;

+    }

+    

+}

diff --git a/groovy/src/main/org/codehaus/groovy/vmplugin/VMPluging.java b/groovy/src/main/org/codehaus/groovy/vmplugin/VMPluging.java
new file mode 100644
index 0000000..86e86ba
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/vmplugin/VMPluging.java
@@ -0,0 +1,27 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.vmplugin;

+

+import org.codehaus.groovy.ast.ClassNode;

+

+/**

+ * interface to access VM version based actions

+ * @author Jochen Theodorou

+ */

+public interface VMPluging {

+

+    void setGenericsTypes(ClassNode c);

+}

diff --git a/groovy/src/main/org/codehaus/groovy/vmplugin/package.html b/groovy/src/main/org/codehaus/groovy/vmplugin/package.html
new file mode 100644
index 0000000..e51e284
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/vmplugin/package.html
@@ -0,0 +1,26 @@
+<!--
+    Copyright 2003-2007 the original author or authors.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!-- $Id: package.html 7254 2007-08-15 22:26:39Z user57 $ -->
+
+<html>
+    <body>
+        <p>
+            JVM version specific classes.
+        </p>
+    </body>
+</html>
+
diff --git a/groovy/src/main/org/codehaus/groovy/vmplugin/v4/Java4.java b/groovy/src/main/org/codehaus/groovy/vmplugin/v4/Java4.java
new file mode 100644
index 0000000..48d6015
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/vmplugin/v4/Java4.java
@@ -0,0 +1,32 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.codehaus.groovy.vmplugin.v4;

+

+import org.codehaus.groovy.ast.ClassNode;

+import org.codehaus.groovy.vmplugin.VMPluging;

+

+/**

+ * java 4 based functions

+ * @author Jochen Theodorou

+ *

+ */

+public class Java4 implements VMPluging {

+

+    public void setGenericsTypes(ClassNode c) {

+        return;        

+    }

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/vmplugin/v4/package.html b/groovy/src/main/org/codehaus/groovy/vmplugin/v4/package.html
new file mode 100644
index 0000000..1032588
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/vmplugin/v4/package.html
@@ -0,0 +1,26 @@
+<!--
+    Copyright 2003-2007 the original author or authors.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!-- $Id: package.html 7254 2007-08-15 22:26:39Z user57 $ -->
+
+<html>
+    <body>
+        <p>
+            Java 1.4 specific classes.
+        </p>
+    </body>
+</html>
+
diff --git a/groovy/src/main/org/codehaus/groovy/vmplugin/v5/Java5.java b/groovy/src/main/org/codehaus/groovy/vmplugin/v5/Java5.java
new file mode 100644
index 0000000..8041de0
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/vmplugin/v5/Java5.java
@@ -0,0 +1,109 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package org.codehaus.groovy.vmplugin.v5;

+

+import java.lang.reflect.*;

+

+import org.codehaus.groovy.GroovyBugError;

+import org.codehaus.groovy.ast.ClassNode;

+import org.codehaus.groovy.ast.GenericsType;

+import org.codehaus.groovy.vmplugin.VMPluging;

+import org.codehaus.groovy.ast.ClassHelper;

+

+/**

+ * java 5 based functions

+ * @author Jochen Theodorou

+ *

+ */

+public class Java5 implements VMPluging {

+

+    public void setGenericsTypes(ClassNode cn) {

+        TypeVariable[] tvs = cn.getTypeClass().getTypeParameters();

+        GenericsType[] gts = configureTypeVariable(tvs);

+        cn.setGenericsTypes(gts);

+        Object a = Enum.class;

+    }

+    

+    private GenericsType[] configureTypeVariable(TypeVariable[] tvs) {

+        if (tvs.length==0) return null;

+        GenericsType[] gts = new GenericsType[tvs.length];

+        for (int i = 0; i < tvs.length; i++) {

+            gts[i] = configureTypeVariableDefintion(tvs[i]);

+        }

+        return gts;

+    }

+    

+    private GenericsType configureTypeVariableDefintion(TypeVariable tv) {

+       ClassNode base = configureTypeVariableReference(tv);

+       Type[] tBounds = tv.getBounds();

+       if (tBounds.length==0) return new GenericsType(base);

+       ClassNode[] cBounds = new ClassNode[tBounds.length];

+       for (int i = 0; i < tBounds.length; i++) {

+           cBounds[i] = configureType(tBounds[i]);

+       }

+       GenericsType gt = new GenericsType(base,cBounds,null);

+       gt.setPlaceholder(true);

+       return gt;

+    }

+    

+    private ClassNode configureType(Type type) {

+        if (type instanceof WildcardType) {

+            return configureWildcardType((WildcardType) type);

+        } else if (type instanceof ParameterizedType) {

+            return configureParameterizedType((ParameterizedType) type);

+        } else if (type instanceof GenericArrayType) {

+            throw new GroovyBugError("Not yet implemented");

+        } else if (type instanceof TypeVariable) {

+            return configureTypeVariableReference((TypeVariable) type);

+        } else if (type instanceof Class) {

+            return ClassHelper.makeWithoutCaching((Class) type, false);

+        } else {

+            throw new GroovyBugError("unknown type: " + type + " := " + type.getClass());

+        }        

+    }

+    

+    private ClassNode configureWildcardType(WildcardType wildcardType) {

+        throw new GroovyBugError("Not yet implemented");

+    }

+    

+    private ClassNode configureParameterizedType(ParameterizedType parameterizedType) {

+        ClassNode base = configureType(parameterizedType.getRawType());

+        GenericsType[] gts = configureTypeArguments(parameterizedType.getActualTypeArguments());

+        base.setGenericsTypes(gts);

+        return base;

+    }

+    

+    private ClassNode configureTypeVariableReference(TypeVariable tv) {

+        ClassNode cn = ClassHelper.makeWithoutCaching(tv.getName());

+        cn.setGenericsPlaceHolder(true);

+        ClassNode cn2 = ClassHelper.makeWithoutCaching(tv.getName());

+        GenericsType[] gts = new GenericsType[]{new GenericsType(cn2)};

+        cn.setGenericsTypes(gts);

+        cn.setRedirect(ClassHelper.OBJECT_TYPE);

+        return cn;

+    }

+    

+    private GenericsType[] configureTypeArguments(Type[] ta) {

+        if (ta.length==0) return null;

+        GenericsType[] gts = new GenericsType[ta.length];

+        for (int i = 0; i < ta.length; i++) {

+            gts[i] = new GenericsType(configureType(ta[i]));

+        }

+        return gts;

+    }

+

+}

diff --git a/groovy/src/main/org/codehaus/groovy/vmplugin/v5/package.html b/groovy/src/main/org/codehaus/groovy/vmplugin/v5/package.html
new file mode 100644
index 0000000..53e8e18
--- /dev/null
+++ b/groovy/src/main/org/codehaus/groovy/vmplugin/v5/package.html
@@ -0,0 +1,26 @@
+<!--
+    Copyright 2003-2007 the original author or authors.
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<!-- $Id: package.html 7254 2007-08-15 22:26:39Z user57 $ -->
+
+<html>
+    <body>
+        <p>
+            Java 5 specific classes.
+        </p>
+    </body>
+</html>
+
diff --git a/groovy/src/native/base.c b/groovy/src/native/base.c
new file mode 100644
index 0000000..2200421
--- /dev/null
+++ b/groovy/src/native/base.c
@@ -0,0 +1,85 @@
+#ifdef __APPLE__
+#ifdef __MACH__
+#define MACOSX
+#define UNIX
+#endif
+#endif
+
+#ifdef __linux__
+#define UNIX
+#define LINUX
+#endif
+
+#ifdef UNIX
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#endif
+
+#ifdef LINUX
+#include <unistd.h>
+#include <linux/limits.h>
+#include <malloc.h>
+#endif
+
+#ifdef MACOSX
+#include <mach-o/dyld.h>
+#include <sys/param.h>
+#endif
+
+int main(int argc, char* argv[]) {
+
+  // Get the location of the executable -- platform specific
+#ifdef UNIX
+  char *jarexe = malloc((MAXPATHLEN+2)*sizeof(char));
+#endif
+
+#ifdef MACOSX
+  uint32_t length = MAXPATHLEN + 2;
+  while (_NSGetExecutablePath((char*)jarexe, &length) == -1) {
+    free(jarexe);
+    jarexe = malloc((length)*sizeof(char));
+  }
+#endif
+
+#ifdef LINUX
+  char temp[PATH_MAX];
+  sprintf(temp, "/proc/%d/exe", getpid());
+  realpath(temp, jarexe);
+#endif
+
+  // Setup the command line.
+  // TODO: Add -J support for passing Java options
+  char* argv2[argc+2];
+  argv2[0] = "java";
+  argv2[1] = JAVA_CLASS_NAME;
+
+  // Setup the CLASSPATH environment
+  char *CLASSPATH = (char*) getenv("CLASSPATH");
+  if (!CLASSPATH) CLASSPATH="";
+  char *NEWCLASSPATH = (char*) calloc(strlen(CLASSPATH)+1+strlen(jarexe)+1, sizeof(char)); // +1 for :, +1 for null terminator
+  sprintf(NEWCLASSPATH, "%s:%s", jarexe, CLASSPATH);
+  setenv("CLASSPATH", NEWCLASSPATH, 1);
+
+  // Setup the rest of the command line that was passed in.
+  // TODO: This will also be affected by -J options
+  int i;
+  for (i = 1; i < argc; i++) {
+    argv2[i+1] = argv[i];
+  }
+  argv2[argc+1] = 0;
+
+  // Execute java
+#ifdef UNIX
+  free(jarexe);
+  execvp("java", argv2);
+#endif
+
+  // Report if the exec fails
+  printf("Cannot execute '");
+  for (i = 0; i < argc+1; i ++) { printf("%s ", argv2[i]); }
+#ifdef UNIX
+  printf("', caused by error: %d\n.  In order to run %s you must have the Java VM you want to use in your PATH", errno, argv[0]);
+#endif
+}
diff --git a/groovy/src/tck/build.xml b/groovy/src/tck/build.xml
new file mode 100644
index 0000000..b2d8316
--- /dev/null
+++ b/groovy/src/tck/build.xml
@@ -0,0 +1,202 @@
+<?xml version="1.0"?>
+<project name="tck" default="default">
+
+  <!-- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -->
+  <!--                   compile settings                -->
+  <!-- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -->
+  <property name="build.debug" value="on"/>
+  
+  <!-- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -->
+  <!--                   directories                     -->
+  <!-- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -->
+  <property name="src.dir" value="src"/>
+  <property name="build.dir" value="build"/>
+  <property name="build.classes.dir" value="${build.dir}/classes"/>
+  <property name="dist.dir" value="dist"/>
+
+  <!-- javadoc properties -->
+  <property name="javadoc.dir" value="doc"/>
+  <property name="javadoc.packages" value="org.codehaus.groovy.*"/>
+
+  <!-- junit properties -->
+  <property name="junit.style.dir" value="styles"/>
+  <property name="testcase.src.dir" value="test"/>
+  <property name="generated.test.src.dir" value="gentest"/>
+  
+  <property name="test.classes.dir" value="${build.dir}/test/classes"/>
+
+  <!-- TODO: here you specify where the classes to test reside -->
+  <property name="items.under.test.dir" value="../../target/install/lib"/>
+  
+  <property name="test.reports.dir" value="reports"/>
+  
+  <!-- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -->
+  <!--                  targets                          -->
+  <!-- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -->
+  <target name="default" 
+          depends="clean,test-compile" 
+          description="default: run the groovy test suite"
+  />
+  
+  <target name="all" 
+          depends="test,dist" 
+          description="build and test everything"
+  />
+
+  <target name="clean" description="remove all built files">
+    <delete dir="${javadoc.dir}" />
+    <delete dir="${build.dir}" />
+    <delete dir="${dist.dir}" />
+    <delete dir="${generated.test.src.dir}" />
+  </target>
+
+  <target name="dist" 
+          depends="compile" 
+          description="create distributables (jars etc)">
+    <mkdir dir="${dist.dir}" />
+    <jar jarfile="${dist.dir}/tck.jar" 
+         manifest="${src.dir}/manifest.txt">
+      <fileset dir="${build.classes.dir}"/>
+    </jar>
+  </target>
+
+  <target name="docs" depends="init" description="generate documentation">
+    <mkdir dir="${javadoc.dir}" />
+    <javadoc sourcepath="${src.dir}" 
+             destdir="${javadoc.dir}" 
+             packagenames="${javadoc.packages}"
+             use="true"
+             windowtitle="tck"
+             private="true"/>
+  </target>
+
+  <target name="run" depends="compile" description="compile and run">
+    <java classname="org.codehaus.groovy.tck.GenerateTestCases" 
+          fork="yes" 
+          classpathref="project.classpath"/>
+  </target>
+
+  <target name="plain-test"
+          depends="test-compile" 
+          description="run all test cases">
+    <junit haltonfailure="true" fork="no">
+      <classpath refid="project.classpath"/>
+      <formatter type="plain" usefile="false"/>
+      <formatter type="xml" usefile="true"/>
+      <batchtest todir="${test.reports.dir}">
+        <fileset dir="${test.classes.dir}">
+          <include name="**/*Test.class" />
+        </fileset>
+      </batchtest>
+    </junit>
+  </target>
+        
+  <target name="test" 
+          depends="test-compile" 
+          description="HTML output of test cases">
+    <mkdir dir="${test.reports.dir}"/>
+    <junit haltonfailure="false" fork="no">
+      <classpath refid="project.classpath"/>
+      <formatter type="xml" usefile="true"/>
+
+      <batchtest todir="${test.reports.dir}">
+        <fileset dir="${test.classes.dir}">
+            <include name="**/*Test.class" /> 
+        </fileset>
+      </batchtest>
+
+    </junit>
+
+    <junitreport todir="${test.reports.dir}">
+      <fileset dir="${test.reports.dir}">
+        <include name="TEST-*.xml"/>
+      </fileset>
+      <report format="frames" styledir="${junit.style.dir}" todir="${test.reports.dir}"/>
+    </junitreport>
+    <echo>
++-------------------------+
+| open reports/index.html |
++-------------------------+
+</echo>
+  </target>
+
+
+
+  <target name="compile" depends="init" description="compile java and groovy sources">
+    <mkdir dir="${build.classes.dir}" />
+
+    <groovyc destdir="${build.classes.dir}"
+             srcdir="${src.dir}"
+             listfiles="true">
+        <classpath refid="project.classpath"/>
+    </groovyc>
+
+    <javac srcdir="${src.dir}"
+           destdir="${build.classes.dir}"
+           classpathref="project.classpath"
+           debug="${build.debug}"
+           deprecation="on"/>
+  </target>
+
+    <target name="generate" depends="dist" description="generate test cases">
+      <mkdir dir="${generated.test.src.dir}" />
+      <taskdef name="gentests" classname="org.codehaus.groovy.tck.GenerateTestCases" classpathref="project.classpath"/>
+
+      <gentests destdir="${generated.test.src.dir}"
+                srcdir="${testcase.src.dir}"
+                listfiles="true"/>
+    </target>
+
+  <target name="test-compile" depends="generate" description="compile tests">
+    <mkdir dir="${test.classes.dir}" />
+    <javac srcdir="${generated.test.src.dir}"
+           destdir="${test.classes.dir}" 
+           classpathref="project.classpath" 
+           debug="${build.debug}" 
+           deprecation="on">
+    </javac>
+  </target>
+
+  <target name="independent-test-compile" depends="generate" description="compile tests">
+    <mkdir dir="${test.classes.dir}" />
+    <javac srcdir="${generated.test.src.dir}"
+           destdir="${test.classes.dir}" 
+           classpathref="project.classpath" 
+           debug="${build.debug}" 
+           deprecation="on">
+    </javac>
+  </target>
+
+  <target name="init" depends="init.sub.build,init.independent.build">
+    <tstamp/>
+  </target>
+
+  <!-- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -->
+  <!--                     Datatypes                     -->
+  <!-- -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -->
+
+  <!-- this target is performed we are within a maven build -->
+  <target name="init.sub.build" if="maven.compile.source">
+
+    <path id="project.classpath">
+      <pathelement location="${test.classes.dir}"/>
+      <path refid="passed.classpath"/>
+      <pathelement location="${build.classes.dir}"/>
+    </path>
+  
+  </target>
+
+  <!-- this target is performed when ant is called from command line -->
+  <target name="init.independent.build" unless="maven.compile.source">
+
+    <path id="project.classpath">
+      <pathelement location="${test.classes.dir}"/>
+      <pathelement location="${build.classes.dir}"/>
+      <fileset dir="${items.under.test.dir}">
+        <include name="**/*.jar" />
+      </fileset>
+    </path>
+
+  </target>
+       
+</project>
diff --git a/groovy/src/tck/src/manifest.txt b/groovy/src/tck/src/manifest.txt
new file mode 100644
index 0000000..2dd0636
--- /dev/null
+++ b/groovy/src/tck/src/manifest.txt
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Main-Class: org.codehaus.groovy.tck.GenerateTestCases
diff --git a/groovy/src/tck/src/org/codehaus/groovy/tck/BatchGenerate.groovy b/groovy/src/tck/src/org/codehaus/groovy/tck/BatchGenerate.groovy
new file mode 100644
index 0000000..af43c2c
--- /dev/null
+++ b/groovy/src/tck/src/org/codehaus/groovy/tck/BatchGenerate.groovy
@@ -0,0 +1,86 @@
+/**
+ * @author Jeremy Rayner
+ */
+package org.codehaus.groovy.tck
+
+import java.io.File;
+
+class BatchGenerate {
+    def generator;
+    def srcDirPath;
+    def targetDir;
+    def srcEncoding;
+    def srcs;
+    def spew
+
+    public BatchGenerate() {
+        generator = new TestGenerator();
+        // verbose = false;
+        spew = true;
+        srcDirPath = "./";
+    }
+
+    public void setSrcdirPath(String pathName) {
+        if (spew) {println("srcDir:${pathName}") }
+        srcDirPath = pathName;
+    }
+
+    public void setTargetDirectory(File destDir) {
+        if (spew) { println("destDir:${destDir}") }
+        targetDir = destDir;
+    }
+
+    public void setSourceEncoding(String encoding) {
+        if (spew) { println("encoding:${encoding}") }
+        srcEncoding = encoding;
+    }
+
+    public void addSources( File[] compileList ) {
+        if (spew) { println("compileList:${compileList}") }
+        srcs = compileList
+    }
+
+    public void setVerbose(boolean verbose) {
+        spew = verbose
+    }
+
+    public void compile() {
+        if (spew) { println("compile()") }
+
+
+        for (src in srcs) {
+            println( src )
+            // mung the ${test.src.dir}/gls/ch14/s4 path into ${dest.dir}/gls/ch14/s4
+            // first determine the relative path e.g. gls/ch14/s4
+            def relativeSrcFilePathAndName = src.getAbsolutePath().substring(srcDirPath.length() + 1)
+            def relativeSrcFileNameStartIndex = relativeSrcFilePathAndName.lastIndexOf(File.separator);
+            def relativeOutputPath = ""
+            if (relativeSrcFileNameStartIndex >= 0) {
+                relativeOutputPath = relativeSrcFilePathAndName.substring(0,relativeSrcFileNameStartIndex);
+            }
+
+            // then determine the absolute output path
+            def ghostOutputFile = new File(targetDir, relativeSrcFilePathAndName)
+            def ghostOutputFilePath = ghostOutputFile.getAbsolutePath()
+            def fileNameStartIndex = ghostOutputFilePath.lastIndexOf(File.separator);
+            def realOutputPath = ghostOutputFilePath.substring(0,fileNameStartIndex);
+
+            // mkdir if does not exist
+            File directory = new File(realOutputPath)
+            if (directory != null && !directory.exists()) {
+                directory.mkdirs();
+            }
+
+            // generate a suitable java file to put there
+            def fileStem = src.name.tokenize(".")[0]
+            def targetFileName = "${fileStem}Test.java"
+            def anOutputFile = new File(realOutputPath, targetFileName)
+
+            System.out.println("generating " + targetFileName)
+            def someOutputText = generator.generate(relativeOutputPath, targetDir, src.name,src.text);
+            if (someOutputText != null && someOutputText != "") {
+                anOutputFile.write(someOutputText);
+            }
+        }
+    }
+}
diff --git a/groovy/src/tck/src/org/codehaus/groovy/tck/ClassicGroovyTestGeneratorHelper.java b/groovy/src/tck/src/org/codehaus/groovy/tck/ClassicGroovyTestGeneratorHelper.java
new file mode 100644
index 0000000..3c6be2b
--- /dev/null
+++ b/groovy/src/tck/src/org/codehaus/groovy/tck/ClassicGroovyTestGeneratorHelper.java
@@ -0,0 +1,93 @@
+package org.codehaus.groovy.tck;
+
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import junit.framework.TestResult;
+// Jsr parser
+// @todo - refactor pulling generic parser interface up
+import org.codehaus.groovy.antlr.parser.GroovyLexer;
+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
+
+// codehaus reference implementation usage
+// @todo - remove classic references from the TCK
+import org.codehaus.groovy.control.CompilerConfiguration;
+import groovy.lang.GroovyShell;
+import antlr.RecognitionException;
+
+/** Helper methods for generated TCK test case using new JSR parser and classic groovy AST and evaluation */
+public class ClassicGroovyTestGeneratorHelper implements TestGeneratorHelper {
+
+    /** evaluate the source text against the classic AST with the JSR parser implementation*/
+    public Object evaluate(String theSrcText, String testName) throws Exception {
+        parse(theSrcText, testName); // fail early with a direct message if possible')
+        GroovyShell groovy = new GroovyShell(new CompilerConfiguration());
+        return groovy.run(theSrcText, "main", new ArrayList());
+    }
+
+    /** run the JSR parser implementation over the supplied source text*/
+    public void parse(String theSrcText, String testName) throws Exception {
+        System.out.println("-------------------------------");
+        System.out.println("  " + testName);
+        System.out.println("-------------------------------");
+        try {
+            Reader reader = new BufferedReader(new StringReader(theSrcText));
+            GroovyRecognizer recognizer = GroovyRecognizer.make(reader);
+            recognizer.compilationUnit();
+            System.out.println(decorateWithLineNumbers(theSrcText));
+
+        } catch (RecognitionException parseException) {
+            System.out.println(decorateWithLineNumbersAndErrorMessage(theSrcText,parseException));
+            throw parseException;
+        }
+        System.out.println("-------------------------------");
+
+    }
+
+    private String decorateWithLineNumbersAndErrorMessage(String theSrcText, RecognitionException parseException) {
+        try {
+            BufferedReader reader = new BufferedReader(new StringReader(theSrcText));
+            String line = null;
+            StringBuffer numberedSrcTextBuffer = new StringBuffer();
+            int lineNum = 1;
+            while ((line = reader.readLine() ) != null) {
+                numberedSrcTextBuffer.append(lineNum);
+                numberedSrcTextBuffer.append("\t");
+                numberedSrcTextBuffer.append(line);
+                numberedSrcTextBuffer.append(lineSep);
+
+                if (parseException != null) {
+                    if (lineNum == parseException.getLine()) {
+                        StringBuffer padding = new StringBuffer("\t");
+                        for (int col=1; col<parseException.getColumn();col++) {
+                            padding.append(" ");
+                        }
+                        numberedSrcTextBuffer.append(padding);
+                        numberedSrcTextBuffer.append("^");
+                        numberedSrcTextBuffer.append(lineSep);
+                        numberedSrcTextBuffer.append("ERROR:");
+                        numberedSrcTextBuffer.append(lineSep);
+                        numberedSrcTextBuffer.append(parseException.getMessage());
+                        numberedSrcTextBuffer.append(lineSep);
+                        numberedSrcTextBuffer.append(lineSep);
+                    }
+                }
+
+                lineNum++;
+
+            }
+            theSrcText = numberedSrcTextBuffer.toString();
+        } catch (IOException e) {
+            //ignore
+        }
+        return theSrcText;
+    }
+
+    private String decorateWithLineNumbers(String theSrcText) {
+        return decorateWithLineNumbersAndErrorMessage(theSrcText,null);
+    }
+
+    protected String lineSep = System.getProperty("line.separator");
+}
diff --git a/groovy/src/tck/src/org/codehaus/groovy/tck/GenerateTestCases.java b/groovy/src/tck/src/org/codehaus/groovy/tck/GenerateTestCases.java
new file mode 100644
index 0000000..9456fbf
--- /dev/null
+++ b/groovy/src/tck/src/org/codehaus/groovy/tck/GenerateTestCases.java
@@ -0,0 +1,387 @@
+/**
+ * @author Jeremy Rayner
+ */
+package org.codehaus.groovy.tck;
+
+import java.io.*;
+import java.nio.charset.Charset;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.*;
+import org.apache.tools.ant.util.*;
+
+/**
+ * Generates test files. This task can take the following
+ * arguments:
+ * <ul>
+ * <li>sourcedir
+ * <li>destdir
+ * </ul>
+ * Both are required.
+ * <p>
+ * When this task executes, it will recursively scan the sourcedir
+ * looking for source files to expand into testcases. This task makes its
+ * generation decision based on timestamp.
+ *
+ * Based heavily on the Javac implementation in Ant
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+public class GenerateTestCases extends MatchingTask {
+
+    private BatchGenerate batchGenerate = new BatchGenerate();
+    private Path src;
+    private File destDir;
+    private Path compileClasspath;
+    private Path compileSourcepath;
+    private String encoding;
+
+    protected boolean failOnError = true;
+    protected boolean listFiles = false;
+    protected File[] compileList = new File[0];
+
+    public GenerateTestCases() {
+    }
+
+    /**
+     * Adds a path for source compilation.
+     *
+     * @return a nested src element.
+     */
+    public Path createSrc() {
+        if (src == null) {
+            src = new Path(getProject());
+        }
+        return src.createPath();
+    }
+
+    /**
+     * Recreate src.
+     *
+     * @return a nested src element.
+     */
+    protected Path recreateSrc() {
+        src = null;
+        return createSrc();
+    }
+
+    /**
+     * Set the source directories to find the source Java files.
+     * @param srcDir the source directories as a path
+     */
+    public void setSrcdir(Path srcDir) {
+        if (src == null) {
+            src = srcDir;
+        }
+        else {
+            src.append(srcDir);
+        }
+        batchGenerate.setSrcdirPath(src.toString());
+    }
+
+    /**
+     * Gets the source dirs to find the source java files.
+     * @return the source directorys as a path
+     */
+    public Path getSrcdir() {
+        return src;
+    }
+
+    /**
+     * Set the destination directory into which the Java source
+     * files should be compiled.
+     * @param destDir the destination director
+     */
+    public void setDestdir(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * Enable verbose compiling which will display which files
+     * are being compiled
+     * @param verbose
+     */
+    public void setVerbose(boolean verbose) {
+        batchGenerate.setVerbose( verbose );
+    }
+
+    /**
+     * Gets the destination directory into which the java source files
+     * should be compiled.
+     * @return the destination directory
+     */
+    public File getDestdir() {
+        return destDir;
+    }
+
+    /**
+     * Set the sourcepath to be used for this compilation.
+     * @param sourcepath the source path
+     */
+    public void setSourcepath(Path sourcepath) {
+        if (compileSourcepath == null) {
+            compileSourcepath = sourcepath;
+        }
+        else {
+            compileSourcepath.append(sourcepath);
+        }
+    }
+
+    /**
+     * Gets the sourcepath to be used for this compilation.
+     * @return the source path
+     */
+    public Path getSourcepath() {
+        return compileSourcepath;
+    }
+
+    /**
+     * Adds a path to sourcepath.
+     * @return a sourcepath to be configured
+     */
+    public Path createSourcepath() {
+        if (compileSourcepath == null) {
+            compileSourcepath = new Path(getProject());
+        }
+        return compileSourcepath.createPath();
+    }
+
+    /**
+     * Adds a reference to a source path defined elsewhere.
+     * @param r a reference to a source path
+     */
+    public void setSourcepathRef(Reference r) {
+        createSourcepath().setRefid(r);
+    }
+
+    /**
+     * Set the classpath to be used for this compilation.
+     *
+     * @param classpath an Ant Path object containing the compilation classpath.
+     */
+    public void setClasspath(Path classpath) {
+        if (compileClasspath == null) {
+            compileClasspath = classpath;
+        }
+        else {
+            compileClasspath.append(classpath);
+        }
+    }
+
+    /**
+     * Gets the classpath to be used for this compilation.
+     * @return the class path
+     */
+    public Path getClasspath() {
+        return compileClasspath;
+    }
+
+    /**
+     * Adds a path to the classpath.
+     * @return a class path to be configured
+     */
+    public Path createClasspath() {
+        if (compileClasspath == null) {
+            compileClasspath = new Path(getProject());
+        }
+        return compileClasspath.createPath();
+    }
+
+    /**
+     * Adds a reference to a classpath defined elsewhere.
+     * @param r a reference to a classpath
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    public String createEncoding() {
+        if (encoding == null) {
+            encoding = System.getProperty("file.encoding");
+        }
+        return encoding;
+    }
+
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * If true, list the source files being handed off to the compiler.
+     * @param list if true list the source files
+     */
+    public void setListfiles(boolean list) {
+        listFiles = list;
+    }
+
+    /**
+     * Get the listfiles flag.
+     * @return the listfiles flag
+     */
+    public boolean getListfiles() {
+        return listFiles;
+    }
+
+    /**
+     * Indicates whether the build will continue
+     * even if there are compilation errors; defaults to true.
+     * @param fail if true halt the build on failure
+     */
+    public void setFailonerror(boolean fail) {
+        failOnError = fail;
+    }
+
+    /**
+     * @param proceed inverse of failoferror
+     */
+    public void setProceed(boolean proceed) {
+        failOnError = !proceed;
+    }
+
+    /**
+     * Gets the failonerror flag.
+     * @return the failonerror flag
+     */
+    public boolean getFailonerror() {
+        return failOnError;
+    }
+
+    /**
+     * Executes the task.
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        checkParameters();
+        resetFileLists();
+
+        // scan source directories and dest directory to build up
+        // compile lists
+        String[] list = src.list();
+        for (int i = 0; i < list.length; i++) {
+            File srcDir = getProject().resolveFile(list[i]);
+            if (!srcDir.exists()) {
+                throw new BuildException("srcdir \"" + srcDir.getPath() + "\" does not exist!", getLocation());
+            }
+
+            DirectoryScanner ds = this.getDirectoryScanner(srcDir);
+            String[] files = ds.getIncludedFiles();
+
+            scanDir(srcDir, destDir != null ? destDir : srcDir, files);
+        }
+
+        compile();
+    }
+
+    /**
+     * Clear the list of files to be compiled and copied..
+     */
+    protected void resetFileLists() {
+        compileList = new File[0];
+    }
+
+    /**
+     * Scans the directory looking for source files to be compiled.
+     * The results are returned in the class variable compileList
+     *
+     * @param srcDir   The source directory
+     * @param destDir  The destination directory
+     * @param files    An array of filenames
+     */
+    protected void scanDir(File srcDir, File destDir, String[] files) {
+        GlobPatternMapper m = new GlobPatternMapper();
+        m.setFrom("*");
+        m.setTo("*.html");
+        SourceFileScanner sfs = new SourceFileScanner(this);
+        File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);
+
+        if (newFiles.length > 0) {
+            File[] newCompileList = new File[compileList.length + newFiles.length];
+            System.arraycopy(compileList, 0, newCompileList, 0, compileList.length);
+            System.arraycopy(newFiles, 0, newCompileList, compileList.length, newFiles.length);
+            compileList = newCompileList;
+        }
+    }
+
+    /**
+     * Gets the list of files to be compiled.
+     * @return the list of files as an array
+     */
+    public File[] getFileList() {
+        return compileList;
+    }
+
+    protected void checkParameters() throws BuildException {
+        if (src == null) {
+            throw new BuildException("srcdir attribute must be set!", getLocation());
+        }
+        if (src.size() == 0) {
+            throw new BuildException("srcdir attribute must be set!", getLocation());
+        }
+
+        if (destDir != null && !destDir.isDirectory()) {
+            throw new BuildException(
+                "destination directory \"" + destDir + "\" does not exist " + "or is not a directory",
+                getLocation());
+        }
+
+        if (encoding != null && !Charset.isSupported(encoding)) {
+            throw new BuildException("encoding \"\" not supported");
+        }
+    }
+
+    protected void compile() {
+        if (compileList.length > 0) {
+            log(
+                "Generating Tests "
+                    + compileList.length
+                    + " source file"
+                    + (compileList.length == 1 ? "" : "s")
+                    + (destDir != null ? " to " + destDir : ""));
+
+            if (listFiles) {
+                for (int i = 0; i < compileList.length; i++) {
+                    String filename = compileList[i].getAbsolutePath();
+                    log(filename);
+                }
+            }
+
+            try {
+                Path classpath = getClasspath();
+                if (classpath != null) {
+                    //@todo - is this useful?
+                    //batchOfBiscuits.setClasspath(classpath.toString());
+                }
+                batchGenerate.setTargetDirectory(destDir);
+
+                if (encoding != null) {
+                    batchGenerate.setSourceEncoding(encoding);
+                }
+
+                batchGenerate.addSources( compileList );
+                batchGenerate.compile( );
+            }
+            catch (Exception e) {
+
+                StringWriter writer = new StringWriter();
+                //@todo --
+                e.printStackTrace();
+                //new ErrorReporter( e, false ).write( new PrintWriter(writer) );
+                String message = writer.toString();
+
+                if (failOnError) {
+                    throw new BuildException(message, e, getLocation());
+                }
+                else {
+                    log(message, Project.MSG_ERR);
+                }
+
+            }
+        }
+    }
+}
diff --git a/groovy/src/tck/src/org/codehaus/groovy/tck/TestGenerator.groovy b/groovy/src/tck/src/org/codehaus/groovy/tck/TestGenerator.groovy
new file mode 100644
index 0000000..0f808f5
--- /dev/null
+++ b/groovy/src/tck/src/org/codehaus/groovy/tck/TestGenerator.groovy
@@ -0,0 +1,209 @@
+/**
+ * This will take a groovy test file and turn it into a Java TestCase
+ * @author Jeremy Rayner
+ */
+package org.codehaus.groovy.tck
+import java.io.*;
+class TestGenerator{
+    public String generate(realOutputPath, targetDir, srcName,srcText) {
+//        System.out.println('single \\\\')
+//        System.out.println("double \\\\")
+        srcText = srcText.replaceAll('\\\\','\\\\\\\\') // need to escape a slash with slash slash
+
+        def resultWriter = new StringWriter()
+        def result = new PrintWriter(resultWriter)
+
+        def fileName = srcName
+        def fileStem = fileName.tokenize(".")[0]
+
+        def comments = scrape(srcText," * ",".") // Take the first javadoc sentence, if it exists, for use as method name
+        if (comments == null || comments[0] == null) {comments = [""]}
+        def behaviourDescription = comments[0].trim()
+
+        if ("" != realOutputPath) {
+            def realOutputPackage = ''
+            if (File.separator != '\\')
+                realOutputPackage = realOutputPath.replaceAll(File.separator,'.')
+            else
+                realOutputPackage = realOutputPath.replaceAll('\\\\','.')
+            result.println("package ${realOutputPackage};")
+        }
+        result.println("import junit.framework.*;")
+        result.println("import org.codehaus.groovy.tck.*;")
+
+        result.println("public class ${fileStem}Test extends TestCase {")
+
+        //methodName = turnSentenceIntoJavaName(behaviourDescription)
+        def methodName = ""
+        methodName = "test${methodName}"
+
+        // test for the source 'as is'
+        printCommonTestMethodStart(result, "${methodName}Pass",srcText)
+        result.println('        Object result = helper.evaluate(srcBuffer.toString(),"' + "${methodName}Pass" + '");')
+        result.println('        if (result instanceof TestResult) {')
+        result.println('            TestResult testResult = (TestResult)result;')
+        result.println('            if (testResult.errorCount() > 0) {')
+        result.println('                TestFailure firstTestFailure = (TestFailure)testResult.errors().nextElement();')
+        result.println('                throw firstTestFailure.thrownException();')
+        result.println('            }')
+        result.println('            if (testResult.failureCount() > 0) {')
+        result.println('                AssertionFailedError firstFailure = (AssertionFailedError)(testResult.failures().nextElement());')
+        result.println('                throw firstFailure;')
+        result.println('            }')
+        result.println('        }')
+        result.println("    }")
+
+        // test for each of the '@pass' alternatives
+        def passAlternatives = generateAlternatives(srcText,"@pass")
+
+        passAlternatives.eachWithIndex{anAlternative,i ->
+            printCommonTestMethodStart(result, "${methodName}Pass${i+1}",anAlternative[0]);
+            result.println('        Object result = helper.evaluate(srcBuffer.toString(),"' + "${methodName}Pass${i+1}" + '");')
+            result.println('        if (result instanceof TestResult) {')
+            result.println('            TestResult testResult = (TestResult)result;')
+            result.println('            if (testResult.errorCount() > 0) {')
+            result.println('                TestFailure firstTestFailure = (TestFailure)testResult.errors().nextElement();')
+            result.println('                throw firstTestFailure.thrownException();')
+            result.println('            }')
+            result.println('            if (testResult.failureCount() > 0) {')
+            result.println('                AssertionFailedError firstFailure = (AssertionFailedError)(testResult.failures().nextElement());')
+            result.println('                throw firstFailure;')
+            result.println('            }')
+            result.println('        }')
+
+            result.println("    }")
+        }
+
+        // test for each of the '@fail:parse' alternatives
+        def failureToParseAlternatives = generateAlternatives(srcText,"@fail:parse")
+        failureToParseAlternatives.eachWithIndex{anAlternative,i ->
+            printCommonTestMethodStart(result, "${methodName}FailParse${i+1}",anAlternative[0]);
+            result.println("        try {")
+            result.println('            helper.parse(srcBuffer.toString(),"' + "${methodName}FailParse${i+1}" + '");')
+
+
+            result.println('            fail("This line did not fail to parse: ' + anAlternative[1] + '");')
+            result.println("        } catch (Exception e) {")
+            result.println("            // ignore an exception as that is what we're hoping for in this case.")
+            result.println("        }")
+            result.println("    }")
+        }
+
+        // test for each of the '@fail' alternatives, i.e. without being followed by a colon
+        def failureAlternatives = generateAlternatives(srcText,"@fail(?!:)")
+        failureAlternatives.eachWithIndex{anAlternative,i ->
+            printCommonTestMethodStart(result, "${methodName}Fail${i+1}",anAlternative[0]);
+            result.println("        try {")
+            result.println('            helper.evaluate(srcBuffer.toString(),"' + "${methodName}Fail${i+1}" + '");')
+            result.println('            fail("This line did not fail to evaluate: ' + anAlternative[1] + '");')
+            result.println("        } catch (Exception e) {")
+            result.println("            // ignore an exception as that is what we're hoping for in this case.")
+            result.println("        }")
+            result.println("    }")
+        }
+        result.println('    protected String lineSep = System.getProperty("line.separator");')
+        result.println('    protected TestGeneratorHelper helper = new ClassicGroovyTestGeneratorHelper();')
+        result.println("}")
+
+        return resultWriter.toString()
+    }
+
+
+    // -- useful stuff
+
+    /**
+     * Creates alternative versions of the given source, one for each end of line comment tag e.g. //@fail
+     * will remove the double slash from the start of each of the matching line.
+     * e.g. src text of...
+     * <pre>
+     *     // a = 1 // @fail
+     *     // b = 2 // @fail
+     * </pre>
+     * will return
+     * <pre>
+     * [ "a = 1 // @fail NLS // b = 2 // @fail",
+     *   "// a = 1 // @fail NLS b = 2 // @fail" ]
+     * </pre>
+     *
+     */
+    List generateAlternatives(String srcText, String tag) {
+        def alternatives = []
+        def m = java.util.regex.Pattern.compile("//(.*?//\\s*" + tag + "\\S*)\\s").matcher(srcText)
+        while (m.find()) {
+            def foundText = m.group(1)
+            def uncommentedSrcText = (srcText.substring(0,m.start()) + "  " + srcText.substring(m.start() + 2))
+            alternatives << [uncommentedSrcText, foundText.replaceAll('"', '\\\\"')]
+        }
+        return alternatives
+    }
+
+
+    /**
+     * Common setup code for each test method
+     */
+    void printCommonTestMethodStart(result, fullMethodName,someSrcText) {
+        def buffer = new java.io.StringReader(someSrcText)
+
+        result.println("    public void ${fullMethodName}() throws Throwable {")
+        result.println("        StringBuffer srcBuffer = new StringBuffer();")
+
+        // append each line to the buffer
+        buffer.eachLine {line ->
+            // escape double quotes
+            line = line.replaceAll('"','\\\\"')
+            result.println ('        srcBuffer.append("' + line + '").append(lineSep);')
+        }
+    }
+
+    /**
+     * Converts the given sentence into a Java style name like TheQuickBrownFox
+     */
+    String turnSentenceIntoJavaName(String sentence) {
+        //uppercase each word and remove spaces to give camel case
+        def tokens = sentence.tokenize(" ,;");
+        def methodName = ""
+        for (t in tokens) {
+            if (t.size() > 1) {
+                methodName += ( t[0].toUpperCase() + t[1..<t.size()].toLowerCase() )
+            } else if (t.size() == 1) {
+                methodName += t[0].toUpperCase()
+            }
+        }
+
+        //remove nonalphanumeric characters
+        methodName = methodName.replaceAll("[^A-Za-z0-9]","")
+
+        return methodName
+    }
+
+    /**
+     * Fetches a list of all the occurances of text between a string delimiter.
+     */
+    List scrape(String txt, String tag) {
+        return scrape(txt,tag,tag)
+    }
+
+    /**
+     * Fetches a list of all the occurances of text between two string delimiters (tags).
+     */
+    List scrape(String txt, String openTag, String closeTag) {
+        def i = 0; def j = 0; def k = 0;
+        def contents = []
+        if (txt != null) {
+            while (i> -1 && k > -1) {
+              i = txt.indexOf(openTag,k)
+                if (i > -1) {
+                  j = i + openTag.length()
+                    if (j > -1) {
+                      k = txt.indexOf(closeTag,j)
+                        if (k > -1) {
+                          contents << txt.substring(j,k)
+                        }
+                    }
+                }
+            }
+        }
+        return contents
+    }
+
+}
diff --git a/groovy/src/tck/src/org/codehaus/groovy/tck/TestGeneratorHelper.java b/groovy/src/tck/src/org/codehaus/groovy/tck/TestGeneratorHelper.java
new file mode 100644
index 0000000..7059f62
--- /dev/null
+++ b/groovy/src/tck/src/org/codehaus/groovy/tck/TestGeneratorHelper.java
@@ -0,0 +1,8 @@
+package org.codehaus.groovy.tck;
+
+/** Helper methods used by generated TCK test cases */
+
+public interface TestGeneratorHelper {
+    Object evaluate(String theSrcText, String testName) throws Exception;
+    void parse(String theSrcText, String testName) throws Exception;
+}
diff --git a/groovy/src/tck/styles/junit-frames.xsl b/groovy/src/tck/styles/junit-frames.xsl
new file mode 100644
index 0000000..c208c5a
--- /dev/null
+++ b/groovy/src/tck/styles/junit-frames.xsl
@@ -0,0 +1,723 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="http://xml.apache.org/xalan/redirect"
+    xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils"
+    extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<xsl:decimal-format decimal-separator="." grouping-separator=","/>
+<!--
+   Copyright 2001-2004 The Apache Software Foundation
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+ -->
+
+<!--
+
+ Sample stylesheet to be used with Ant JUnitReport output.
+
+ It creates a set of HTML files a la javadoc where you can browse easily
+ through all packages and classes.
+
+ @author Stephane Bailliez <a href="mailto:sbailliez@apache.org"/>
+ @author Erik Hatcher <a href="mailto:ehatcher@apache.org"/>
+ @author Martijn Kruithof <a href="mailto:martijn@kruithof.xs4all.nl"/>
+
+-->
+<xsl:param name="output.dir" select="'.'"/>
+
+
+<xsl:template match="testsuites">
+    <!-- create the index.html -->
+    <redirect:write file="{$output.dir}/index.html">
+        <xsl:call-template name="index.html"/>
+    </redirect:write>
+
+    <!-- create the stylesheet.css -->
+    <redirect:write file="{$output.dir}/stylesheet.css">
+        <xsl:call-template name="stylesheet.css"/>
+    </redirect:write>
+
+    <!-- create the overview-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-summary.html">
+        <xsl:apply-templates select="." mode="overview.packages"/>
+    </redirect:write>
+
+    <!-- create the all-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-frame.html">
+        <xsl:apply-templates select="." mode="all.packages"/>
+    </redirect:write>
+
+    <!-- create the all-classes.html at the root -->
+    <redirect:write file="{$output.dir}/allclasses-frame.html">
+        <xsl:apply-templates select="." mode="all.classes"/>
+    </redirect:write>
+
+    <!-- process all packages -->
+    <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+        <xsl:call-template name="package">
+            <xsl:with-param name="name" select="@package"/>
+        </xsl:call-template>
+    </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template name="package">
+    <xsl:param name="name"/>
+    <xsl:variable name="package.dir">
+        <xsl:if test="not($name = '')"><xsl:value-of select="translate($name,'.','/')"/></xsl:if>
+        <xsl:if test="$name = ''">.</xsl:if>
+    </xsl:variable>
+    <!--Processing package <xsl:value-of select="@name"/> in <xsl:value-of select="$output.dir"/> -->
+    <!-- create a classes-list.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+        <xsl:call-template name="classes.list">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- create a package-summary.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+        <xsl:call-template name="package.summary">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- for each class, creates a @name.html -->
+    <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
+    <xsl:for-each select="/testsuites/testsuite[@package = $name]">
+        <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
+            <xsl:apply-templates select="." mode="class.details"/>
+        </redirect:write>
+        <xsl:if test="string-length(./system-out)!=0">
+            <redirect:write file="{$output.dir}/{$package.dir}/{@name}-out.txt">
+                <xsl:value-of select="./system-out" />
+            </redirect:write>
+        </xsl:if>
+        <xsl:if test="string-length(./system-err)!=0">
+            <redirect:write file="{$output.dir}/{$package.dir}/{@name}-err.txt">
+                <xsl:value-of select="./system-err" />
+            </redirect:write>
+        </xsl:if>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="index.html">
+<html>
+    <head>
+        <title>Groovy TCK Results.</title>
+    </head>
+    <frameset cols="20%,80%">
+        <frameset rows="30%,70%">
+            <frame src="overview-frame.html" name="packageListFrame"/>
+            <frame src="allclasses-frame.html" name="classListFrame"/>
+        </frameset>
+        <frame src="overview-summary.html" name="classFrame"/>
+        <noframes>
+            <h2>Frame Alert</h2>
+            <p>
+                This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+            </p>
+        </noframes>
+    </frameset>
+</html>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+body {
+    font:normal 68% verdana,arial,helvetica;
+    color:#000000;
+}
+table tr td, table tr th {
+    font-size: 68%;
+}
+table.details tr th{
+    font-weight: bold;
+    text-align:left;
+    background:#a6caf0;
+}
+table.details tr td{
+    background:#eeeee0;
+}
+
+p {
+    line-height:1.5em;
+    margin-top:0.5em; margin-bottom:1.0em;
+}
+h1 {
+    margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+}
+h2 {
+    margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+}
+h3 {
+    margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+}
+h4 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h5 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h6 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+.Error {
+    font-weight:bold; color:red;
+}
+.Failure {
+    font-weight:bold; color:purple;
+}
+.Properties {
+  text-align:right;
+}
+</xsl:template>
+
+
+<!-- ======================================================================
+    This page is created for every testsuite class.
+    It prints a summary of the testsuite and detailed information about
+    testcase methods.
+     ====================================================================== -->
+<xsl:template match="testsuite" mode="class.details">
+    <xsl:variable name="package.name" select="@package"/>
+    <xsl:variable name="class.name"><xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></xsl:variable>
+    <html>
+        <head>
+          <title>Groovy TCK Results: <xsl:value-of select="$class.name"/></title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$package.name"/>
+            </xsl:call-template>
+       <script type="text/javascript" language="JavaScript">
+        var TestCases = new Array();
+        var cur;
+        <xsl:apply-templates select="properties"/>
+       </script>
+       <script type="text/javascript" language="JavaScript"><![CDATA[
+        function displayProperties (name) {
+          var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
+          var doc = win.document.open();
+          doc.write("<html><head><title>Properties of " + name + "</title>");
+          doc.write("<style type=\"text/css\">");
+          doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
+          doc.write("table tr td, table tr th { font-size: 68%; }");
+          doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
+          doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
+          doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
+          doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
+          doc.write("</style>");
+          doc.write("</head><body>");
+          doc.write("<h3>Properties of " + name + "</h3>");
+          doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
+          doc.write("<table class='properties'>");
+          doc.write("<tr><th>Name</th><th>Value</th></tr>");
+          for (prop in TestCases[name]) {
+            doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
+          }
+          doc.write("</table>");
+          doc.write("</body></html>");
+          doc.close();
+          win.focus();
+        }
+      ]]>
+      </script>
+        </head>
+        <body>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Class <xsl:value-of select="$class.name"/></h3>
+
+
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                <xsl:call-template name="testsuite.test.header"/>
+                <xsl:apply-templates select="." mode="print.test"/>
+            </table>
+            <h2>Tests</h2>
+            <xsl:if test="string-length(./system-out)!=0">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@name"/>-out.txt</xsl:attribute>
+                        sources &#187;
+                    </a>
+            </xsl:if>
+            <p/>
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <xsl:call-template name="testcase.test.header"/>
+              <!--
+              test can even not be started at all (failure to load the class)
+              so report the error directly
+              -->
+                <xsl:if test="./error">
+                    <tr class="Error">
+                        <td colspan="4"><xsl:apply-templates select="./error"/></td>
+                    </tr>
+                </xsl:if>
+                <xsl:apply-templates select="./testcase" mode="print.test"/>
+            </table>
+            <div class="Properties">
+                <a>
+                    <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+                    Properties &#187;
+                </a>
+            </div>
+            <xsl:if test="string-length(./system-out)!=0">
+                <div class="Properties">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@name"/>-out.txt</xsl:attribute>
+                        System.out &#187;
+                    </a>
+                </div>
+            </xsl:if>
+            <xsl:if test="string-length(./system-err)!=0">
+                <div class="Properties">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@name"/>-err.txt</xsl:attribute>
+                        System.err &#187;
+                    </a>
+                </div>
+            </xsl:if>
+        </body>
+    </html>
+</xsl:template>
+
+  <!--
+   Write properties into a JavaScript data structure.
+   This is based on the original idea by Erik Hatcher (ehatcher@apache.org)
+   -->
+  <xsl:template match="properties">
+    cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
+    <xsl:for-each select="property">
+    <xsl:sort select="@name"/>
+        cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
+    </xsl:for-each>
+  </xsl:template>
+
+
+<!-- ======================================================================
+    This page is created for every package.
+    It prints the name of all classes that belongs to this package.
+    @param name the package name to print classes.
+     ====================================================================== -->
+<!-- list of classes in a package -->
+<xsl:template name="classes.list">
+    <xsl:param name="name"/>
+    <html>
+        <head>
+            <title>Groovy TCK Classes: <xsl:value-of select="$name"/></title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <table width="100%">
+                <tr>
+                    <td nowrap="nowrap">
+                        <h2><a href="package-summary.html" target="classFrame">
+                            <xsl:value-of select="$name"/>
+                            <xsl:if test="$name = ''">&lt;none&gt;</xsl:if>
+                        </a></h2>
+                    </td>
+                </tr>
+            </table>
+
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:for-each select="/testsuites/testsuite[./@package = $name]">
+                    <xsl:sort select="@name"/>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+
+<!--
+    Creates an all-classes.html file that contains a link to all package-summary.html
+    on each class.
+-->
+<xsl:template match="testsuites" mode="all.classes">
+    <html>
+        <head>
+            <title>All Groovy TCK Classes</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:apply-templates select="testsuite" mode="all.classes">
+                    <xsl:sort select="@name"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.classes">
+    <xsl:variable name="package.name" select="@package"/>
+    <tr>
+        <td nowrap="nowrap">
+            <a target="classFrame">
+                <xsl:attribute name="href">
+                    <xsl:if test="not($package.name='')">
+                        <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+                    </xsl:if><xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
+                </xsl:attribute>
+                <xsl:value-of select="@name"/>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!--
+    Creates an html file that contains a link to all package-summary.html files on
+    each package existing on testsuites.
+    @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="testsuites" mode="all.packages">
+    <html>
+        <head>
+            <title>All Groovy TCK Packages</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+            <h2>Packages</h2>
+            <table width="100%">
+                <xsl:apply-templates select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]" mode="all.packages">
+                    <xsl:sort select="@package"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.packages">
+    <tr>
+        <td nowrap="nowrap">
+            <a href="./{translate(@package,'.','/')}/package-summary.html" target="classFrame">
+                <xsl:value-of select="@package"/>
+                <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<xsl:template match="testsuites" mode="overview.packages">
+    <html>
+        <head>
+            <title>Groovy TCK Results: Summary</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+        <xsl:attribute name="onload">open('allclasses-frame.html','classListFrame')</xsl:attribute>
+        <xsl:call-template name="pageHeader"/>
+        <h2>Summary</h2>
+        <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
+        <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
+        <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
+        <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
+        <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <tr valign="top">
+            <th>Tests</th>
+            <th>Failures</th>
+            <th>Errors</th>
+            <th>Success rate</th>
+            <th>Time</th>
+        </tr>
+        <tr valign="top">
+            <xsl:attribute name="class">
+                <xsl:choose>
+                    <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+                    <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+                    <xsl:otherwise>Pass</xsl:otherwise>
+                </xsl:choose>
+            </xsl:attribute>
+            <td><xsl:value-of select="$testCount"/></td>
+            <td><xsl:value-of select="$failureCount"/></td>
+            <td><xsl:value-of select="$errorCount"/></td>
+            <td>
+                <xsl:call-template name="display-percent">
+                    <xsl:with-param name="value" select="$successRate"/>
+                </xsl:call-template>
+            </td>
+            <td>
+                <xsl:call-template name="display-time">
+                    <xsl:with-param name="value" select="$timeCount"/>
+                </xsl:call-template>
+            </td>
+
+        </tr>
+        </table>
+        <table border="0" width="95%">
+        <tr>
+        <td style="text-align: justify;">
+        Note: <em>failures</em> are anticipated and checked for with assertions while <em>errors</em> are unanticipated.
+        </td>
+        </tr>
+        </table>
+
+        <h2>Packages</h2>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+            <xsl:call-template name="testsuite.test.header"/>
+            <xsl:for-each select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+                <xsl:sort select="@package" order="ascending"/>
+                <!-- get the node set containing all testsuites that have the same package -->
+                <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = current()/@package]"/>
+                <tr valign="top">
+                    <!-- display a failure if there is any failure/error in the package -->
+                    <xsl:attribute name="class">
+                        <xsl:choose>
+                            <xsl:when test="sum($insamepackage/@errors) &gt; 0">Error</xsl:when>
+                            <xsl:when test="sum($insamepackage/@failures) &gt; 0">Failure</xsl:when>
+                            <xsl:otherwise>Pass</xsl:otherwise>
+                        </xsl:choose>
+                    </xsl:attribute>
+                    <td><a href="./{translate(@package,'.','/')}/package-summary.html">
+                        <xsl:value-of select="@package"/>
+                        <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+                    </a></td>
+                    <td><xsl:value-of select="sum($insamepackage/@tests)"/></td>
+                    <td><xsl:value-of select="sum($insamepackage/@errors)"/></td>
+                    <td><xsl:value-of select="sum($insamepackage/@failures)"/></td>
+                    <td>
+                    <xsl:call-template name="display-time">
+                        <xsl:with-param name="value" select="sum($insamepackage/@time)"/>
+                    </xsl:call-template>
+                    </td>
+                </tr>
+            </xsl:for-each>
+        </table>
+        </body>
+        </html>
+</xsl:template>
+
+
+<xsl:template name="package.summary">
+    <xsl:param name="name"/>
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <xsl:attribute name="onload">open('package-frame.html','classListFrame')</xsl:attribute>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Package <xsl:value-of select="$name"/></h3>
+
+            <!--table border="0" cellpadding="5" cellspacing="2" width="95%">
+                <xsl:call-template name="class.metrics.header"/>
+                <xsl:apply-templates select="." mode="print.metrics"/>
+            </table-->
+
+            <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = $name]"/>
+            <xsl:if test="count($insamepackage) &gt; 0">
+                <h2>Classes</h2>
+                <p>
+                <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                    <xsl:call-template name="testsuite.test.header"/>
+                    <xsl:apply-templates select="$insamepackage" mode="print.test">
+                        <xsl:sort select="@name"/>
+                    </xsl:apply-templates>
+                </table>
+                </p>
+            </xsl:if>
+        </body>
+    </html>
+</xsl:template>
+
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+    <xsl:param name="path"/>
+    <xsl:if test="contains($path,'.')">
+        <xsl:text>../</xsl:text>
+        <xsl:call-template name="path">
+            <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+        </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="not(contains($path,'.')) and not($path = '')">
+        <xsl:text>../</xsl:text>
+    </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+    <xsl:param name="package.name"/>
+    <link rel="stylesheet" type="text/css" title="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></link>
+</xsl:template>
+
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+    <img src="http://groovy.codehaus.org/images/groovy-logo.png" alt="Groovy"/>
+    <h1>TCK Results</h1>
+    <table width="100%">
+    <tr>
+        <td align="left"></td>
+        <td align="right"><a href="http://groovy.codehaus.org/jsr/spec">Specification</a>.</td>
+    </tr>
+    </table>
+    <hr size="1"/>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="testsuite.test.header">
+    <tr valign="top">
+        <th width="80%">Name</th>
+        <th>Tests</th>
+        <th>Errors</th>
+        <th>Failures</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="testcase.test.header">
+    <tr valign="top">
+        <th>Name</th>
+        <th>Status</th>
+        <th width="80%">Type</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="testsuite" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
+                <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
+                <xsl:otherwise>Pass</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><a href="{@name}.html"><xsl:value-of select="@name"/></a></td>
+        <td><xsl:apply-templates select="@tests"/></td>
+        <td><xsl:apply-templates select="@errors"/></td>
+        <td><xsl:apply-templates select="@failures"/></td>
+        <td><xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="testcase" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="error">Error</xsl:when>
+                <xsl:when test="failure">Failure</xsl:when>
+                <xsl:otherwise>TableRowColor</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><xsl:value-of select="@name"/></td>
+        <xsl:choose>
+            <xsl:when test="failure">
+                <td>Failure</td>
+                <td><xsl:apply-templates select="failure"/></td>
+            </xsl:when>
+            <xsl:when test="error">
+                <td>Error</td>
+                <td><xsl:apply-templates select="error"/></td>
+            </xsl:when>
+            <xsl:otherwise>
+                <td>Success</td>
+                <td></td>
+            </xsl:otherwise>
+        </xsl:choose>
+        <td>
+            <xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!-- Note : the below template error and failure are the same style
+            so just call the same style store in the toolkit template -->
+<xsl:template match="failure">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<xsl:template match="error">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<!-- Style for the error and failure in the testcase template -->
+<xsl:template name="display-failures">
+    <xsl:choose>
+        <xsl:when test="not(@message)">N/A</xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="@message"/>
+        </xsl:otherwise>
+    </xsl:choose>
+    <!-- display the stacktrace -->
+    <br/><br/>
+    <code>
+        <xsl:call-template name="br-replace">
+            <xsl:with-param name="word" select="."/>
+        </xsl:call-template>
+    </code>
+    <!-- the latter is better but might be problematic for non-21" monitors... -->
+    <!--pre><xsl:value-of select="."/></pre-->
+</xsl:template>
+
+<xsl:template name="JS-escape">
+    <xsl:param name="string"/>
+    <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
+    <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
+    <xsl:value-of select="$tmp2"/>
+</xsl:template>
+
+
+<!--
+    template that will convert a carriage return into a br tag
+    @param word the text from which to convert CR to BR tag
+-->
+<xsl:template name="br-replace">
+    <xsl:param name="word"/>
+    <xsl:param name="br"><br/></xsl:param>
+    <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
+</xsl:template>
+
+<xsl:template name="display-time">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.000')"/>
+</xsl:template>
+
+<xsl:template name="display-percent">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.00%')"/>
+</xsl:template>
+</xsl:stylesheet>
+
diff --git a/groovy/src/tck/test/gls/ch03/s01/Unicode1.groovy b/groovy/src/tck/test/gls/ch03/s01/Unicode1.groovy
new file mode 100644
index 0000000..e72d400
--- /dev/null
+++ b/groovy/src/tck/test/gls/ch03/s01/Unicode1.groovy
@@ -0,0 +1,43 @@
+package gls.ch03.s01;
+/**
+ * Except for comments, identifiers and the contents of ... string 
+ * literals, all input elements are formed from ASCII characters.
+ *
+ * TODO: Find a better way to test these things
+ * Note that this is a little hard to test since the input file is ASCII.
+ *
+ * @author Alan Green
+ * @author Jeremy Rayner
+ */
+
+class Unicode1 extends GroovyTestCase {
+    //TODO: find some way to assert that Unicode3.0 + is available
+
+    /**
+      * This doc comment checks that Unicode is allowed in javadoc.
+      * e.g. \u05D0\u2136\u05d3\u05d7
+      */
+    public void testComments() {
+        // Unicode is allowed in comments
+        // This is a comment \u0410\u0406\u0414\u0419
+        /* Another comment \u05D0\u2136\u05d3\u05d7 */
+
+        /**/ // Tiny comment
+        /***/ // Also valid
+    }
+
+    public void testStringLiterals() {
+        assert 1 == "\u0040".length()
+        assert "A" == "\u0041"
+    }
+
+    public void testCharNotAvailableAsLiteral() {
+        char a = 'x'
+        char b = "x"
+        def c = "x".charAt(0)
+        assert a == b
+        assert a == c 
+    }
+
+}
+
diff --git a/groovy/src/tck/test/gls/ch03/s01/Unicode2.groovy b/groovy/src/tck/test/gls/ch03/s01/Unicode2.groovy
new file mode 100644
index 0000000..b10add8
--- /dev/null
+++ b/groovy/src/tck/test/gls/ch03/s01/Unicode2.groovy
@@ -0,0 +1,24 @@
+package gls.ch03.s01;
+/**
+ * Except for comments, identifiers and the contents of ... string 
+ * literals, all input elements are formed from ASCII characters.
+ *
+ * TODO: Find a better way to test these things
+ * Note that this is a little hard to test since the input file is ASCII.
+ *
+ * @author Jeremy Rayner
+ */
+
+class Unicode2 extends GroovyTestCase {
+
+//todo - this doesn't seem to work in raw Java5.0 either
+//    public void testUTF16SupplementaryCharacters() {
+//        assert 1 == "\uD840\uDC00".length()
+//    }
+
+    public void testIdentifiers() {
+        def foo\u0044 = 12
+        assert 20 == foo\u0044 + 8
+    }
+}
+
diff --git a/groovy/src/tck/test/gls/ch03/s02/LexicalTranslation1.groovy b/groovy/src/tck/test/gls/ch03/s02/LexicalTranslation1.groovy
new file mode 100644
index 0000000..fcf424f
--- /dev/null
+++ b/groovy/src/tck/test/gls/ch03/s02/LexicalTranslation1.groovy
@@ -0,0 +1,12 @@
+package gls.ch03.s02
+
+/** Checks Lexical Translation steps as defined in $3.2 of GLS
+ * @author Jeremy Rayner
+ */
+class LexicalTranslation1 extends GroovyTestCase {
+    void testTranslationOfUnicodeEscapes() {
+        assert "A" == "\u0041"
+    }
+    //todo: test that we have a stream of tokens (RI is antlr specific...)
+}
+
diff --git a/groovy/src/tck/test/gls/ch03/s02/Longest1.groovy b/groovy/src/tck/test/gls/ch03/s02/Longest1.groovy
new file mode 100644
index 0000000..e673ed8
--- /dev/null
+++ b/groovy/src/tck/test/gls/ch03/s02/Longest1.groovy
@@ -0,0 +1,28 @@
+package gls.ch03.s02
+
+/**
+ * GLS 3.2: The longest possible translation is used at each step, even if the 
+ * result does not ultimately make a correct program while another lexical 
+ * translation would.
+ * 
+ * This is fundamental to the way the lexer works. If there is a problem with
+ * it, other tests (e.g. to test functionality of operators or identifier
+ * names) would expose it quickly. Nevertheless, we test some combinations
+ * here for consistency.
+ *
+ * @author Alan Green
+ */
+class Longest1 extends GroovyTestCase {
+
+    // Increment and decrement operators
+    void testPrefixIncDec() {
+        def a = 20
+        def b = 10
+        def c = a - b
+        //c = a -- b // @fail:parse 
+        //c = a ++ b // @fail:parse
+        //c = a +- b // @pass
+        //c = a -+ b // @pass
+    }
+}
+
diff --git a/groovy/src/tck/test/gls/ch03/s03/UnicodeEscapes1.groovy b/groovy/src/tck/test/gls/ch03/s03/UnicodeEscapes1.groovy
new file mode 100644
index 0000000..653f595
--- /dev/null
+++ b/groovy/src/tck/test/gls/ch03/s03/UnicodeEscapes1.groovy
@@ -0,0 +1,49 @@
+package gls.ch03.s03
+/**
+ * GLS 3.3:
+ * Implementations first recognize Unicode escapes in their input, translating 
+ * the ASCII characters backslash and 'u' followed by four hexadecimal digits
+ * to the Unicode character with the indicated hexadecimal value, and passing
+ * all other characters unchanged.  
+ *
+ * @author Alan Green
+ * @author Jeremy Rayner
+ */
+
+class UnicodeEscapes1 extends GroovyTestCase {
+
+    void testAllHexDigits() {
+        // All hex digits work (char def0 is a special codepoint)
+        def s = "\u1234\u5678\u9abc\u0fed\u9ABC\u0FEC"
+        assert s.charAt(0) == 0x1234
+        assert s.charAt(1) == 0x5678
+        assert s.charAt(2) == 0x9abc
+        assert s.charAt(3) == 0x0fed
+        assert s.charAt(4) == 0x9abc
+        assert s.charAt(5) == 0x0fec
+    }
+
+    // There can be 1 or more u's after the backslash
+    void testMultipleUs() {
+        assert "\uu0061" == "a"
+        assert "\uuu0061" == "a"
+        assert "\uuuuu0061" == "a"
+    }
+
+    void testOtherVariations() {
+        // Capital 'U' not allowed
+        // assert "\U0061" == "a" // @fail:parse 
+    }
+
+    // todo: Implementations should use the \ uxxxx notation as an output format to
+    // display Unicode characters when a suitable font is not available.
+    // (to be tested as part of the standard library)
+
+    // todo: Representing supplementary characters requires two consecutive Unicode
+    // escapes. 
+    // (not sure how to test)
+    // see: gls.ch03.s01.Unicode2.testUTF16SupplementaryCharacters()
+
+    // todo: test unicode escapes last in file
+    // and invalid escapes at end of file
+}
diff --git a/groovy/src/tck/test/gls/ch03/s03/UnicodeEscapes2.groovy b/groovy/src/tck/test/gls/ch03/s03/UnicodeEscapes2.groovy
new file mode 100644
index 0000000..83f2bfa
--- /dev/null
+++ b/groovy/src/tck/test/gls/ch03/s03/UnicodeEscapes2.groovy
@@ -0,0 +1,51 @@
+package gls.ch03.s03
+/**
+ * GLS 3.3:
+ * Implementations first recognize Unicode escapes in their input, translating 
+ * the ASCII characters backslash and 'u' followed by four hexadecimal digits
+ * to the Unicode character with the indicated hexadecimal value, and passing
+ * all other characters unchanged.  
+ *
+ * @author Alan Green
+ */
+
+class UnicodeEscapes2 extends GroovyTestCase {
+
+    // GLS: If an even number of backslashes precede the 'u', it is not 
+    // an escape
+    void testCountBackslash() {
+        def a = 1
+        assert \u0061 == 1 // char 61 is 'a'
+        
+        // Not intepreted as an escape
+        // \\u0061 == 1 // @fail:parse
+
+        assert "\u0061".length() == 1
+        // Double backslash interpreted as a single backslash in string
+        assert "\\u0061".length() == 6
+        assert "\\\u0061".length() == 2
+        
+    }
+
+    // GLS: If an eligible \ is followed by u, or more than one u, and the last u
+    // is not followed by four hexadecimal digits, then a compile-time error
+    // occurs.
+    void testFourHexDigits() {
+        // these next lines won't work. The backslash has been replace by a 
+        // forwards slash so that the file parses. (Comments don't comment out
+        // unicode escapes.)
+        // assert "/u7" == "\07" //@fail:parse 
+        // def /u61 = 2 //@fail:parse 
+        // def /u061 = 2 //@fail:parse 
+
+        // If five digits, only the first four count
+        def \u00610 = 2 
+        assert a0 == 2
+    }
+    void testInvalidHexDigits() {
+        // invalid hex digits
+        // assert "\ufffg" == "a" // @fail:parse
+        // assert "\uu006g" == "a" // @fail:parse
+        // assert "\uab cd" == "acd" // @fail:parse
+    }
+}
diff --git a/groovy/src/test/JavaSourceCodehausPackagesSuite.java b/groovy/src/test/JavaSourceCodehausPackagesSuite.java
new file mode 100644
index 0000000..6bd474f
--- /dev/null
+++ b/groovy/src/test/JavaSourceCodehausPackagesSuite.java
@@ -0,0 +1,87 @@
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.codehaus.groovy.ant.GroovyTest;
+import org.codehaus.groovy.antlr.AnnotationSourceParsingTest;
+import org.codehaus.groovy.antlr.EnumSourceParsingTest;
+import org.codehaus.groovy.antlr.GroovySourceASTTest;
+import org.codehaus.groovy.antlr.SourceBufferTest;
+import org.codehaus.groovy.antlr.treewalker.*;
+import org.codehaus.groovy.ast.ClassNodeTest;
+import org.codehaus.groovy.ast.ModuleNodeTest;
+import org.codehaus.groovy.bsf.BSFTest;
+import org.codehaus.groovy.bsf.CacheBSFTest;
+import org.codehaus.groovy.classgen.*;
+import org.codehaus.groovy.control.CompilationUnitTest;
+import org.codehaus.groovy.control.CompilerConfigurationTest;
+import org.codehaus.groovy.control.messages.SyntaxErrorMessageTest;
+import org.codehaus.groovy.runtime.*;
+import org.codehaus.groovy.syntax.TokenTest;
+import org.codehaus.groovy.tools.CompilerTest;
+import org.codehaus.groovy.tools.FileSystemCompilerTest;
+import org.codehaus.groovy.tools.groovydoc.GroovyDocToolTest;
+
+/**
+ * All Java Unit tests in the 'org.codehaus.groovy' dir
+ */
+
+public class JavaSourceCodehausPackagesSuite {
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+        suite.addTestSuite(BSFTest.class);
+        suite.addTestSuite(BytecodeHelperTest.class);
+        suite.addTestSuite(CacheBSFTest.class);
+        suite.addTestSuite(CapitalizeTest.class);
+        suite.addTestSuite(ClassCompletionVerifierTest.class);
+        suite.addTestSuite(ClassNodeTest.class);
+        suite.addTestSuite(CompilationUnitTest.class);
+        suite.addTestSuite(CompilerTest.class);
+        suite.addTestSuite(CompositeVisitorTest.class);
+        suite.addTestSuite(ConstructorTest.class);
+        suite.addTestSuite(DefaultGroovyMethodsTest.class);
+        suite.addTestSuite(FileSystemCompilerTest.class);
+        suite.addTestSuite(ForTest.class);
+        suite.addTestSuite(GetPropertyTest.class);
+        suite.addTestSuite(GroovyTest.class);
+        suite.addTestSuite(GroovyClassLoaderTest.class);
+        suite.addTestSuite(GroovyDocToolTest.class);
+        suite.addTestSuite(GroovySourceASTTest.class);
+        suite.addTestSuite(EnumSourceParsingTest.class);
+        suite.addTestSuite(AnnotationSourceParsingTest.class);
+        suite.addTestSuite(GStringTest.class);
+        suite.addTestSuite(IfElseTest.class);
+        suite.addTestSuite(InvokerTest.class);
+        suite.addTestSuite(InvokeMethodTest.class);
+        suite.addTestSuite(InvokeGroovyMethodTest.class);
+        suite.addTestSuite(InvokeConstructorTest.class);
+        suite.addTestSuite(InheritedInterfaceMethodTest.class);
+        suite.addTestSuite(MainTest.class);
+        suite.addTestSuite(MethodFailureTest.class);
+        suite.addTestSuite(MethodKeyTest.class);
+        suite.addTestSuite(MethodTest.class);
+        suite.addTestSuite(ModuleNodeTest.class);
+        suite.addTestSuite(MindMapPrinterTest.class);
+        suite.addTestSuite(NewStaticMetaMethodTest.class);
+        suite.addTestSuite(NodeAsHTMLPrinterTest.class);
+        suite.addTestSuite(NodePrinterTest.class);
+        suite.addTestSuite(org.codehaus.groovy.classgen.PropertyTest.class);
+        suite.addTestSuite(org.codehaus.groovy.runtime.PropertyTest.class);
+        suite.addTestSuite(ReflectorGeneratorTest.class);
+        suite.addTestSuite(RunBugsTest.class);
+        suite.addTestSuite(RunClosureTest.class);
+        suite.addTestSuite(RunGroovyTest.class);
+//        suite.addTestSuite(RunWikiTest.class);
+        suite.addTestSuite(SourceBufferTest.class);
+        suite.addTestSuite(SourcePrinterTest.class);
+        suite.addTestSuite(UnimplementedSyntaxTest.class);
+        suite.addTestSuite(SyntaxErrorMessageTest.class);
+//        suite.addTestSuite(TestCaseRenderEngineTest.class);
+        suite.addTestSuite(TokenTest.class);
+        suite.addTestSuite(org.codehaus.groovy.classgen.TupleListTest.class);
+        suite.addTestSuite(org.codehaus.groovy.runtime.TupleListTest.class);
+        suite.addTestSuite(VerifierCodeVisitorTest.class);
+        suite.addTestSuite(JdkDynamicProxyTest.class);
+        suite.addTestSuite(CompilerConfigurationTest.class);
+        return suite;
+    }
+}
diff --git a/groovy/src/test/JavaSourceGroovyPackagesNonSecuritySuite.java b/groovy/src/test/JavaSourceGroovyPackagesNonSecuritySuite.java
new file mode 100644
index 0000000..fb14225
--- /dev/null
+++ b/groovy/src/test/JavaSourceGroovyPackagesNonSecuritySuite.java
@@ -0,0 +1,43 @@
+import groovy.inspect.InspectorTest;
+import groovy.lang.*;
+import groovy.servlet.GroovyServletTest;
+import groovy.text.TemplateTest;
+import groovy.text.XmlTemplateEngineTest;
+import groovy.tree.NodePrinterTest;
+import groovy.util.EvalTest;
+import groovy.util.MBeanTest;
+import groovy.xml.FactorySupportTest;
+import groovy.xml.XmlTest;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * All non-security related Java Unit tests in the 'groovy' dir
+ */
+
+public class JavaSourceGroovyPackagesNonSecuritySuite {
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+        suite.addTestSuite(InspectorTest.class);
+        suite.addTestSuite(GroovyShellTest.class);
+        suite.addTestSuite(GStringTest.class);
+        suite.addTestSuite(IntRangeTest.class);
+        suite.addTestSuite(MetaClassTest.class);
+        suite.addTestSuite(ScriptIntegerDivideTest.class);
+        suite.addTestSuite(ScriptPrintTest.class);
+        suite.addTestSuite(ScriptTest.class);
+        suite.addTestSuite(SequenceTest.class);
+        suite.addTestSuite(TupleTest.class);
+        suite.addTestSuite(GroovyServletTest.class);
+        suite.addTestSuite(TemplateTest.class);
+        suite.addTestSuite(XmlTemplateEngineTest.class);
+        suite.addTestSuite(NodePrinterTest.class);
+        suite.addTestSuite(EvalTest.class);
+        suite.addTestSuite(MBeanTest.class);
+        suite.addTestSuite(XmlTest.class);
+        suite.addTestSuite(FactorySupportTest.class);
+        suite.addTest(new RangeTestSuite());
+        return suite;
+    }
+}
diff --git a/groovy/src/test/JavaSourceGroovyPackagesSecuritySuite.java b/groovy/src/test/JavaSourceGroovyPackagesSecuritySuite.java
new file mode 100644
index 0000000..1fccc23
--- /dev/null
+++ b/groovy/src/test/JavaSourceGroovyPackagesSecuritySuite.java
@@ -0,0 +1,18 @@
+import junit.framework.Test;

+import junit.framework.TestSuite;

+import groovy.security.SecurityTest;

+import groovy.security.SignedJarTest;

+

+/**

+ * All Java security-related Unit tests in the 'groovy' dir

+ */

+

+public class JavaSourceGroovyPackagesSecuritySuite {

+

+    public static Test suite() {

+        TestSuite suite = new TestSuite();

+        suite.addTestSuite(SecurityTest.class);

+        suite.addTestSuite(SignedJarTest.class);

+        return suite;

+    }

+}

diff --git a/groovy/src/test/JavaSourceTckSuite.java b/groovy/src/test/JavaSourceTckSuite.java
new file mode 100644
index 0000000..a83b734
--- /dev/null
+++ b/groovy/src/test/JavaSourceTckSuite.java
@@ -0,0 +1,16 @@
+import gls.ch06.s05.JName1Test;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * All Java Unit tests in the 'gls' dir
+ */
+
+public class JavaSourceTckSuite {
+
+    public static Test suite() {
+        TestSuite suite = new TestSuite();
+        suite.addTestSuite(JName1Test.class);
+        return suite;
+    }
+}
diff --git a/groovy/src/test/UberTestCaseBugs.java b/groovy/src/test/UberTestCaseBugs.java
new file mode 100644
index 0000000..9f72a3f
--- /dev/null
+++ b/groovy/src/test/UberTestCaseBugs.java
@@ -0,0 +1,26 @@
+/**
+ * Collects all Bug-related tests.
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+import groovy.util.AllTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+public class UberTestCaseBugs extends TestCase {
+    public static Test suite() {
+        return AllTestSuite.suite("./src/test", "groovy/**/*Bug.groovy");
+    }
+
+// no tests inside (should we have an AbstractGroovyTestCase???)
+//        groovy.bugs.TestSupport.class
+
+//  The following classes appear in target/test-classes but do not extend junit.framework.TestCase
+//        groovy.bugs.Cheese.class
+//        groovy.bugs.MyRange.class
+//        groovy.bugs.Scholastic.class
+//        groovy.bugs.SimpleModel.class
+
+}
diff --git a/groovy/src/test/UberTestCaseGroovySourceCodehausPackages.java b/groovy/src/test/UberTestCaseGroovySourceCodehausPackages.java
new file mode 100644
index 0000000..785da0c
--- /dev/null
+++ b/groovy/src/test/UberTestCaseGroovySourceCodehausPackages.java
@@ -0,0 +1,24 @@
+/**

+ * Collecting all Groovy unit tests that are written in Groovy, not in root, 

+ * and not Bug-related.

+ *

+ * @author Joachim Baumann

+ * @version $Revision: 6546 $

+ */

+

+import groovy.util.AllTestSuite;

+import junit.framework.Test;

+import junit.framework.TestCase;

+

+/**

+ * Collect all groovy tests 

+ */

+public class UberTestCaseGroovySourceCodehausPackages extends TestCase {

+    /**

+     * Add all groovy tests from the codehaus subdirs

+     * @return testsuite

+     */

+    public static Test suite() {

+        return AllTestSuite.suite("src/test", "org/codehaus/**/*Test.groovy");

+    }

+}

diff --git a/groovy/src/test/UberTestCaseGroovySourceRootPackage.java b/groovy/src/test/UberTestCaseGroovySourceRootPackage.java
new file mode 100644
index 0000000..204e468
--- /dev/null
+++ b/groovy/src/test/UberTestCaseGroovySourceRootPackage.java
@@ -0,0 +1,76 @@
+/**
+ * Collects all TestCases in the Groovy test root that are written in Groovy.
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @author Dierk Koenig (refactored to use AllTestSuite)
+ * @version $Revision$
+ */
+
+import groovy.util.AllTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class UberTestCaseGroovySourceRootPackage extends TestCase {
+    public static Test suite() throws ClassNotFoundException {
+        TestSuite suite = (TestSuite) AllTestSuite.suite("src/test", "groovy/*Test.groovy");
+
+        String osName = System.getProperty("os.name");
+        if (osName.equals("Linux") || osName.equals("SunOS") || osName.equals("Solaris") || osName.equals("Mac OS X")) {
+            Class linuxTestClass = Class.forName("groovy.execute.ExecuteTest_LinuxSolaris");
+            suite.addTestSuite(linuxTestClass);
+        } else if (osName.equals("Windows 2000") || osName.equals("Windows 2003") || osName.equals("Windows XP") || osName.equals("Windows Vista")) {
+            Class windowsTestClass = Class.forName("groovy.execute.ExecuteTest_Windows");
+            suite.addTestSuite(windowsTestClass);
+        } else {
+            System.err.println("No execute tests for operating system: " + osName + "!!!");
+        }
+
+        return suite;
+    }
+}
+
+//  The following classes appear in target/test-classes but do not extend junit.framework.TestCase
+//
+//        AnotherMockInputStream.class
+//        Bean.class
+//        Bean249.class
+//        BooleanBean.class
+//        CallAnotherScript.class
+//        ClassWithScript.class
+//        ComparableFoo.class
+//        CreateData.class
+//        Entry.class
+//        EvalInScript.class
+//        Feed.class
+//        Foo.class
+//        HelloWorld.class
+//        HelloWorld2.class
+//        Html2Wiki.class
+//        IntegerCategory.class
+//        Loop.class
+//        Loop2.class
+//        MapFromList.class
+//        MarkupTestScript.class
+//        MethodTestScript.class
+//        MockInputStream.class
+//        MockProcess.class
+//        MockSocket.class
+//        OverloadA.class
+//        OverloadB.class
+//        NavToWiki.class
+//        Person.class
+//        SampleMain.class
+//        ScriptWithFunctions.class
+//        ShowArgs.class
+//        StringCategory.class
+//        SuperBase.class
+//        SuperDerived.class
+//        TestBase.class
+//        TestCaseBug.class
+//        TestDerived.class
+//        TinyAgent.class
+//        UnitTestAsScript.class
+//        UseClosureInScript.class
+//        X.class
+//        createLoop.class
diff --git a/groovy/src/test/UberTestCaseGroovySourceSubPackages.java b/groovy/src/test/UberTestCaseGroovySourceSubPackages.java
new file mode 100644
index 0000000..e3e466c
--- /dev/null
+++ b/groovy/src/test/UberTestCaseGroovySourceSubPackages.java
@@ -0,0 +1,41 @@
+/**
+ * Collecting all Groovy unit tests that are written in Groovy, not in root, and not Bug-related.
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @author Dierk Koenig
+ * @version $Revision$
+ */
+
+import groovy.util.AllTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+public class UberTestCaseGroovySourceSubPackages extends TestCase {
+    public static Test suite() {
+        return AllTestSuite.suite("src/test", "groovy/*/**/*Test.groovy");
+    }
+
+// no tests inside (should we have an AbstractGroovyTestCase???)
+//
+//        suite.addTestSuite(org.codehaus.groovy.classgen.DummyTestDerivation.class);
+//        suite.addTestSuite(org.codehaus.groovy.classgen.TestSupport.class);
+
+//  The following classes appear in target/test-classes but do not extend junit.framework.TestCase
+//
+//        suite.addTestSuite(org.codehaus.groovy.classgen.DerivedBean.class);
+//        suite.addTestSuite(org.codehaus.groovy.classgen.DummyReflector.class);
+//        suite.addTestSuite(org.codehaus.groovy.classgen.DumpClass.class);
+//        suite.addTestSuite(org.codehaus.groovy.classgen.DumpClass2.class);
+//        suite.addTestSuite(org.codehaus.groovy.classgen.DumpClass3.class);
+//        suite.addTestSuite(org.codehaus.groovy.classgen.DumpClass4.class);
+//        suite.addTestSuite(org.codehaus.groovy.classgen.DumpingClassLoader.class);
+//        suite.addTestSuite(org.codehaus.groovy.classgen.Main.class);
+//        suite.addTestSuite(org.codehaus.groovy.classgen.MyBean.class);
+//        suite.addTestSuite(org.codehaus.groovy.classgen.SimpleBean.class);
+//        suite.addTestSuite(org.codehaus.groovy.dummy.FooHandler.class);
+//        suite.addTestSuite(org.codehaus.groovy.runtime.DummyBean.class);
+//        suite.addTestSuite(org.codehaus.groovy.runtime.MockGroovyObject.class);
+//        suite.addTestSuite(org.codehaus.groovy.syntax.parser.TestParserSupport.class);
+//        suite.addTestSuite(org.codehaus.groovy.tools.DocGeneratorMain.class);
+
+}
diff --git a/groovy/src/test/UberTestCaseJavaSourceCodehausPackages.java b/groovy/src/test/UberTestCaseJavaSourceCodehausPackages.java
new file mode 100644
index 0000000..9548785
--- /dev/null
+++ b/groovy/src/test/UberTestCaseJavaSourceCodehausPackages.java
@@ -0,0 +1,20 @@
+/**
+ * The tests collected here all take a 'significant' length of time to execute,
+ * i.e. greater than 2 seconds elapsed on my machine.
+ *
+ * to prevent a JVM startup-shutdown time per test, it should be more efficient to
+ * collect the tests together into a suite.
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+public class UberTestCaseJavaSourceCodehausPackages extends TestCase {
+    public static Test suite() {
+        return JavaSourceCodehausPackagesSuite.suite();
+    }
+
+}
diff --git a/groovy/src/test/UberTestCaseJavaSourceGroovyPackagesNonSecurity.java b/groovy/src/test/UberTestCaseJavaSourceGroovyPackagesNonSecurity.java
new file mode 100644
index 0000000..8c714f2
--- /dev/null
+++ b/groovy/src/test/UberTestCaseJavaSourceGroovyPackagesNonSecurity.java
@@ -0,0 +1,14 @@
+import junit.framework.TestCase;
+import junit.framework.Test;
+
+/**
+ * Collecting all security-related Unit Tests under groovy package, written in Java.
+ *
+ * @author Christian Stein
+ * @author Dierk Koenig
+ */
+public class UberTestCaseJavaSourceGroovyPackagesNonSecurity extends TestCase {
+    public static Test suite() {
+        return JavaSourceGroovyPackagesNonSecuritySuite.suite();
+    }
+}
diff --git a/groovy/src/test/UberTestCaseJavaSourceGroovyPackagesSecurity.java b/groovy/src/test/UberTestCaseJavaSourceGroovyPackagesSecurity.java
new file mode 100644
index 0000000..6462b9b
--- /dev/null
+++ b/groovy/src/test/UberTestCaseJavaSourceGroovyPackagesSecurity.java
@@ -0,0 +1,16 @@
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+/**
+ * Collecting all security-related Unit Tests under groovy package, written in Java.
+ *
+ * @author Christian Stein
+ * @author Dierk Koenig
+ */
+public class UberTestCaseJavaSourceGroovyPackagesSecurity extends TestCase {
+
+    public static Test suite() {
+        return JavaSourceGroovyPackagesSecuritySuite.suite();
+    }
+
+}
diff --git a/groovy/src/test/UberTestCaseTCK.java b/groovy/src/test/UberTestCaseTCK.java
new file mode 100644
index 0000000..facbd8a
--- /dev/null
+++ b/groovy/src/test/UberTestCaseTCK.java
@@ -0,0 +1,20 @@
+/**
+ * All TCK testcases written in Groovy or Java.
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @author Dierk Koenig
+ * @version $Revision$
+ */
+
+import groovy.util.AllTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class UberTestCaseTCK extends TestCase {
+    public static Test suite() {
+        TestSuite suite = (TestSuite) AllTestSuite.suite("src/test/gls", "**/*Test.groovy", "**/vm5/*Test.groovy");
+        suite.addTest(JavaSourceTckSuite.suite());
+        return suite;
+    }
+}
diff --git a/groovy/src/test/UberTestCaseTCK_VM5.java b/groovy/src/test/UberTestCaseTCK_VM5.java
new file mode 100644
index 0000000..f07c1e3
--- /dev/null
+++ b/groovy/src/test/UberTestCaseTCK_VM5.java
@@ -0,0 +1,19 @@
+/**
+ * All TCK testcases written in Groovy or Java.
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @author Dierk Koenig
+ * @version $Revision$
+ */
+
+import groovy.util.AllTestSuite;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+public class UberTestCaseTCK_VM5 extends TestCase {
+    public static Test suite() {
+        TestSuite suite = (TestSuite) AllTestSuite.suite("src/test/gls", "**/vm5/*Test.groovy");
+        return suite;
+    }
+}
diff --git a/groovy/src/test/gls/CompilableTestSupport.groovy b/groovy/src/test/gls/CompilableTestSupport.groovy
new file mode 100644
index 0000000..19b8084
--- /dev/null
+++ b/groovy/src/test/gls/CompilableTestSupport.groovy
@@ -0,0 +1,23 @@
+package gls

+

+import org.codehaus.groovy.control.CompilationFailedException;

+import groovy.util.GroovyTestCase;

+

+public class CompilableTestSupport extends GroovyTestCase {

+	protected void shouldNotCompile(String script) {

+	  try {

+        GroovyClassLoader gcl = new GroovyClassLoader()

+        gcl.parseClass(script, getTestClassName())

+      } catch (CompilationFailedException cfe) {

+        assert true

+        return

+      }

+      fail("the compilation succeeded but should have failed")

+	}

+	

+	protected void shouldCompile(String script) {

+      GroovyClassLoader gcl = new GroovyClassLoader()

+      gcl.parseClass(script, getTestClassName())

+      assert true

+	}

+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/ch06/s05/GName1Test.groovy b/groovy/src/test/gls/ch06/s05/GName1Test.groovy
new file mode 100644
index 0000000..a3236f1
--- /dev/null
+++ b/groovy/src/test/gls/ch06/s05/GName1Test.groovy
@@ -0,0 +1,145 @@
+package gls.ch06.s05;
+
+import gls.ch06.s05.testClasses.Tt1cgi;
+import gls.ch06.s05.testClasses.Tt1cgo;
+import gls.ch06.s05.testClasses.Tt1gi;
+import gls.ch06.s05.testClasses.Tt1go;
+import gls.ch06.s05.testClasses.Tt1;
+import gls.ch06.s05.testClasses.Tt1c;
+
+class GName1Test extends GroovyTestCase {
+  void testObjectSupportNameHandling() {
+    Tt1  obj = new Tt1()  // Test POJO
+    def newX = "new x"
+    def newX1 = "new x1"
+    def newX2 = "new x2"
+    
+    assert obj.x == "property"
+    assert obj.@x == "field"
+    assert obj.x() == "method"
+    
+    obj.x = newX
+    obj.@x = newX1
+    
+    assert obj.x == newX
+    assert obj.@x == newX1
+    
+    obj.setX newX2
+    
+    assert obj.x == newX2
+    assert obj.@x == newX1
+  }
+  
+  void testObjectSupportNameHandling1() {
+    Tt1go  obj = new Tt1go()  // Test class subclassing GroovyObjectSupport
+    def newX = "new x"
+    def newX1 = "new x1"
+    def newX2 = "new x2"
+    
+    assert obj.x == "property"
+    assert obj.@x == "field"
+    assert obj.x() == "method"
+    
+    obj.x = newX
+    obj.@x = newX1
+    
+    assert obj.x == newX
+    assert obj.@x == newX1
+    
+    obj.setX newX2
+    
+    assert obj.x == newX2
+    assert obj.@x == newX1
+  }
+  
+  void testObjectSupportNameHandling2() {
+    Tt1  obj = new Tt1gi()  // Test POJO implementing GroovyObject
+    def newX = "new x"
+    def newX1 = "new x1"
+    def newX2 = "new x2"
+    
+    assert obj.x == "dynamic property"
+    assert obj.@x == "field"
+    assert obj.x() == "dynamic method"
+    
+    obj.x = newX
+    obj.@x = newX1
+    
+    assert obj.x == "dynamic property"
+    assert obj.@x == newX1
+    
+    obj.setX newX2
+    
+    assert obj.x == "dynamic property"
+    assert obj.@x == newX1
+  }
+  
+  void testObjectSupportNameHandlingWitnClosureValues() {
+    Tt1c obj = new Tt1c()  // Test POJO
+    def newX = {"new x"}
+    def newX1 = {"new x1"}
+    def newX2 = {"new x2"}
+    
+    assert (obj.x)() == "property"
+    assert obj.@x() == "field"
+    assert obj.x() == "method"
+    
+      
+    obj.x = newX
+    obj.@x = newX1
+    
+    assert (obj.x)() == newX()
+    assert obj.@x() == newX1()
+    
+    obj.setX newX2
+    
+    assert (obj.x)() == newX2()
+    assert obj.@x() == newX1()
+  }
+  
+  void testObjectSupportNameHandlingWitnClosureValues1() {
+    Tt1cgo obj = new Tt1cgo()  // class subclassing GroovyObjectSupport
+    def newX = {"new x"}
+    def newX1 = {"new x1"}
+    def newX2 = {"new x2"}
+    
+    assert (obj.x)() == "property"
+    assert obj.@x() == "field"
+    assert obj.x() == "method"
+    
+      
+    obj.x = newX
+    obj.@x = newX1
+    
+    assert (obj.x)() == newX()
+    assert (obj.@x)() == newX1()
+    
+    obj.setX newX2
+    
+    assert (obj.x)() == newX2()
+    assert (obj.@x)() == newX1()
+  }
+  
+  void testObjectSupportNameHandlingWitnClosureValues2() {
+    Tt1c obj = new Tt1cgi()  // Test POJO implementing GroovyObject
+    def newX = {"new x"}
+    def newX1 = {"new x1"}
+    def newX2 = {"new x2"}
+    
+    assert (obj.x)() == "property"
+    assert (obj.@x)() == "field"  // can't write obj.@x() - syntax error
+    assert obj.x() == "method"
+    
+      
+    obj.x = newX
+    obj.@x = newX1
+    
+    assert (obj.x)() == newX()
+    assert (obj.@x)() == newX1()
+    
+    obj.setX newX2
+    
+    assert (obj.x)() == newX2()
+    assert (obj.@x)() == newX1()
+  }
+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/ch06/s05/JName1Test.java b/groovy/src/test/gls/ch06/s05/JName1Test.java
new file mode 100644
index 0000000..6f8d697
--- /dev/null
+++ b/groovy/src/test/gls/ch06/s05/JName1Test.java
@@ -0,0 +1,291 @@
+package gls.ch06.s05;
+
+
+import gls.ch06.s05.testClasses.Tt1cgi;
+import gls.ch06.s05.testClasses.Tt1cgo;
+import gls.ch06.s05.testClasses.Tt1gi;
+import gls.ch06.s05.testClasses.Tt1go;
+import groovy.lang.Closure;
+import junit.framework.TestCase;
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @author John Wilson
+ */
+
+public class JName1Test extends TestCase {
+    public void testObjectSupportNameHandling() {
+        final Tt1go obj = new Tt1go();  // Test subclass of GroovyObjectSupport
+        final String newX = "new x";
+        final String newX1 = "new x1";
+        final String newX2 = "new x2";
+        final String newX3 = "new x3";
+
+        assertTrue(obj.getProperty("x") == obj.getX());
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == obj.x);
+        assertTrue(obj.invokeMethod("x", new Object[]{}) == obj.x());
+
+        obj.setProperty("x", newX);
+        obj.getMetaClass().setAttribute(obj, "x", newX1);
+
+        assertTrue(obj.getProperty("x") == newX);
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == newX1);
+
+        obj.setX(newX2);
+        obj.x = newX3;
+
+        assertTrue(obj.getProperty("x") == newX2);
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == newX3);
+    }
+
+    public void testObjectSupportNameHandling1() {
+        final Tt1go obj = new Tt1go() {
+        }; // repeat test with subclass
+        final String newX = "new x";
+        final String newX1 = "new x1";
+        final String newX2 = "new x2";
+        final String newX3 = "new x3";
+
+        assertTrue(obj.getProperty("x") == obj.getX());
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == obj.x);
+        assertTrue(obj.invokeMethod("x", new Object[]{}) == obj.x());
+
+        obj.setProperty("x", newX);
+        obj.getMetaClass().setAttribute(obj, "x", newX1);
+
+        assertTrue(obj.getProperty("x") == newX);
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == newX1);
+
+        obj.setX(newX2);
+        obj.x = newX3;
+
+        assertTrue(obj.getProperty("x") == newX2);
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == newX3);
+    }
+
+    public void testObjectSupportNameHandlingWitnClosureValues() {
+        final Tt1cgo obj = new Tt1cgo();  // Test subclass of GroovyObjectSupport
+        final Closure newX = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x";
+            }
+        };
+        final Closure newX1 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x1";
+            }
+        };
+        final Closure newX2 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x2";
+            }
+        };
+        final Closure newX3 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x3";
+            }
+        };
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == obj.getX().call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == obj.x.call());
+        assertTrue(obj.invokeMethod("x", new Object[]{}) == obj.x());
+
+        obj.setProperty("x", newX);
+        obj.getMetaClass().setAttribute(obj, "x", newX1);
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == newX.call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == newX1.call());
+
+        obj.setX(newX2);
+        obj.x = newX3;
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == newX2.call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == newX3.call());
+    }
+
+    public void testObjectSupportNameHandlingWitnClosureValuesi() {
+        final Tt1cgo obj = new Tt1cgo() {
+        };  // repeat test with subclass
+        final Closure newX = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x";
+            }
+        };
+        final Closure newX1 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x1";
+            }
+        };
+        final Closure newX2 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x2";
+            }
+        };
+        final Closure newX3 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x3";
+            }
+        };
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == obj.getX().call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == obj.x.call());
+        assertTrue(obj.invokeMethod("x", new Object[]{}) == obj.x());
+
+        obj.setProperty("x", newX);
+        obj.getMetaClass().setAttribute(obj, "x", newX1);
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == newX.call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == newX1.call());
+
+        obj.setX(newX2);
+        obj.x = newX3;
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == newX2.call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == newX3.call());
+    }
+
+    public void testMetaClassNameHandling() {
+        final Tt1gi obj = new Tt1gi();  // Test class implementing GroovyObject
+        final String newX = "new x";
+        final String newX1 = "new x1";
+        final String newX2 = "new x2";
+        final String newX3 = "new x3";
+
+        assertTrue("dynamic property".equals(obj.getProperty("x")));
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == obj.x);
+        assertTrue("dynamic method".equals(obj.invokeMethod("x", new Object[]{})));
+
+        obj.setProperty("x", newX);
+        obj.getMetaClass().setAttribute(obj, "x", newX1);
+
+        assertTrue("dynamic property".equals(obj.getProperty("x")));
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == newX1);
+
+        obj.setX(newX2);
+        obj.x = newX3;
+
+        assertTrue("dynamic property".equals(obj.getProperty("x")));
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == newX3);
+    }
+
+    public void testMetaClassNameHandling1() {
+        final Tt1gi obj = new Tt1gi() {
+        }; // repeat test with subclass
+        final String newX = "new x";
+        final String newX1 = "new x1";
+        final String newX2 = "new x2";
+        final String newX3 = "new x3";
+
+        assertTrue("dynamic property".equals(obj.getProperty("x")));
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == obj.x);
+        assertTrue("dynamic method".equals(obj.invokeMethod("x", new Object[]{})));
+
+        obj.setProperty("x", newX);
+        obj.getMetaClass().setAttribute(obj, "x", newX1);
+
+        assertTrue("dynamic property".equals(obj.getProperty("x")));
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == newX1);
+
+        obj.setX(newX2);
+        obj.x = newX3;
+
+        assertTrue("dynamic property".equals(obj.getProperty("x")));
+        assertTrue(obj.getMetaClass().getAttribute(obj, "x") == newX3);
+    }
+
+    public void testMetaClassNameHandlingWithClosures() {
+        final Tt1cgi obj = new Tt1cgi();  // Test class implementing GroovyObject
+        final Closure newX = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x";
+            }
+        };
+        final Closure newX1 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x1";
+            }
+        };
+        final Closure newX2 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x2";
+            }
+        };
+        final Closure newX3 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x3";
+            }
+        };
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == obj.getX().call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == obj.x.call());
+        assertTrue(obj.invokeMethod("x", new Object[]{}) == obj.x());
+
+        obj.setProperty("x", newX);
+        obj.getMetaClass().setAttribute(obj, "x", newX1);
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == newX.call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == newX1.call());
+
+        obj.setX(newX2);
+        obj.x = newX3;
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == newX2.call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == newX3.call());
+    }
+
+    public void testMetaClassNameHandlingWithClosures1() {
+        final Tt1cgi obj = new Tt1cgi() {
+        };  // repeat test with subclass
+        final Closure newX = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x";
+            }
+        };
+        final Closure newX1 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x1";
+            }
+        };
+        final Closure newX2 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x2";
+            }
+        };
+        final Closure newX3 = new Closure(null) {
+            public Object doCall(final Object params) {
+                return "new x3";
+            }
+        };
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == obj.getX().call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == obj.x.call());
+        assertTrue(obj.invokeMethod("x", new Object[]{}) == obj.x());
+
+        obj.setProperty("x", newX);
+        obj.getMetaClass().setAttribute(obj, "x", newX1);
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == newX.call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == newX1.call());
+
+        obj.setX(newX2);
+        obj.x = newX3;
+
+        assertTrue(((Closure) obj.getProperty("x")).call() == newX2.call());
+        assertTrue(((Closure) obj.getMetaClass().getAttribute(obj, "x")).call() == newX3.call());
+    }
+}
diff --git a/groovy/src/test/gls/ch06/s05/testClasses/Tt1.java b/groovy/src/test/gls/ch06/s05/testClasses/Tt1.java
new file mode 100644
index 0000000..1e04146
--- /dev/null
+++ b/groovy/src/test/gls/ch06/s05/testClasses/Tt1.java
@@ -0,0 +1,42 @@
+/*
+* Copyright 2005 John G. Wilson
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*
+*/
+
+package gls.ch06.s05.testClasses;
+
+
+/**
+ * @author John Wilson
+ */
+
+public class Tt1 {
+    public String x = "field";
+
+    public String getX() {
+        return this.p1;
+    }
+
+    public void setX(final String x) {
+        this.p1 = x;
+    }
+
+    public String x() {
+        return "method";
+    }
+
+    private String p1 = "property";
+}
+
diff --git a/groovy/src/test/gls/ch06/s05/testClasses/Tt1c.java b/groovy/src/test/gls/ch06/s05/testClasses/Tt1c.java
new file mode 100644
index 0000000..23d1b47
--- /dev/null
+++ b/groovy/src/test/gls/ch06/s05/testClasses/Tt1c.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package gls.ch06.s05.testClasses;
+
+import groovy.lang.Closure;
+
+/**
+ * @author John Wilson
+ */
+
+public class Tt1c {
+    public Closure x = new Closure(null) {
+        public Object doCall(final Object params) {
+            return "field";
+        }
+    };
+
+    public Closure getX() {
+        return this.p1;
+    }
+
+    public void setX(final Closure y) {
+        this.p1 = y;
+    }
+
+    public String x() {
+        return "method";
+    }
+
+    private Closure p1 = new Closure(null) {
+        public Object doCall(final Object params) {
+            return "property";
+        }
+    };
+}
diff --git a/groovy/src/test/gls/ch06/s05/testClasses/Tt1cgi.java b/groovy/src/test/gls/ch06/s05/testClasses/Tt1cgi.java
new file mode 100644
index 0000000..97d4288
--- /dev/null
+++ b/groovy/src/test/gls/ch06/s05/testClasses/Tt1cgi.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package gls.ch06.s05.testClasses;
+
+
+import groovy.lang.GroovyObject;
+import groovy.lang.MetaClass;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * @author John Wilson
+ */
+
+public class Tt1cgi extends Tt1c implements GroovyObject {
+
+    private MetaClass metaClass = InvokerHelper.getMetaClass(this);
+
+    public MetaClass getMetaClass() {
+        return this.metaClass;
+    }
+
+    public Object getProperty(final String property) {
+        return this.metaClass.getProperty(this, property);
+    }
+
+    public Object invokeMethod(final String name, final Object args) {
+        return this.metaClass.invokeMethod(this, name, args);
+    }
+
+    public void setMetaClass(final MetaClass metaClass) {
+        this.metaClass = metaClass;
+    }
+
+    public void setProperty(final String property, final Object newValue) {
+        this.metaClass.setProperty(this, property, newValue);
+    }
+}
diff --git a/groovy/src/test/gls/ch06/s05/testClasses/Tt1cgo.java b/groovy/src/test/gls/ch06/s05/testClasses/Tt1cgo.java
new file mode 100644
index 0000000..af149a5
--- /dev/null
+++ b/groovy/src/test/gls/ch06/s05/testClasses/Tt1cgo.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package gls.ch06.s05.testClasses;
+
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+
+/**
+ * @author John Wilson
+ */
+
+public class Tt1cgo extends GroovyObjectSupport {
+    public Closure x = new Closure(null) {
+        public Object doCall(final Object params) {
+            return "field";
+        }
+    };
+
+    public Closure getX() {
+        return this.p1;
+    }
+
+    public void setX(final Closure x) {
+        this.p1 = x;
+    }
+
+    public String x() {
+        return "method";
+    }
+
+    private Closure p1 = new Closure(null) {
+        public Object doCall(final Object params) {
+            return "property";
+        }
+    };
+}
diff --git a/groovy/src/test/gls/ch06/s05/testClasses/Tt1gi.java b/groovy/src/test/gls/ch06/s05/testClasses/Tt1gi.java
new file mode 100644
index 0000000..3125f00
--- /dev/null
+++ b/groovy/src/test/gls/ch06/s05/testClasses/Tt1gi.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package gls.ch06.s05.testClasses;
+
+
+import groovy.lang.GroovyInterceptable;
+import groovy.lang.GroovyObject;
+import groovy.lang.MetaClass;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * @author John Wilson
+ */
+
+public class Tt1gi extends Tt1 implements GroovyObject, GroovyInterceptable {
+
+    private MetaClass metaClass = InvokerHelper.getMetaClass(this);
+
+    public MetaClass getMetaClass() {
+        return this.metaClass;
+    }
+
+    public Object getProperty(final String property) {
+        if ("x".equals(property)) {
+            return "dynamic property";
+        } else {
+            return this.metaClass.getProperty(this, property);
+        }
+    }
+
+    public Object invokeMethod(final String name, final Object args) {
+        if ("x".equals(name)) {
+            return "dynamic method";
+        } else {
+            return this.metaClass.invokeMethod(this, name, args);
+        }
+    }
+
+    public void setMetaClass(final MetaClass metaClass) {
+        this.metaClass = metaClass;
+    }
+
+    public void setProperty(final String property, final Object newValue) {
+        this.metaClass.setProperty(this, property, newValue);
+    }
+}
diff --git a/groovy/src/test/gls/ch06/s05/testClasses/Tt1go.java b/groovy/src/test/gls/ch06/s05/testClasses/Tt1go.java
new file mode 100644
index 0000000..f4643e0
--- /dev/null
+++ b/groovy/src/test/gls/ch06/s05/testClasses/Tt1go.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2005 John G. Wilson
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package gls.ch06.s05.testClasses;
+
+
+import groovy.lang.GroovyObjectSupport;
+
+/**
+ * @author John Wilson
+ */
+
+public class Tt1go extends GroovyObjectSupport {
+    public String x = "field";
+
+    public String getX() {
+        return this.p1;
+    }
+
+    public void setX(final String x) {
+        this.p1 = x;
+    }
+
+    public String x() {
+        return "method";
+    }
+
+    public String p1 = "property";
+}
diff --git a/groovy/src/test/gls/ch08/s04/FormalParameterTest.groovy b/groovy/src/test/gls/ch08/s04/FormalParameterTest.groovy
new file mode 100644
index 0000000..b803b8f
--- /dev/null
+++ b/groovy/src/test/gls/ch08/s04/FormalParameterTest.groovy
@@ -0,0 +1,74 @@
+package gls.ch08.s04
+
+import gls.scope.CompilableTestSupport
+
+/**
+* a formal parameter is a parameter to a method, this parameter must work
+* as any local variable. But we generally do boxing on local variables, which
+* is not possible for formal parameters. The type is givven through the
+* method signature.
+*/
+class FormalParameterTest extends CompilableTestSupport{
+    
+  void testPrimitiveParameterAssignment(){
+    // test int and long as they have different lengths on in the bytecode
+    assert intMethod(1i,2i) == 2i
+    assert longMethod(1l,2l) == 2l
+    
+  }
+  
+  int intMethod(int i, int j) {
+    i=j
+    return i
+  }
+  
+  long longMethod(long i, long j) {
+    i=j
+    return i
+  }
+  
+  /**
+ * Chapter 8:		Classes
+ * Section 8.4:		Method Declarations
+ * Author:		Ken Barclay
+ *
+ * File:		arity.method.declaration.classes.8.4.groovy
+ *
+ * A class declaration may include any number of method declarations including
+ *   abstract method declarations.
+ *
+ * A method is given a name and an optional list of formal parameter declarations
+ *   enclosed in parentheses ( and ). A parameter declaration at its simplest is
+ *   simply a parameter name. It may be prefixed with a combination of optional
+ *   parameter modifiers (def or final), a type, or a type followed by the varargs
+ *   symbol (...). Two formal parameters with the same name is disallowed.
+ *
+ * A formal parameter may be optionally initialized with an expression, referred
+ *   to as a default parameter. If the number of actual parameters is fewer than
+ *   the number of formal parameters, then each actual is used to initialize, in
+ *   order, the non-default formal parameters. When all the actual parameters are
+ *   used in this manner, all subsequent formal parameters require default values.
+ *
+ * If the last formal parameter is a variable arity parameter, it is considered
+ *   to define a method that is referred to as a variable arity method. Invocations
+ *   of a variable arity method may contain more actual argument expressions than
+ *   formal parameters. All the actual argument expressions that do not correspond
+ *   to the formal parameters preceding the variable arity parameter will be evaluated
+ *   and the results stored into an array that will be passed to the method invocation.
+ */
+ 
+ def dump(age, String... names) {
+   names.collect { name ->
+       "name: $name age: $age"
+    }
+  }
+  
+  void testVariableArity(){
+    def l1 = dump(22, 'Ken', 'Barclay')
+    def l2 = ["name: Ken age: 22", "name: Barclay age: 22"]
+    l1.eachWithIndex { it, i ->
+      assert it == l2[i]
+    }
+ }
+  
+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/ch08/s04/RepetitiveMethodTest.groovy b/groovy/src/test/gls/ch08/s04/RepetitiveMethodTest.groovy
new file mode 100644
index 0000000..22c3f86
--- /dev/null
+++ b/groovy/src/test/gls/ch08/s04/RepetitiveMethodTest.groovy
@@ -0,0 +1,16 @@
+package gls.ch08.s04
+
+import gls.scope.CompilableTestSupport
+
+class RepetitiveMethodTest extends CompilableTestSupport{
+
+  void testRepetitiveMethod() {
+    def text ="""
+		class A  {
+			void foo() {}
+			void foo() {}
+		}
+	"""
+	shouldNotCompile(text)
+  }
+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/enums/vm5/EnumTest.groovy b/groovy/src/test/gls/enums/vm5/EnumTest.groovy
new file mode 100644
index 0000000..052cd9d
--- /dev/null
+++ b/groovy/src/test/gls/enums/vm5/EnumTest.groovy
@@ -0,0 +1,15 @@
+package gls.enums.vm5
+
+class EnumTest extends GroovyTestCase {
+    void testValues() {
+        assert UsCoin.values().size() == 4
+        assert UsCoin.values().toList().sum{ it.value } == 41
+    }
+}
+
+enum UsCoin {
+    penny(1), nickel(5), dime(10), quarter(25)
+    UsCoin(int value) { this.value = value }
+    private final int value
+    int getValue() { value }
+}
diff --git a/groovy/src/test/gls/generics/vm5/GenericsTest.groovy b/groovy/src/test/gls/generics/vm5/GenericsTest.groovy
new file mode 100644
index 0000000..c692e18
--- /dev/null
+++ b/groovy/src/test/gls/generics/vm5/GenericsTest.groovy
@@ -0,0 +1,232 @@
+package gls.generics.vm5
+
+class GenericsTest extends GenericsTestBase {
+
+    public void testClassWithoutParameterExtendsClassWithFixedParameter() {
+        createClassInfo """
+            class B extends ArrayList<Long> {}
+        """
+        assert signatures==[
+            "class" : "Ljava/util/ArrayList<Ljava/lang/Long;>;Lgroovy/lang/GroovyObject;",
+            ]
+    }
+
+    public void testMultipleImplementsWithParameter() {
+        createClassInfo """
+            abstract class B<T> implements Runnable,List<T> {}
+        """
+        assert signatures == ["class":"<T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/lang/Runnable;Ljava/util/List<TT;>;Lgroovy/lang/GroovyObject;"]
+    }
+
+    public void testImplementsWithParameter() {
+        createClassInfo """
+            abstract class B<T> implements List<T> {}
+        """
+        assert signatures==["class":"<T:Ljava/lang/Object;>Ljava/lang/Object;Ljava/util/List<TT;>;Lgroovy/lang/GroovyObject;"]
+    }
+
+    public void testExtendsWithParameter() {
+	    createClassInfo """
+	    	class B<T> extends ArrayList<T> {}
+	    """
+	    assert signatures==["class":"<T:Ljava/lang/Object;>Ljava/util/ArrayList<TT;>;Lgroovy/lang/GroovyObject;"]
+	}
+
+	public void testNestedExtendsWithParameter() {
+	    createClassInfo """
+	    	class B<T> extends HashMap<T,List<T>> {}
+	    """
+	    assert signatures == ["class":"<T:Ljava/lang/Object;>Ljava/util/HashMap<TT;Ljava/util/List<TT;>;>;Lgroovy/lang/GroovyObject;"]
+	}
+	
+	public void testBoundInterface() {
+	    createClassInfo """
+	    	class B<T extends List> {}
+	    """
+	    assert signatures == ["class":"<T::Ljava/util/List;>Ljava/lang/Object;Lgroovy/lang/GroovyObject;"]
+	}
+	
+	public void testNestedReuseOfParameter() {
+	    createClassInfo """
+	    	class B<Y,T extends Map<String,Map<Y,Integer>>> {}
+	    """
+	    assert signatures == ["class":"<Y:Ljava/lang/Object;T::Ljava/util/Map<Ljava/lang/String;Ljava/util/Map<TY;Ljava/lang/Integer;>;>;>Ljava/lang/Object;Lgroovy/lang/GroovyObject;"]
+	}
+	
+	public void testFieldWithParameter() {
+	    createClassInfo """
+	    	class B { public Collection<Integer> books }
+	    """
+	    assert signatures == [books : "Ljava/util/Collection<Ljava/lang/Integer;>;"]
+	}
+	
+	public void testFieldReusedParameter() {
+	    createClassInfo """
+	    	class B<T> { public Collection<T> collection }
+	    """
+	    assert signatures == ["class"    : "<T:Ljava/lang/Object;>Ljava/lang/Object;Lgroovy/lang/GroovyObject;",
+	                          collection : "Ljava/util/Collection<TT;>;"]
+	}
+	
+	public void testParameterAsReturnType() {
+	    createClassInfo """
+	    	class B {
+	    		static <T> T foo() {return null}
+			}
+	    """
+	    assert signatures == ["foo()Ljava/lang/Object;":"<T:Ljava/lang/Object;>()TT;"]
+	}
+	
+	public void testParameterAsReturnTypeAndParameter() {
+	    createClassInfo """
+	    	class B {
+	    		static <T> T foo(T t) {return null}
+			}
+	    """
+	    assert signatures == ["foo(Ljava/lang/Object;)Ljava/lang/Object;":"<T:Ljava/lang/Object;>(TT;)TT;"]
+	}
+	
+	public void testParameterAsMethodParameter() {
+	    createClassInfo """
+			class B<T> {
+	    		void foo(T t){}
+			}
+	    """
+	    assert signatures == ["class" : "<T:Ljava/lang/Object;>Ljava/lang/Object;Lgroovy/lang/GroovyObject;", 
+	        				  "foo(Ljava/lang/Object;)V":"(TT;)V"]
+	}
+	
+	public void testParameterAsNestedMethodParameter() {
+	    createClassInfo """
+			class B<T> {
+	    		void foo(List<T> t){}
+			}
+	    """
+	    assert signatures == ["class" : "<T:Ljava/lang/Object;>Ljava/lang/Object;Lgroovy/lang/GroovyObject;", 
+	        				  "foo(Ljava/util/List;)V":"(Ljava/util/List<TT;>;)V"]
+	}
+	
+	public void testParameterAsNestedMethodParameterReturningInterface() {
+	    createClassInfo """
+			class B<T> {
+	    		Cloneable foo(List<T> t){}
+			}
+	    """
+	    assert signatures == ["class" : "<T:Ljava/lang/Object;>Ljava/lang/Object;Lgroovy/lang/GroovyObject;", 
+	        				  "foo(Ljava/util/List;)Ljava/lang/Cloneable;":"(Ljava/util/List<TT;>;)Ljava/lang/Cloneable;"]
+	}
+	
+	public void testMultipleBounds() {
+	    createClassInfo """
+			class Pair<	A extends Comparable<A> & Cloneable , 
+    					B extends Cloneable & Comparable<B> > 
+			{
+	    		A foo(){}
+				B bar(){}
+			}
+	    """
+	    assert signatures == 
+	        ["class" : "<A::Ljava/lang/Comparable<TA;>;:Ljava/lang/Cloneable;B::Ljava/lang/Cloneable;:Ljava/lang/Comparable<TB;>;>Ljava/lang/Object;Lgroovy/lang/GroovyObject;",
+	    	 "foo()Ljava/lang/Comparable;" : "()TA;",
+	    	 "bar()Ljava/lang/Cloneable;"  : "()TB;"]
+	}
+
+	public void testWildCard() {
+	    createClassInfo """
+			class B {
+				private Collection<?> f1 
+				private List<? extends Number> f2 
+				private Comparator<? super String> f3 
+				private Map<String,?> f4  
+			}
+	    """
+	    assert signatures==[
+	    	f1 : "Ljava/util/Collection<*>;",
+	    	f2 : "Ljava/util/List<+Ljava/lang/Number;>;",
+	    	f3 : "Ljava/util/Comparator<-Ljava/lang/String;>;",
+			f4 : "Ljava/util/Map<Ljava/lang/String;*>;"   
+	    	]
+	}
+	
+	public void testParameterAsParameterForReturnTypeAndFieldClass() {
+	    createClassInfo """
+		   	public class B<T> {
+   				private T owner;
+   				public Class<T> getOwnerClass(){}
+   
+			} 
+	    """
+	    assert signatures==[
+			"class" : "<T:Ljava/lang/Object;>Ljava/lang/Object;Lgroovy/lang/GroovyObject;",
+			"owner" : "TT;",
+			"getOwnerClass()Ljava/lang/Class;" : "()Ljava/lang/Class<TT;>;"
+			]
+	}
+	
+	public void testInterfaceWithParameter() {
+	    createClassInfo """
+	    	interface B<T> {}
+	    """
+	    assert signatures == ["class"    : "<T:Ljava/lang/Object;>Ljava/lang/Object;"]
+	}	
+	
+	
+	public void testInvalidParameterUsage() {
+	    shouldNotCompile """
+	    	abstract class B<T> implements Map<T>{}
+	    """
+	    shouldNotCompile """
+	        class A<T,V> extends ArrayList<T,V>{}
+        """ 
+        shouldNotCompile """
+	        class A<T extends Number> {}
+	        class B<T> extends A<T>{}
+        """
+	    shouldNotCompile """
+	        class B<T> extends ArrayList<?>{}
+        """ 
+	}
+	
+	void testCovariantReturn() {
+        shouldNotCompile """
+          class A<T> {
+              T foo(T t) {1}
+           }
+
+          class B extends A<Long>{
+              String foo(Long l){"2"}
+          }
+        """
+
+        assertScript """
+          class A<T> {
+              T foo(T t) {1}
+           }
+
+          class B extends A<Long>{
+              Long foo(Long l){2}
+          }
+          def b = new B();
+          try {
+            b.foo(new Object())
+            assert false
+          } catch (ClassCastException cce) {
+            assert true
+          }
+          assert b.foo((Long) 1) == 2
+        """
+	}	
+	
+	void testCovariantReturnWithInterface() {
+	  assertScript """
+	    import java.util.concurrent.*
+
+        class CallableTask implements Callable<String> {
+          public String call() { "x" }
+        } 
+        
+        def task = new CallableTask()
+        assert task.call() == "x"
+      """  
+	}
+}
diff --git a/groovy/src/test/gls/generics/vm5/GenericsTestBase.java b/groovy/src/test/gls/generics/vm5/GenericsTestBase.java
new file mode 100644
index 0000000..93bc829
--- /dev/null
+++ b/groovy/src/test/gls/generics/vm5/GenericsTestBase.java
@@ -0,0 +1,89 @@
+package gls.generics.vm5;

+

+

+import java.util.HashMap;

+import java.util.Map;

+

+import org.codehaus.groovy.ast.ClassNode;

+import org.codehaus.groovy.control.CompilationFailedException;

+import org.codehaus.groovy.control.CompilationUnit;

+import org.codehaus.groovy.control.SourceUnit;

+import org.objectweb.asm.ClassAdapter;

+import org.objectweb.asm.ClassReader;

+import org.objectweb.asm.ClassVisitor;

+import org.objectweb.asm.FieldVisitor;

+import org.objectweb.asm.MethodVisitor;

+

+import groovy.lang.GroovyClassLoader;

+import groovy.lang.GroovyClassLoader.InnerLoader;

+import groovy.util.GroovyTestCase;

+

+

+public class GenericsTestBase extends GroovyTestCase {

+    MyLoader loader;

+    HashMap signatures = new HashMap();

+    

+    private class MyLoader extends GroovyClassLoader{

+        public MyLoader(ClassLoader classLoader) {

+            super(classLoader);

+        }

+

+        protected ClassCollector createCollector(CompilationUnit unit,SourceUnit su) {

+            return new MyCollector(new InnerLoader(this), unit, su);

+        }

+    }

+    private class MyCollector extends GroovyClassLoader.ClassCollector{

+

+        public MyCollector(InnerLoader myLoader, CompilationUnit unit, SourceUnit su) {

+           super(myLoader,unit,su);

+        }

+        protected Class createClass(byte[] code, ClassNode classNode) {

+            ClassReader cr = new ClassReader(code);

+            GenericsTester classVisitor = new GenericsTester(new org.objectweb.asm.tree.ClassNode());

+            cr.accept(classVisitor, true);

+            return super.createClass(code,classNode);

+        }        

+    }

+    private class GenericsTester  extends ClassAdapter {

+        public GenericsTester(ClassVisitor cv) {

+            super(cv);

+        }

+        public void visit(int version, int access, String name,

+                String signature, String superName, String[] interfaces) {

+            if (signature!=null) signatures.put("class", signature);

+        }

+        public FieldVisitor visitField(int access, String name, String desc,

+                String signature, Object value) {

+            if (signature!=null) signatures.put(name,signature);

+            return super.visitField(access, name, desc, signature, value);

+        }

+        public MethodVisitor visitMethod(int access, String name, String desc,

+                String signature, String[] exceptions) {

+            if (signature!=null) signatures.put(name+desc,signature);

+            return super.visitMethod(access, name, desc, signature, exceptions);

+        }

+

+    }

+    

+    public void setUp(){

+        loader = new MyLoader(this.getClass().getClassLoader());

+    }

+    

+    public void createClassInfo(String script) {

+        loader.parseClass(script);

+    }

+    

+    public Map getSignatures() {

+        return signatures;

+    }

+    

+    void shouldNotCompile(String script) {

+        try {

+            loader.parseClass(script);

+        } catch (CompilationFailedException cfe) {

+            return;

+        }

+        throw new AssertionError("compilation of script '"+script+"' should have failed, but did not.");

+    }

+}

+

diff --git a/groovy/src/test/gls/invocaton/ConstrcutorDelegationTest.groovy b/groovy/src/test/gls/invocaton/ConstrcutorDelegationTest.groovy
new file mode 100644
index 0000000..908adcc
--- /dev/null
+++ b/groovy/src/test/gls/invocaton/ConstrcutorDelegationTest.groovy
@@ -0,0 +1,31 @@
+package gls.invocaton

+

+import gls.scope.CompilableTestSupport

+

+public class ConstrcutorDelegationTest extends CompilableTestSupport {

+

+  public void testThisCallWithParameter() {

+    assertScript """

+       class A {

+         def foo

+         A(String x){foo=x}

+         A(){this("bar")}

+       }

+       def a = new A()

+       assert a.foo == "bar"

+    """   

+  }

+  

+  public void testThisCallWithoutParameter() {

+    assertScript """

+       class A {

+         def foo

+         A(String x){this(); foo=x}

+         A(){foo="bar"}

+       }

+       def a = new A("foo")

+       assert a.foo == "foo"

+    """   

+  }

+

+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/invocaton/CovariantReturnTest.groovy b/groovy/src/test/gls/invocaton/CovariantReturnTest.groovy
new file mode 100644
index 0000000..af76fb8
--- /dev/null
+++ b/groovy/src/test/gls/invocaton/CovariantReturnTest.groovy
@@ -0,0 +1,35 @@
+package gls.invocaton

+

+import gls.scope.CompilableTestSupport

+

+public class CovariantReturnTest extends CompilableTestSupport {

+

+  void testCovariantReturn() {

+    assertScript """

+      class A {

+        Object foo() {1}

+      }

+   

+      class B extends A{

+        String foo(){"2"}

+      }

+      def b = new B();

+      assert b.foo()=="2"

+      assert B.declaredMethods.findAll{it.name=="foo"}.size()==2

+    """   

+  }

+  

+  void testCovariantReturnOverwritingAbstractMethod() {

+    assertScript """

+       abstract class Numeric {

+         abstract Numeric eval();

+       }

+

+       class Rational extends Numeric {

+         Rational eval() {this}

+       }

+     

+       assert Rational.declaredMethods.findAll{it.name=="eval"}.size()==2    

+    """

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/invocaton/MethodSelectionTest.groovy b/groovy/src/test/gls/invocaton/MethodSelectionTest.groovy
new file mode 100644
index 0000000..5098a4e
--- /dev/null
+++ b/groovy/src/test/gls/invocaton/MethodSelectionTest.groovy
@@ -0,0 +1,60 @@
+package gls.invocaton

+

+import gls.scope.CompilableTestSupport

+

+public class MethodSelectionTest extends CompilableTestSupport {

+

+  /**

+   * This test ensures Groovy can choose a method based on interfaces.

+   * Choosing such an interface should not be hidden by subclasses.

+   */

+  public void testMostSpecificInterface() {

+    assertScript """

+      interface A{}

+      interface B extends A{}

+      class C implements B,A{}

+      class D extends C{}

+      class E implements B{}

+

+      def m(A a){1}

+      def m(B b){2}

+

+      assert m(new D()) == 2

+      assert m(new C()) == 2

+      assert m(new E()) == 2

+    """   

+  }

+  

+  public void testMostGeneralForNull() {

+    // we use the same signatures with different method orders,

+    // because we want to catch method ordering bugs

+    assertScript """

+      def m(String x){1}

+      def m(Integer x){2}

+      assert m(null) == 1

+      

+      def n(Integer x){2}

+      def n(String x){1}

+      assert n(null) == 1

+    """

+  }

+  

+  void testMethodSelectionException() {

+    assertScript """

+      import org.codehaus.groovy.runtime.metaclass.MethodSelectionException as MSE

+    

+      def foo(int x) {}

+      def foo(Number x) {}

+      

+      try {

+        foo()

+        assert false

+      } catch (MSE mse) {

+        assert mse.message.indexOf("foo()") >0

+        assert mse.message.indexOf("#foo(int)") >0

+        assert mse.message.indexOf("#foo(java.lang.Number)") >0

+      }

+    

+    """  

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/scope/BlockScopeVisibilityTest.groovy b/groovy/src/test/gls/scope/BlockScopeVisibilityTest.groovy
new file mode 100644
index 0000000..632923d
--- /dev/null
+++ b/groovy/src/test/gls/scope/BlockScopeVisibilityTest.groovy
@@ -0,0 +1,14 @@
+package gls.scope
+
+class BlockScopeVisibilityTest extends CompilableTestSupport {
+
+  public void testForLoopVariableNotVisibleOutside() {
+ 	
+  	assertScript("""
+  	  i=1
+  	  for (i in [1,2]) {}
+  	  assert i==1
+  	""")
+  }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/scope/ClassVariableHidingTest.groovy b/groovy/src/test/gls/scope/ClassVariableHidingTest.groovy
new file mode 100644
index 0000000..02c5401
--- /dev/null
+++ b/groovy/src/test/gls/scope/ClassVariableHidingTest.groovy
@@ -0,0 +1,19 @@
+package gls.scope;
+
+public class ClassVariableHidingTest extends CompilableTestSupport {
+
+   def foo=1;
+   def bar=2;
+   
+   public void testFooHiding() {
+     assert foo==1
+     def foo = 5
+     assert foo == 5
+   }
+   
+   public void testBarHiding() {
+     assert bar==2
+     def bar = 5
+     assert bar == 5
+   }
+ }
\ No newline at end of file
diff --git a/groovy/src/test/gls/scope/CompilableTestSupport.groovy b/groovy/src/test/gls/scope/CompilableTestSupport.groovy
new file mode 100644
index 0000000..7a41dc0
--- /dev/null
+++ b/groovy/src/test/gls/scope/CompilableTestSupport.groovy
@@ -0,0 +1,23 @@
+package gls.scope
+
+import org.codehaus.groovy.control.CompilationFailedException;
+import groovy.util.GroovyTestCase;
+
+public class CompilableTestSupport extends GroovyTestCase {
+	protected void shouldNotCompile(String script) {
+	  try {
+        GroovyClassLoader gcl = new GroovyClassLoader()
+        gcl.parseClass(script, getTestClassName())
+      } catch (CompilationFailedException cfe) {
+        assert true
+        return
+      }
+      fail("the compilation succeeded but should have failed")
+	}
+	
+	protected void shouldCompile(String script) {
+      GroovyClassLoader gcl = new GroovyClassLoader()
+      gcl.parseClass(script, getTestClassName())
+      assert true
+	}
+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/scope/MultipleDefinitionOfSameVariableTest.groovy b/groovy/src/test/gls/scope/MultipleDefinitionOfSameVariableTest.groovy
new file mode 100644
index 0000000..93c2030
--- /dev/null
+++ b/groovy/src/test/gls/scope/MultipleDefinitionOfSameVariableTest.groovy
@@ -0,0 +1,103 @@
+package gls.scope
+
+import gls.scope.CompilableTestSupport
+
+public class MultipleDefinitionOfSameVariableTest extends CompilableTestSupport {
+
+   public void testInSameBlock() {
+     shouldNotCompile("""
+       def foo = 1
+       def foo = 2
+     """)
+      
+     shouldNotCompile("""
+       class Foo {
+         def foo() {
+           def bar=1
+           def bar=2
+         }
+       }
+     """)
+   }
+   
+   public void testInSubblocks() {
+     shouldNotCompile("""
+       def foo = 1
+       5.times { def foo=2 }
+     """)
+     
+     shouldNotCompile("""
+       def foo = 1
+       label1: { def foo=2 }
+     """)
+     
+     shouldNotCompile("""
+       def foo = 1
+       for (i in []) { def foo=2 }
+     """)
+     
+     shouldNotCompile("""
+       def foo = 1
+       while (true) { def foo=2 }
+     """)     
+   } 
+   
+   public void testInNestedClosure() {
+     shouldNotCompile("""
+       def foo = 1
+       5.times { 6.times {def foo=2 }
+     """)
+     
+     assertScript ("""
+       def foo = 1
+       5.times { 6.times {foo=2 } }
+       assert foo == 2
+     """)
+   }
+   
+   public void testBindingHiding() {
+     assertScript("""
+       foo = 1
+       def foo = 3
+       assert foo==3
+       assert this.foo == 1
+       assert binding.foo == 1
+     """)
+   }
+   
+   public void testBindingAccessInMethod() {
+	   assertScript("""
+	     def methodUsingBinding() {
+	       try {
+	         s = "  bbb  ";
+	       } finally {
+	         s = s.trim();
+	       }
+	       assert s == "bbb"
+	     } 
+	     methodUsingBinding()
+	     assert s == "bbb"
+	   """)
+   }
+   
+   public void testMultipleOfSameName() {
+   		shouldNotCompile("""
+   		  class DoubleField {
+			def zero = 0
+			public zero = 0
+		  }
+		  
+   		""")
+   
+   		shouldNotCompile("""
+   		  class DoubleField {
+			def zero = 0
+			def zero = 0
+		  }
+		  
+   		""")
+
+   }
+   
+   
+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/scope/NameResolvingTest.groovy b/groovy/src/test/gls/scope/NameResolvingTest.groovy
new file mode 100644
index 0000000..179d0d7
--- /dev/null
+++ b/groovy/src/test/gls/scope/NameResolvingTest.groovy
@@ -0,0 +1,41 @@
+package gls.scope
+
+import gls.CompilableTestSupport
+
+class NameResolvingTest extends CompilableTestSupport {
+  public void testVariableNameEqualsToAClassName() {
+	Object String = ""
+	assert String == ""
+	assert String.class == java.lang.String
+  }
+  
+  public void testVariableNameEqualsCurrentClassName() {
+	Object NameResolvingTest = ""
+	assert NameResolvingTest == ""
+	assert NameResolvingTest.class == java.lang.String.class
+  }  
+  
+  public void testClassNoVariableInStaticMethod(){
+    assertScript """
+      static def foo() {
+   	     Class.forName('java.lang.Integer')
+      }
+      assert foo() == Integer
+    """
+  }
+  
+  public void testAssignmentToNonLocalVariableWithSameNameAsClass() {
+    shouldNotCompile """
+      String = 1    
+    """
+  }
+  
+  public void testClassUsageInSuper(){
+     shouldCompile """
+       class A {A(x){}}
+       class B extends A {
+         B(x){super(Thread)}
+       }
+     """   
+  }
+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/scope/StaticScopeTest.groovy b/groovy/src/test/gls/scope/StaticScopeTest.groovy
new file mode 100644
index 0000000..8444810
--- /dev/null
+++ b/groovy/src/test/gls/scope/StaticScopeTest.groovy
@@ -0,0 +1,203 @@
+package gls.scope
+
+public class StaticScopeTest extends CompilableTestSupport {
+
+    public void testNormalStaticScopeInScript() {
+        shouldNotCompile """
+        static foo() {
+            foo = 1
+        }
+        """
+
+        shouldCompile """
+        static foo() {
+            def foo=1
+        }
+        """
+    }
+
+    public void testStaticImportInclass() {
+        assertScript """
+        import static java.lang.Math.*
+        class B {
+            static main(args) { assert cos(2 * PI) == 1.0 }
+        }
+        """
+    }
+
+    public void testStaticMethodInConstructor() {
+        assertScript """
+        class B {
+            def instanceVariable = 0
+            static classVariable = 0
+            B() { instanceVariable = method(); init() }
+            static int method() { 1 }
+            static int init() { classVariable = 2 }
+        }
+        assert new B().instanceVariable == 1
+        assert B.classVariable == 2
+        """
+    }
+
+    public void testStaticMethodInSpecialConstructorCall() {
+        assertScript """
+        class A {
+            def instanceVariable = 0
+            A(x) { instanceVariable = x }
+            A(x, y) { this( method(x + y) ) }
+            static int method(x) { 2 * x }
+        }
+        assert new A(2).instanceVariable == 2
+        assert new A(2, 5).instanceVariable == 14
+        """
+        assertScript """
+        class B {
+            def instanceVariable = 0
+            B(x) { instanceVariable = x }
+        }
+        class C extends B {
+            C(x, y) { super( method(x + y) ) }
+            static int method(x) { 2 * x }
+        }
+        assert new B(2).instanceVariable == 2
+        assert new C(2, 5).instanceVariable == 14
+        """
+    }
+
+    public void testStaticImportProperty() {
+        assertScript """
+        import static A.*
+        class B {
+            static main(args) { assert temperature == 42 }
+        }
+        class A { static temperature = 42 }
+        """
+    }
+
+    public void testNormalStaticScopeInclass() {
+        assertScript """
+        class A {
+            static i
+            static foo() {
+                i=1
+            }
+        }
+        A.foo()
+        assert A.i == 1
+        """
+
+        shouldNotCompile """
+        class A {
+            def i
+            static foo() {
+                i=1
+            }
+        }
+        """
+    }
+
+    public void testClosureInStaticScope() {
+        shouldCompile """
+        5.times { foo=2 }
+        """
+
+        shouldCompile """
+        5.times { foo=it }
+        """
+    }
+
+    public void testScriptMethodCall() {
+        assertScript """
+        import static java.util.Calendar.getInstance as now
+        def now = now().time
+        assert now.class == Date
+        """
+
+        // TODO: why does in-lining of now variable from above break? GROOVY-1809 issue?
+        shouldCompile """
+        import static java.util.Calendar.getInstance as now
+        assert now().time.class == Date
+        """
+
+        shouldCompile """
+        import static java.lang.Math.*
+        cos(cos(cos(PI)))
+        """
+    }
+
+    public void testFullyQualifiedClassName() {
+        assertScript """
+        static foo() {java.lang.Integer}
+        assert foo() == java.lang.Integer
+        """
+
+        shouldNotCompile """
+        static foo() { java.lang.JavaOrGroovyThatsTheQuestion }
+        """
+
+        shouldCompile """
+        foo() { java.lang.JavaOrGroovyThatsTheQuestion }
+        """
+    }
+
+    public void testStaticPropertyInit() {
+        // GROOVY-1910
+        assertScript """
+        class Foo {
+             static p1 = 1
+             static p2 = p1
+        }
+        assert Foo.p2 == Foo.p1
+        assert Foo.p1 == 1
+        """
+
+        // should not compile for mistyped name
+        shouldNotCompile """
+        class Foo {
+            static p1 = 1
+            static p2 = x1
+        }
+        assert Foo.p2 == Foo.p1
+        assert Foo.p1 == 1
+        """
+    }
+
+    public void testSpecialConstructorAccess() {
+        shouldCompile """
+        class A{ A(x){} }
+        class B extends A {
+            B(x) { super(x) }
+        }
+        """
+
+        shouldCompile """
+        class A{ A(x){} }
+        class B extends A {
+            B(x) { super(x.something) }
+        }
+        """
+
+        shouldNotCompile """
+        class A{ A(x){} }
+        class B extends A {
+            B(x) { super(nonExistingParameter) }
+        }
+        """
+
+        shouldNotCompile """
+        class A{ A(x){} }
+        class B extends A {
+            def doNotAccessDynamicFieldsOrProperties
+            B(x) { super(doNotAccessDynamicFieldsOrProperties) }
+        }
+        """
+
+        shouldCompile """
+        class A{ A(x){} }
+        class B extends A {
+            static allowUsageOfStaticPropertiesAndFields
+            B(x) { super(allowUsageOfStaticPropertiesAndFields) }
+        }
+        """
+    }
+}
diff --git a/groovy/src/test/gls/statements/ReturnTest.groovy b/groovy/src/test/gls/statements/ReturnTest.groovy
new file mode 100644
index 0000000..016c3d7
--- /dev/null
+++ b/groovy/src/test/gls/statements/ReturnTest.groovy
@@ -0,0 +1,24 @@
+package gls.statements

+

+import gls.scope.CompilableTestSupport

+

+public class ReturnTest extends CompilableTestSupport {

+

+  public void testObjectInitializer() {

+      shouldNotCompile """

+         class A {

+            {return}

+         }      

+      """

+  }

+  

+  public void testStaticInitializer() {

+      assertScript """

+         class A {

+             static foo=2

+             static { return; foo=1 }

+         }

+         assert A.foo==2

+      """      

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/syntax/AssertTest.groovy b/groovy/src/test/gls/syntax/AssertTest.groovy
new file mode 100644
index 0000000..6e08438
--- /dev/null
+++ b/groovy/src/test/gls/syntax/AssertTest.groovy
@@ -0,0 +1,12 @@
+package gls.syntax

+

+public class AssertTest extends gls.CompilableTestSupport {

+  

+  void testAssignment() {

+    // don't allow "=" here, it most certainly must be a "=="

+    shouldNotCompile """

+       def a = 1

+       assert a = 2

+    """

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/syntax/OldClosureSyntaxRemovalTest.groovy b/groovy/src/test/gls/syntax/OldClosureSyntaxRemovalTest.groovy
new file mode 100644
index 0000000..427c218
--- /dev/null
+++ b/groovy/src/test/gls/syntax/OldClosureSyntaxRemovalTest.groovy
@@ -0,0 +1,25 @@
+package gls.syntax

+

+public class OldClosureSyntaxRemovalTest extends gls.CompilableTestSupport {

+  def a = 2

+  def b = 3

+  

+  void testOneParameter(){

+    def newClosure = {a -> a}

+    def oldClosure = {a|b}

+    assert newClosure(1)==1

+    assert oldClosure.getMaximumNumberOfParameters() == 1

+    // the old closure would have cimply returned b

+    // after removal this is the logic or

+    assert oldClosure(1)==(a|b)

+  }

+  

+  void testMultipleParameters() {

+    shouldNotCompile """

+       c = {a,b|a+b}

+    """

+    shouldCompile   """

+       c = { a,b -> a+b }

+    """

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/syntax/OldPropertySyntaxRemovalTest.groovy b/groovy/src/test/gls/syntax/OldPropertySyntaxRemovalTest.groovy
new file mode 100644
index 0000000..0bc084e
--- /dev/null
+++ b/groovy/src/test/gls/syntax/OldPropertySyntaxRemovalTest.groovy
@@ -0,0 +1,12 @@
+package gls.syntax

+

+public class OldPropertySyntaxRemovalTest extends gls.CompilableTestSupport {

+  

+  void testMultipleParameters() {

+    shouldNotCompile """

+       class C {

+         @Property foo

+       }

+    """

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/gls/syntax/OldSpreadTest.groovy b/groovy/src/test/gls/syntax/OldSpreadTest.groovy
new file mode 100644
index 0000000..817fc9f
--- /dev/null
+++ b/groovy/src/test/gls/syntax/OldSpreadTest.groovy
@@ -0,0 +1,11 @@
+package gls.syntax

+

+public class OldSpreadTest extends gls.CompilableTestSupport {

+  

+  void testSpreadStatement() {

+    // don't allow spread outside a method call

+    shouldNotCompile """

+       *x       

+    """

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/AbstractClassAndInterfaceTest.groovy b/groovy/src/test/groovy/AbstractClassAndInterfaceTest.groovy
new file mode 100644
index 0000000..5689eca
--- /dev/null
+++ b/groovy/src/test/groovy/AbstractClassAndInterfaceTest.groovy
@@ -0,0 +1,208 @@
+package groovy
+
+import org.codehaus.groovy.control.CompilationFailedException
+
+class AbstractClassAndInterfaceTest extends GroovyTestCase {
+
+	def shouldNotCompile(String script) {
+	  try {
+        GroovyShell shell = new GroovyShell()
+        shell.parse(script, getTestClassName())
+      } catch (CompilationFailedException cfe) {
+        assert true
+        return
+      }
+      fail("the compilation succeeded but should have failed")
+	}
+
+	void testInterface() {
+    	def shell = new GroovyShell()
+        def text = """
+        	interface A {
+				void methodOne(Object o)
+				Object methodTwo()
+			}
+			
+			class B implements A {
+				void methodOne(Object o){assert true}
+				Object methodTwo(){
+					assert true
+					methodOne(null)
+					return new Object()
+				}
+			}
+			
+			def b = new B();
+			return b.methodTwo()
+			"""
+		def retVal = shell.evaluate(text)
+		assert retVal.class == Object
+	}
+	
+	void testClassImplementingAnInterfaceButMissesMethod() {
+        shouldNotCompile """
+        	interface A {
+				void methodOne(Object o)
+				Object methodTwo()
+			}
+			
+			class B implements A {
+				void methodOne(Object o){assert true}
+			}
+			
+			def b = new B();
+			return b.methodTwo()
+			"""
+		
+		shouldNotCompile """
+			interface A {
+				Object methodTwo()
+		    }
+        	interface B extends A{
+				void methodOne(Object o)
+			}
+			
+			class C implements A {
+				void methodOne(Object o){assert true}
+			}
+			
+			def b = new C();
+			return b.methodTwo()
+			"""
+	}
+	
+	void testAbstractClass() {
+    	def shell = new GroovyShell()
+        def text = """
+        	abstract class A {
+				abstract void methodOne(Object o)
+				Object methodTwo(){
+					assert true
+					methodOne(null)
+					return new Object()
+				}
+			}
+			
+			class B extends A {
+				void methodOne(Object o){assert true}
+			}
+			
+			def b = new B();
+			return b.methodTwo()
+			"""
+		def retVal = shell.evaluate(text)
+		assert retVal.class == Object
+	}	
+	
+	void testClassExtendingAnAbstractClassButMissesMethod() {
+        shouldNotCompile """
+        	abstract class A {
+				abstract void methodOne(Object o)
+				Object methodTwo(){
+					assert true
+					methodOne(null)
+					return new Object()
+				}
+				abstract void MethodThree()
+			}
+			
+			abstract class B extends A {
+				void methodOne(Object o){assert true}
+			}
+			
+			class C extends B{}
+			
+			def b = new C();
+			return b.methodTwo()
+			"""	
+		
+       shouldNotCompile """
+        	abstract class A {
+				abstract void methodOne(Object o)
+				Object methodTwo(){
+					assert true
+					methodOne(null)
+					return new Object()
+				}
+				abstract void MethodThree()
+			}
+			
+			class B extends A {
+				void methodOne(Object o){assert true}
+			}
+			
+			def b = new B();
+			return b.methodTwo()
+			"""
+	}
+	
+	void testInterfaceAbstractClassCombination() {
+    	def shell = new GroovyShell()
+        def text = """
+			interface A {
+				void methodOne()
+			}
+			
+			abstract class B implements A{
+				abstract void methodTwo()
+			}
+			
+			class C extends B {
+				void methodOne(){assert true}
+				void methodTwo(){
+				  methodOne()
+				}
+			}
+			def c = new C()
+			c.methodTwo()
+			"""
+		shell.evaluate(text)
+		
+		shouldNotCompile """
+			interface A {
+				void methodOne()
+			}
+			
+			abstract class B implements A{
+				abstract void methodTwo()
+			}
+			
+			class C extends B {}
+			def c = new c()
+			c.methodTwo()
+			"""
+	}
+	
+	void testDefaultModifiersForInterfaces() {
+    	def shell = new GroovyShell()
+        def text = """
+            import java.lang.reflect.Modifier
+            
+			interface A {
+				def foo
+			}
+			
+			def fields = A.class.declaredFields
+            assert fields.length==1
+            assert fields[0].name == "foo"
+            assert Modifier.isPublic (fields[0].modifiers)
+            assert Modifier.isStatic (fields[0].modifiers)
+            assert Modifier.isFinal  (fields[0].modifiers)
+			"""
+		shell.evaluate(text)
+	}
+	
+	void testAccessToInterfaceField() {
+    	def shell = new GroovyShell()
+        def text = """
+			interface A {
+				def foo=1
+			}
+            class B implements A {
+              def foo(){foo}
+            }
+            assert new B().foo()==1
+	   """
+	   shell.evaluate(text)
+	}
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/AmbiguousInvocationTest.groovy b/groovy/src/test/groovy/AmbiguousInvocationTest.groovy
new file mode 100644
index 0000000..7ae48c8
--- /dev/null
+++ b/groovy/src/test/groovy/AmbiguousInvocationTest.groovy
@@ -0,0 +1,34 @@
+package groovy
+/**
+ * to prove GROOVY-467 is no longer an issue    
+ * 
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+class AmbiguousInvocationTest extends GroovyTestCase {
+    def dummy1, dummy2
+
+    void setUp() {
+        dummy1 = new groovy.DummyMethodsJava()
+        dummy2 = new groovy.DummyMethodsGroovy()
+    }
+
+    void testAmbiguousInvocationWithFloats() {
+        assert "float args" == dummy1.foo("bar", 1.0f, 2.0f)
+        assert "float args" == dummy1.foo("bar", (float) 1, (float) 2)
+        assert "float args" == dummy1.foo("bar", (Float) 1, (Float) 2)
+        assert "float args" == dummy2.foo("bar", 1.0f, 2.0f)
+        assert "float args" == dummy2.foo("bar", (float) 1, (float) 2)
+        assert "float args" == dummy2.foo("bar", (Float) 1, (Float) 2)
+    }
+
+    void testAmbiguousInvocationWithInts() {
+        assert "int args" == dummy1.foo("bar", 1, 2)
+        assert "int args" == dummy1.foo("bar", (int) 1, (int) 2)
+        assert "int args" == dummy1.foo("bar", (Integer) 1, (Integer) 2)
+        assert "int args" == dummy2.foo("bar", 1, 2)
+        assert "int args" == dummy2.foo("bar", (int) 1, (int) 2)
+        assert "int args" == dummy2.foo("bar", (Integer) 1, (Integer) 2)
+    }
+} 
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ArrayAutoboxingTest.groovy b/groovy/src/test/groovy/ArrayAutoboxingTest.groovy
new file mode 100644
index 0000000..179b665
--- /dev/null
+++ b/groovy/src/test/groovy/ArrayAutoboxingTest.groovy
@@ -0,0 +1,27 @@
+package groovy
+
+class ArrayAutoboxingTest extends GroovyTestCase {
+    
+    void testUnwantedAutoboxingWhenInvokingMethods() {
+      def cl
+      cl = blah2(new int[2*2])
+      assert cl == "[I"
+      cl = blah2(new long[2*2])
+      assert cl == "[J"
+      cl = blah2(new short[2*2])
+      assert cl == "[S"
+      cl = blah2(new boolean[2*2])
+      assert cl == "[Z"
+      cl = blah2(new char[2*2])
+      assert cl == "[C"
+      cl = blah2(new double[2*2])
+      assert cl == "[D"
+      cl = blah2(new float[2*2])
+      assert cl == "[F"
+    }
+    
+    def blah2(Object o) {
+       return o.class.name
+    }
+        
+} 
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ArrayCoerceTest.groovy b/groovy/src/test/groovy/ArrayCoerceTest.groovy
new file mode 100644
index 0000000..2677865
--- /dev/null
+++ b/groovy/src/test/groovy/ArrayCoerceTest.groovy
@@ -0,0 +1,211 @@
+package groovy
+
+class ArrayCoerceTest extends GroovyTestCase {
+
+    Object[] field
+    Long[] numberField
+    int[] primitiveField
+
+    void testStaticallyTypedPrimitiveTypeArrays() {
+        int[] a = [1, 2, 3]
+        assert a instanceof int[]
+        assert a.length == 3
+        dump(a)
+    }
+
+    void testStaticallyTypedPrimitiveFieldArrays() {
+        primitiveField = [1, 2, 3]
+        dump(primitiveField)
+
+        assert primitiveField instanceof int[]
+        assert primitiveField.length == 3
+    }
+
+
+    void testFoo2() {
+        def x = [1, 2, 3] as Object[]
+        dump(x)
+        assert x instanceof Object[]
+        def c = x.getClass()
+        def et = c.componentType
+        assert et == Object.class
+    }
+
+    void testStaticallyTypedObjectArrays() {
+        Object[] b = [1, 2, 3]
+        dump(b)
+
+        assert b instanceof Object[]
+        assert b.length == 3
+        def c = b.getClass()
+        def et = c.componentType
+        assert et == Object.class
+
+    }
+
+    void testStaticallyTypedArrays() {
+        Integer[] b = [1, 2, 3]
+        dump(b)
+
+        assert b instanceof Integer[]
+        assert b.length == 3
+        def c = b.getClass()
+        def et = c.componentType
+        assert et == Integer.class
+
+    }
+
+    void testStaticallyTypedObjectFieldArrays() {
+        field = [1, 2, 3]
+        dump(field)
+
+        assert field instanceof Object[]
+        assert field.length == 3
+    }
+
+    void testStaticallyTypedFieldArrays() {
+        numberField = [1, 2, 3]
+        dump(numberField)
+
+        assert numberField instanceof Long[]
+        assert numberField.length == 3
+    }
+
+    void testMakePrimitiveArrayTypes() {
+        def x = null
+
+        x = [1, 0, 1] as boolean[]
+        assert x instanceof boolean[]
+        assert x.length == 3
+        dump(x)
+
+        x = [1, 2, 3] as byte[]
+        assert x.length == 3
+        assert x instanceof byte[]
+        dump(x)
+
+        x = [1, 2, 3] as char[]
+        assert x.length == 3
+        assert x instanceof char[]
+        dump(x)
+
+        x = [1, 2, 3] as short[]
+        assert x.length == 3
+        assert x instanceof short[]
+        dump(x)
+
+        x = [1, 2, 3] as int[]
+        assert x.length == 3
+        assert x instanceof int[]
+        dump(x)
+
+        x = [1, 2, 3] as long[]
+        assert x.length == 3
+        assert x instanceof long[]
+        dump(x)
+
+        x = [1, 2, 3] as float[]
+        assert x.length == 3
+        assert x instanceof float[]
+        dump(x)
+
+        x = [1, 2, 3] as double[]
+        assert x.length == 3
+        assert x instanceof double[]
+        dump(x)
+    }
+
+
+
+    void testAsObjectArray() {
+        def x = [1, 2, 3] as Object[]
+        def c = x.getClass()
+        def et = c.componentType
+        assert et == Object.class
+        dump(x)
+
+        Integer[] y = [1, 2, 3]
+        c = y.getClass()
+        et = c.componentType
+        assert et == Integer.class
+        dump(y)
+    }
+
+    void testMakeArrayThenCoerceToAnotherType() {
+        def x = [1, 2, 3] as int[]
+        assert x.size() == 3
+        assert x instanceof int[]
+        dump(x)
+
+        // lets try coerce it into an array of longs
+        def y = x as long[]
+        assert y instanceof long[]
+        dump(y)
+
+        def z = y as Object[]
+        assert z instanceof Object[]
+        def c = z.getClass()
+        def et = c.componentType
+        assert et == Object.class
+        dump(z)
+
+        x = y as int[]
+        assert x.size() == 3
+        assert x instanceof int[]
+        dump(x)
+    }
+
+
+    void testMakeArrayTypes() {
+        def x = null
+
+        x = [1, 0, 1] as Boolean[]
+        assert x instanceof Boolean[]
+        assert x.length == 3
+        dump(x)
+
+        x = [1, 2, 3] as Byte[]
+        assert x.length == 3
+        assert x instanceof Byte[]
+        dump(x)
+
+        x = [1, 2, 3] as Character[]
+        assert x.length == 3
+        assert x instanceof Character[]
+        dump(x)
+
+        x = [1, 2, 3] as Short[]
+        assert x.length == 3
+        assert x instanceof Short[]
+        dump(x)
+
+        x = [1, 2, 3] as Integer[]
+        assert x.length == 3
+        assert x instanceof Integer[]
+        dump(x)
+
+        x = [1, 2, 3] as Long[]
+        assert x.length == 3
+        assert x instanceof Long[]
+        dump(x)
+
+        x = [1, 2, 3] as Float[]
+        assert x.length == 3
+        assert x instanceof Float[]
+        dump(x)
+
+        x = [1, 2, 3] as Double[]
+        assert x.length == 3
+        assert x instanceof Double[]
+        dump(x)
+    }
+
+    void dump(array) {
+        println "Array is of type ${array.class} which has element type ${array.class.componentType}"
+        for (i in array) {
+            println "Contains entry $i of type ${i.class}"
+        }
+        println()
+    }
+
+}
diff --git a/groovy/src/test/groovy/ArrayParamMethodTest.groovy b/groovy/src/test/groovy/ArrayParamMethodTest.groovy
new file mode 100644
index 0000000..b95c77c
--- /dev/null
+++ b/groovy/src/test/groovy/ArrayParamMethodTest.groovy
@@ -0,0 +1,25 @@
+package groovy
+
+class ArrayParamMethodTest extends GroovyTestCase implements DummyInterface {
+
+    void testMethodCall() {
+        def array = "a b c".split(' ')
+        
+        assert array.size() == 3
+        
+        methodWithArrayParam(array)
+    }
+    
+    void methodWithArrayParam(String[] args) {
+        println("first item: ${args[0]}")
+        
+        // lets turn it into a list
+        def list = args.toList()
+        assert list instanceof java.util.List
+        list[4] = "e"
+        
+        assert list == ["a", "b", "c", null, "e"]
+        
+        println("Created list ${list}")
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ArrayTest.groovy b/groovy/src/test/groovy/ArrayTest.groovy
new file mode 100644
index 0000000..d0a58ee
--- /dev/null
+++ b/groovy/src/test/groovy/ArrayTest.groovy
@@ -0,0 +1,145 @@
+package groovy
+
+class ArrayTest extends GroovyTestCase {
+
+    void testFixedSize() {
+        def array = new String[10]
+        assert array.size() == 10
+        array[0] = "Hello"
+        assert array[0] == "Hello"
+    }
+    
+    void testArrayWithInitializer() {
+        String[] array = [ "nice", "cheese", "gromit" ]
+        assert array.size() == 3
+        assert array[0] == "nice" , array.inspect()
+        assert array[1] == "cheese"
+        assert array[2] == "gromit"
+    }
+
+    void testCharArrayCreate() {
+           def array = new char[3]
+           assert array.size() == 3
+    }
+
+    void testCharArrayWithInitializer() {
+        char[] array = [ 'a', 'b', 'c' ]
+        assert array.size() == 3
+        assert array[0] == 'a' , array.inspect()
+        assert array[1] == 'b'
+        assert array[2] == 'c'
+    }
+    
+    void testByteArrayCreate() {
+        def array = new byte[100]
+        assert array.size() == 100;
+    }
+
+    void testByteArrayWithInitializer() {
+        byte[] array = [0, 1, 2, 3]
+        assert array.size() == 4
+        assert array[0] == 0 , array.inspect()
+        assert array[1] == 1
+        assert array[2] == 2
+        assert array[3] == 3
+    }
+
+    void testByteArrayWithInitializerAndAssignmentOfNumber() {
+        byte[] array = [ 2, 4]
+        assert array.size() == 2
+        assert array[0] == 2
+        assert array[1] == 4
+
+        array[0] = 76
+        array[1] = 32
+        assert array[0] == 76
+        assert array[1] == 32
+
+        array.putAt(0, 45)
+        array.putAt(1, 67)
+        assert array[0] == 45
+        assert array[1] == 67
+    }
+
+    void testDoubleArrayCreate() {
+         def array  = new double[3]
+         assert array.size() == 3
+    }
+
+    void testDoubleArrayWithInitializer() {
+        double[] array = [ 1.3, 3.14, 2.7]
+        assert array.size() == 3
+        assert array[0] == 1.3 , array.inspect()
+        assert array[1] == 3.14
+        assert array[2] == 2.7
+    }
+
+    void testIntArrayCreate() {
+        def array = new int[5]
+        assert array.size() == 5
+    }
+
+    void testIntArrayWithInitializer() {
+        int[] array = [42, -5, 360]
+        assert array.size() == 3
+        assert array[0] == 42 , array.inspect()
+        assert array[1] == -5
+        assert array[2] == 360
+    }
+
+    void testArrayDeclaration() {
+        String[] array = [ "a", "b", "c" ]
+        assert array.size() == 3
+        assert array[0] == "a"
+        assert array[1] == "b"
+        assert array[2] == "c"
+    }
+
+    void testSimpleArrayEquals() {
+        Integer[] arr1 = [1,2,3,4]
+        Integer[] arr2 = [1,2,3,4]
+        assert arr1 == arr2
+        int[] primarr1 = [1,2,3,4]
+        int[] primarr2 = [1,2,3,4]
+        assert primarr1 == primarr2
+        assert primarr1 == arr2
+        double[] primarr3 = [1,2,3,4]
+        long[] primarr4 = [1,2,3,4]
+        assert primarr3 == primarr4
+        assert primarr3 == primarr1
+        assert primarr2 == primarr4
+        def list1 = [1,2,3,4]
+        assert list1 == arr1
+        assert arr1 == list1
+        assert list1 == primarr1
+        assert primarr1 == list1
+        boolean[] bools1 = [true, true, false]
+        boolean[] bools2 = [true, true, false]
+        assert bools1 == bools2
+        assert bools1 == [true, true, false] as boolean[]
+    }
+
+    void testComplexArrayEquals() {
+        def a = [1,2] as Integer[]
+        def b = [1,2]
+        assert a == b
+        assert [[1,2],[3,4]] == [[1,2],[3,4]]
+        def x = [[1,2] as Integer[]]
+        Object[] y = [[1,2]]
+        assert y == x
+        assert [[1,2],[3,4]] as int[][] == [[1,2],[3,4]] as int[][]
+        assert [[[5,6],[7,8]]] as int[][][] == [[[5,6],[7,8]]] as Long[][][]
+        assert [[1,2],[3,4]] as long[][] == [[1,2],[3,4]] as long[][]
+        assert [[1,2],[3,4]] as long[][] == [[1,2],[3,4]] as Long[][]
+        assert [[1,2],[3,4]] as long[][] == [[1,2],[3,4]]
+        assert [[1,2],[3,4]] as long[][] == [[1,2] as short[], [3,4] as short[]]
+        int[][] intsA = [[1,2],[3,4]]
+        assert intsA == [[1,2],[3,4]] as int[][]
+        int[][] intsB = [[1,2],[3,4]]
+        assert intsA == intsB
+        boolean[][] boolsA = [[true, true], [false, true], [false]]
+        boolean[][] boolsB = [[true, true], [false, true], [false]]
+        assert boolsA == boolsB
+    }
+
+}
diff --git a/groovy/src/test/groovy/ArrayTypeTest.groovy b/groovy/src/test/groovy/ArrayTypeTest.groovy
new file mode 100644
index 0000000..e783ec5
--- /dev/null
+++ b/groovy/src/test/groovy/ArrayTypeTest.groovy
@@ -0,0 +1,22 @@
+package groovy
+
+class ArrayTypeTest extends GroovyTestCase {
+
+    void testClosureWithTypedParam() {
+        def c = {String[] foo->println("called with $foo") }
+        c(null)
+    }
+
+    void testVariableType() {
+        Object[] foo = methodThatReturnsArray()
+        println "foo is $foo"
+
+    }
+
+
+
+    Object[] methodThatReturnsArray() {
+        println "Invoked the method"
+        return null
+    }
+}
diff --git a/groovy/src/test/groovy/AssertNumberTest.groovy b/groovy/src/test/groovy/AssertNumberTest.groovy
new file mode 100644
index 0000000..93a0419
--- /dev/null
+++ b/groovy/src/test/groovy/AssertNumberTest.groovy
@@ -0,0 +1,42 @@
+package groovy
+
+class AssertNumberTest extends GroovyTestCase {
+
+    void testCompare() {
+        def x = null
+
+        assert x == null
+        assert x != 432
+        assert x != 423.2342
+   		     
+        x = 123
+
+        assert x != null
+        assert x != 432
+        assert x != 423.2342
+        assert x == 123
+		
+        x = 42.2342
+
+        assert x != null
+        assert x != 432
+        assert x != 423.2342
+        assert x == 42.2342
+    }
+	
+    void testLessThan() {
+        def x = 123
+
+        assert x < 200
+        assert x <= 200
+        assert x <= 123
+    }
+
+    void testGreaterThan() {
+        def x = 123
+	    
+        assert x > 10
+        assert x >= 10
+        assert x >= 123
+    }
+}
diff --git a/groovy/src/test/groovy/AssertTest.groovy b/groovy/src/test/groovy/AssertTest.groovy
new file mode 100644
index 0000000..73bbdf0
--- /dev/null
+++ b/groovy/src/test/groovy/AssertTest.groovy
@@ -0,0 +1,45 @@
+package groovy
+
+class AssertTest extends GroovyTestCase {
+
+    void testAssert() {
+        def x = null
+        
+        assert x == null
+        assert x != "abc"
+        assert x != "foo"
+	    
+        x = "abc"
+
+        assert x != "foo"
+        assert x !=  null
+        assert x != "def"
+        assert x == "abc"
+        
+        assert x.equals("abc")
+        
+        assert !x.equals("def")
+        assert !false
+        assert !(1==2)
+        assert !(1>3)
+        assert !(1!=1)
+    }
+	
+    void testAssertFail() {
+        def x = 1234
+
+        def runCode = false
+        try {
+            runCode = true
+            assert x == 5
+
+            fail("Should have thrown an exception")
+        }
+        catch (AssertionError e) {
+            //msg = "Expression: (x == 5). Values: x = 1234"
+            //assert e.getMessage() == msg
+            //assert e.message == msg
+        }
+        assert runCode, "has not ran the try / catch block code"
+    }
+}
diff --git a/groovy/src/test/groovy/Bar.groovy b/groovy/src/test/groovy/Bar.groovy
new file mode 100644
index 0000000..2c308ed
--- /dev/null
+++ b/groovy/src/test/groovy/Bar.groovy
@@ -0,0 +1,30 @@
+package groovy
+
+import java.util.HashMap as Goober;
+
+class Cheddar extends Goober implements Runnable
+{
+    Goober theMap;
+    protected def cheesier;
+    public static def cheesiest;
+
+    static void main(args) {
+        def f = new Cheddar()
+        println f
+    }
+
+    def cheeseIt() {  }
+
+    String getStringCheese() { }
+    String getOtherCheese(foo,bar) { }
+
+    void run() { cheeseIt() }
+
+    static Goober mutateGoober(Goober theGoober) { }
+   
+}
+
+class Provolone
+{
+
+}
diff --git a/groovy/src/test/groovy/Base64Test.groovy b/groovy/src/test/groovy/Base64Test.groovy
new file mode 100644
index 0000000..700c39e
--- /dev/null
+++ b/groovy/src/test/groovy/Base64Test.groovy
@@ -0,0 +1,27 @@
+package groovy
+
+
+class Base64Test extends GroovyTestCase {
+
+    void testCodec() {
+        def testString ="§1234567890-=±!@£\$%^&*()_+qwertyuiop[]QWERTYUIOP{}asdfghjkl;'\\ASDFGHJKL:\"|`zxcvbnm,./~ZXCVBNM<>?\u0003\u00ff\u00f0\u000f"
+
+        // get a byte array using the least significant eigth bits of each caharacter
+           def testBytes = testString.getBytes("ISO-8859-1")
+
+           // turn the bytes back into a string for later comparison
+            testString = new String(testBytes, "ISO-8859-1")
+
+            // encode the bytes as base64. This produces a Writable object convert it to a String
+            def encodedBytes = testBytes.encodeBase64().toString()
+
+            // decode the base64 back to a byte array
+            def decodedBytes = encodedBytes.decodeBase64()
+
+            // turn the byte array back to a String for caomparison
+            def decodedString = new String(decodedBytes, "ISO-8859-1")
+
+            assert decodedString.equals(testString)
+    }
+
+}
diff --git a/groovy/src/test/groovy/BinaryStreamsTest.groovy b/groovy/src/test/groovy/BinaryStreamsTest.groovy
new file mode 100755
index 0000000..12f34e3
--- /dev/null
+++ b/groovy/src/test/groovy/BinaryStreamsTest.groovy
@@ -0,0 +1,151 @@
+/**

+ * Test case for DefaultGroovyMethods involving Object streams and data streams.

+ *

+ * @author Martin C. Martin

+ */

+class BinaryStreamsTest extends GroovyTestCase {

+

+    void testNewObjectStream() {

+        def temp1 = tempFile

+        def oos = temp1.newObjectOutputStream()

+

+        // For fun, let's try storing & restoring a circular list.

+        def writeFirst = [55, null]

+        def writeSecond = [78, writeFirst]

+        writeFirst[1] = writeSecond

+

+        oos.writeObject(writeFirst)

+        oos.close()

+

+        def ois = temp1.newObjectInputStream()

+        def readFirst = ois.readObject()

+        assert readFirst.getClass() == java.util.ArrayList

+        assert readFirst[0] == 55

+        assert readFirst[1][0] == 78

+        assert readFirst[1][1] == readFirst

+        ois.close()

+    }

+

+    void testWithObjectStream() {

+        def temp2 = tempFile

+        temp2.withObjectOutputStream { oos ->

+            oos.writeInt(12345)

+            oos.writeObject("Yoinks!")

+            oos.writeObject(new Date(1170466550755))

+        }

+

+        temp2.withObjectInputStream { ois ->

+            assert ois.readInt() == 12345;

+            assert ois.readObject() == "Yoinks!"

+            assert ois.readObject() == new Date(1170466550755)

+        }

+    }

+

+    void testNewDataStream() {

+        def temp3 = tempFile

+        def dos = temp3.newDataOutputStream()

+        dos.writeInt(0x77654321)

+        dos.writeChars("Miles")

+        dos.close()

+

+        temp3.withInputStream { is ->

+            def data = new byte[4+5*2]

+            is.read(data)

+            byte[] expected = [0x77, 0x65, 0x43, 0x21, 0, 'M', 0, 'i', 0, 'l', 0, 'e', 0, 's']

+            assert data as List == expected as List

+            assert is.read() == -1

+        }

+

+        def dis = temp3.newDataInputStream()

+        assert dis.readInt() == 0x77654321

+        "Miles".each { assert dis.readChar() == it }

+        dis.close()

+    }

+

+    void testWithDataStream() {

+        def temp4 = tempFile

+        temp4.withDataOutputStream { dos ->

+            dos.writeInt(0x12345678)

+            dos.writeChars("Bubba")

+        }

+

+        temp4.withInputStream { is ->

+            def data = new byte[4+5*2]

+            is.read(data)

+            byte[] expected = [0x12, 0x34, 0x56, 0x78, 0, 'B', 0, 'u', 0, 'b', 0, 'b', 0, 'a']

+            assert data as List == expected as List

+            assert is.read() == -1

+        }

+

+        temp4.withDataInputStream { dis ->

+            assert dis.readInt() == 0x12345678

+            "Bubba".each { assert dis.readChar() == it }

+        }

+    }

+

+    void manualTestRawSocketsProcessing() {

+        def server

+        def port = 999

+        Thread.start{

+            server = new ServerSocket(port)

+            server.accept() { socket ->

+                socket.withStreams { input, output ->

+                    def ois = new ObjectInputStream(input)

+                    def oos = new ObjectOutputStream(output)

+                    def arg1 = ois.readObject()

+                    def arg2 = ois.readObject()

+                    oos << arg1 + arg2

+                    ois.close()

+                    oos.close()

+                }

+            }

+        }

+

+        def result

+        def client = new Socket("localhost", port)

+        client.withStreams{ input, output ->

+            def oos = new ObjectOutputStream(output)

+            def ois = new ObjectInputStream(input)

+            oos << 1000

+            oos << 24

+            result = ois.readObject()

+            ois.close()

+            oos.close()

+        }

+        client.close()

+        server.close()

+        assert result == 1024

+    }

+

+    void manualTestObjectSocketsProcessing() {

+        def server

+        def port = 999

+        Thread.start{

+            server = new ServerSocket(port)

+            server.accept() { socket ->

+                socket.withObjectStreams { ois, oos ->

+                    def arg1 = ois.readObject()

+                    def arg2 = ois.readObject()

+                    oos << arg1 + arg2

+                }

+            }

+        }

+

+        def result

+        def client = new Socket("localhost", port)

+        client.withObjectStreams{ ois, oos ->

+            oos << 1000

+            oos << 24

+            result = ois.readObject()

+        }

+        client.close()

+        server.close()

+        assert result == 1024

+    }

+

+    private File getTempFile() {

+        def temp = File.createTempFile("BinaryStreamsTestFile", ".dat")

+        temp.deleteOnExit()

+        return temp

+    }

+}

diff --git a/groovy/src/test/groovy/BindingTest.groovy b/groovy/src/test/groovy/BindingTest.groovy
new file mode 100644
index 0000000..880443b
--- /dev/null
+++ b/groovy/src/test/groovy/BindingTest.groovy
@@ -0,0 +1,21 @@
+package groovy
+
+class BindingTest extends GroovyTestCase {
+
+    void testProperties() {
+    	def b = new Binding()
+    	b.setVariable("foo", 123)
+    	
+    	assert b.foo == 123
+    	
+    	b.bar = 456
+    	
+    	assert b.getVariable("bar") == 456
+    	assert b["bar"] == 456
+    	
+    	b["a.b.c"] = 'abc'
+    	
+    	assert b.getVariable("a.b.c") == 'abc'
+    	assert b["a.b.c"] == 'abc'
+    }
+}
diff --git a/groovy/src/test/groovy/BitSetTest.groovy b/groovy/src/test/groovy/BitSetTest.groovy
new file mode 100644
index 0000000..1981dbd
--- /dev/null
+++ b/groovy/src/test/groovy/BitSetTest.groovy
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy
+
+class BitSetTest extends GroovyTestCase{
+
+    void testSubscriptOperator() {
+        def bitSet = new BitSet()
+
+        bitSet[2] = true
+        bitSet[3] = true
+
+        assertBitFalse bitSet, 0
+        assertBitFalse bitSet, 1
+        assertBitTrue  bitSet, 2
+        assertBitTrue  bitSet, 3
+        assertBitFalse bitSet, 4
+    }
+
+    void testSubscriptAssignmentWithRange() {
+        def bitSet = new BitSet()
+
+        bitSet[2..4] = true
+
+        assertBitFalse bitSet, 0
+        assertBitFalse bitSet, 1
+        assertBitTrue  bitSet, 2
+        assertBitTrue  bitSet, 3
+        assertBitTrue  bitSet, 4
+        assertBitFalse bitSet, 5
+    }
+
+    void testSubscriptAssignmentWithReverseRange() {
+        def bitSet = new BitSet()
+
+        bitSet[4..2] = true
+
+        assertBitFalse bitSet, 0
+        assertBitFalse bitSet, 1
+        assertBitTrue  bitSet, 2
+        assertBitTrue  bitSet, 3
+        assertBitTrue  bitSet, 4
+        assertBitFalse bitSet, 5
+    }
+
+    void testSubscriptAccessWithRange() {
+        def bitSet = new BitSet()
+
+        bitSet[7] = true
+        bitSet[11] = true
+
+        def subSet = bitSet[5..11]
+
+        assertTrue 'subSet should have been a BitSet', subSet instanceof BitSet
+
+        assertNotSame 'subSet should not have been the same object', bitSet, subSet
+
+        // the last true bit should be at index 6
+        assertEquals 'result had wrong logical size', 7, subSet.length()
+
+        assertBitFalse subSet, 0
+        assertBitFalse subSet, 1
+        assertBitTrue  subSet, 2
+        assertBitFalse subSet, 3
+        assertBitFalse subSet, 4
+        assertBitFalse subSet, 5
+        assertBitTrue  subSet, 6
+    }
+
+    void testSubscriptAccessWithReverseRange() {
+        def bitSet = new BitSet()
+
+        bitSet[3] = true
+        bitSet[4] = true
+
+        def subSet = bitSet[8..2]
+
+        assertTrue 'subSet should have been a BitSet', subSet instanceof BitSet
+
+        assertNotSame 'subSet should not have been the same object', bitSet, subSet
+
+        // the last true bit should be at index 5
+        assertEquals 'result had wrong logical size', 6, subSet.length()
+
+        assertBitFalse subSet, 0
+        assertBitFalse subSet, 1
+        assertBitFalse subSet, 2
+        assertBitFalse subSet, 3
+        assertBitTrue  subSet, 4
+        assertBitTrue  subSet, 5
+        assertBitFalse subSet, 6
+    }
+
+    void testAnd() {
+        def a = new BitSet()
+        a[2] = true
+        a[3] = true
+        def b = new BitSet()
+        b[1] = true
+        b[3] = true
+        def c = a & b
+        assertBitFalse c, 0
+        assertBitFalse c, 1
+        assertBitFalse c, 2
+        assertBitTrue  c, 3
+    }
+
+    void testOr() {
+        def a = new BitSet()
+        a[2] = true
+        a[3] = true
+        def b = new BitSet()
+        b[1] = true
+        b[3] = true
+        def c = a | b
+        assertBitFalse c, 0
+        assertBitTrue  c, 1
+        assertBitTrue  c, 2
+        assertBitTrue  c, 3
+    }
+
+    void testXor() {
+        def a = new BitSet()
+        a[2] = true
+        a[3] = true
+        def b = new BitSet()
+        b[1] = true
+        b[3] = true
+        def c = a ^ b
+        assertBitFalse c, 0
+        assertBitTrue  c, 1
+        assertBitTrue  c, 2
+        assertBitFalse c, 3
+    }
+
+    void testBitwiseNegate() {
+        def a = new BitSet()
+        a[2] = true
+        a[3] = true
+        def b = ~a
+        assertBitTrue  b, 0
+        assertBitTrue  b, 1
+        assertBitFalse b, 2
+        assertBitFalse b, 3
+    }
+
+    private assertBitTrue(bitset, index) {
+        assertTrue  'index ' + index + ' should have been true',  bitset[index]
+    }
+
+    private assertBitFalse(bitset, index) {
+        assertFalse 'index ' + index + ' should have been false', bitset[index]
+    }
+}
diff --git a/groovy/src/test/groovy/BreakContinueLabelTest.groovy b/groovy/src/test/groovy/BreakContinueLabelTest.groovy
new file mode 100644
index 0000000..f032e42
--- /dev/null
+++ b/groovy/src/test/groovy/BreakContinueLabelTest.groovy
@@ -0,0 +1,106 @@
+package groovy
+
+/**
+ * todo: add BreakContinueLabelWithClosureTest (when break is used to return from a Closure)
+
+ * @author Dierk Koenig
+ */
+class BreakContinueLabelTest extends GroovyTestCase {
+
+    void testDeclareSimpleLabel() {
+        label_1: assert true
+        label_2:
+        assert true
+    }
+    void testBreakLabelInSimpleForLoop() {
+        label_1: for (i in [1]) {
+            break label_1
+            assert false
+        }
+    }
+
+    void testBreakLabelInNestedForLoop() {
+        label: for (i in [1]) {
+            for (j in [1]){
+                break label
+                assert false, 'did not break inner loop'
+            }
+            assert false, 'did not break outer loop'
+        }
+    }
+
+    void testUnlabelledBreakInNestedForLoop() {
+        def reached = false
+        for (i in [1]) {
+            for (j in [1]){
+                break
+                assert false, 'did not break inner loop'
+            }
+            reached = true
+        }
+        assert reached, 'must not break outer loop'
+    }
+
+    void testBreakLabelInSimpleWhileLoop() {
+        label_1: while (true) {
+            break label_1
+            assert false
+        }
+    }
+
+    void testBreakLabelInNestedWhileLoop() {
+        def count = 0
+        label: while (count < 1) {
+            count++
+            while (true){
+                break label
+                assert false, 'did not break inner loop'
+            }
+            assert false, 'did not break outer loop'
+        }
+    }
+
+    void testBreakLabelInNestedMixedForAndWhileLoop() {
+        def count = 0
+        label_1: while (count < 1) {
+            count++
+            for (i in [1]){
+                break label_1
+                assert false, 'did not break inner loop'
+            }
+            assert false, 'did not break outer loop'
+        }
+        label_2: for (i in [1]) {
+            while (true){
+                break label_2
+                assert false, 'did not break inner loop'
+            }
+            assert false, 'did not break outer loop'
+        }
+    }
+
+    void testUnlabelledContinueInNestedForLoop() {
+        def log = ''
+        for (i in [1,2]) {
+            log += i
+            for (j in [3,4]){
+                if (j==3) continue
+                log += j
+            }
+        }
+        assertEquals '1424',log
+    }
+
+    void testContinueLabelInNestedForLoop() {
+        def log = ''
+        label: for (i in [1,2]) {
+            log += i
+            for (j in [3,4]){
+                if (j==4) continue label
+                log += j
+            }
+            log += 'never reached'
+        }
+        assertEquals '1323',log
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/CallInnerClassCtorTest.groovy b/groovy/src/test/groovy/CallInnerClassCtorTest.groovy
new file mode 100644
index 0000000..67892a5
--- /dev/null
+++ b/groovy/src/test/groovy/CallInnerClassCtorTest.groovy
@@ -0,0 +1,36 @@
+package groovy
+
+/**
+ * Checks that it's possible to call inner classes constructor from groovy
+ * @author Guillaume Laforge
+ */
+class CallInnerClassCtorTest extends GroovyTestCase {
+
+    void testCallCtor() {
+        def user = new groovy.OuterUser()
+        user.name = "Guillaume"
+        user.age = 27
+
+        assert user.name == "Guillaume"
+        assert user.age == 27
+    }
+
+    void testCallInnerCtor() {
+        def address = new groovy.OuterUser.InnerAddress()
+        address.city = "Meudon"
+        address.zipcode = 92360
+
+        assert address.city == "Meudon"
+        assert address.zipcode == 92360
+    }
+
+    void testCallInnerInnerCtor() {
+        def address = new groovy.OuterUser.InnerAddress.Street()
+        address.name = "rue de la paix"
+        address.number = 17
+
+        assert address.name == "rue de la paix"
+        assert address.number == 17
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/CastTest.groovy b/groovy/src/test/groovy/CastTest.groovy
new file mode 100644
index 0000000..07b70b8
--- /dev/null
+++ b/groovy/src/test/groovy/CastTest.groovy
@@ -0,0 +1,122 @@
+package groovy
+
+class CastTest extends GroovyTestCase {
+
+    Short b = 1
+    
+    void testCast() {
+        def x = (Short) 5
+
+        println("Cast Integer to ${x} with type ${x.class}")
+        
+        assert x.class == Short
+        
+        methodWithShort(x)
+    }
+    
+    void testImplicitCast() {
+        Short x = 6
+        
+        println("Created ${x} with type ${x.class}")
+        
+        assert x.class == Short , "Type is ${x.class}"
+        
+		methodWithShort(x)
+        
+        x = 7
+        
+        println("Updated ${x} with type ${x.class}")
+        
+        assert x.class == Short , "Type is ${x.class}"
+    }
+
+    void testImplicitCastOfField() {
+
+        println("Field is ${b} with type ${b.class}")
+        
+        assert b.class == Short , "Type is ${b.class}"
+        
+        b = 5
+        
+        println("Updated field ${b} with type ${b.class}")
+ 
+        assert b.class == Short , "Type is ${b.class}"
+    }
+    
+    void testIntCast() {
+        def i = (Integer) 'x'
+        
+        assert i instanceof Integer
+    }
+    
+    void testCharCompare() {
+        def i = (Integer) 'x'
+        def c = 'x'
+        
+        assert i == c
+        assert i =='x'
+        assert c == 'x'
+		assert i == i
+		assert c == c
+
+        assert 'x' == 'x'
+        assert 'x' == c
+        assert 'x' == i
+    }
+    
+    void testCharCast() {
+        def c = (Character) 'x'
+        
+        assert c instanceof Character
+        
+        c = (Character)10
+        
+        assert c instanceof Character
+    }
+    
+    void methodWithShort(Short s) {
+        println("Called with ${s} with type ${s.class}")
+        assert s.class == Short
+    }
+    
+    void methodWithChar(Character x) {
+        println("Called with ${x} with type ${s.class}")
+        
+        def text = "text"
+        def idx = text.indexOf(x)
+        
+        assert idx == 2
+    }
+    // br
+    void testPrimitiveCasting() {
+        def d = 1.23
+        def i1 = (int)d
+        def i2 = (Integer)d
+        assert i1.class.name == 'java.lang.Integer'
+        assert i2.class.name == 'java.lang.Integer'
+
+        def ch = (char) i1
+        assert ch.class.name == 'java.lang.Character'
+
+        def dd = (double)d
+        assert dd.class.name == 'java.lang.Double'
+
+    }
+    
+    void testAsSet() {
+    	def mySet = [2, 3, 4, 3] as SortedSet
+    	assert mySet instanceof SortedSet
+    	
+    	// identity test
+    	mySet = {} as SortedSet
+    	assert mySet.is ( mySet as SortedSet )
+    	
+    	mySet = [2, 3, 4, 3] as Set
+    	assert mySet instanceof HashSet
+    	
+        // identitiy test
+    	mySet = {} as Set
+    	assert mySet.is ( mySet as Set )
+    }
+
+}
diff --git a/groovy/src/test/groovy/CategoryTest.groovy b/groovy/src/test/groovy/CategoryTest.groovy
new file mode 100644
index 0000000..7eb6694
--- /dev/null
+++ b/groovy/src/test/groovy/CategoryTest.groovy
@@ -0,0 +1,135 @@
+package groovy
+
+class CategoryTest extends GroovyTestCase {
+
+    void setUp() {
+        def dummy = null
+        CategoryTestPropertyCategory.setSomething(dummy, 'hello')
+        CategoryTestHelperPropertyReplacer.setAProperty(dummy, 'anotherValue')
+    }
+
+    void testCategories() {
+        use (StringCategory) {
+            assert "Sam".lower() == "sam";
+            use (IntegerCategory.class) {
+                assert "Sam".lower() == "sam";
+                assert 1.inc() == 2;
+            }
+            shouldFail(MissingMethodException, { 1.inc() });
+        }
+        shouldFail(MissingMethodException, { "Sam".lower() });
+    }
+
+    void testReturnValueWithUseClass() {
+        def returnValue = use(StringCategory) {
+            "Green Eggs And Ham".lower()
+        }
+        assert "green eggs and ham" == returnValue
+    }
+
+    void testReturnValueWithUseList() {
+        def returnValue = use([StringCategory, IntegerCategory]) {
+            "Green Eggs And Ham".lower() + 5.inc()
+        }
+        assert "green eggs and ham6" == returnValue
+    }
+
+    void testCategoryDefinedProperties() {
+        use(CategoryTestPropertyCategory) {
+            assert getSomething() == "hello"
+            assert something == "hello"
+            something = "nihao"
+            assert something == "nihao"
+        }
+
+        // test the new value again in a new block
+        use(CategoryTestPropertyCategory) {
+            assert something == "nihao"
+        }
+    }
+  
+    void testCategoryReplacedPropertyAccessMethod() {
+        def cth = new CategoryTestHelper()
+        cth.aProperty = "aValue"
+        assert cth.aProperty == "aValue"
+        use (CategoryTestHelperPropertyReplacer) {
+            assert cth.aProperty == "anotherValue"
+            cth.aProperty = "this is boring"
+            assert cth.aProperty == "this is boring"
+        }
+        assert cth.aProperty == "aValue"
+    }
+    
+    void testCategoryHiddenByClassMethod() {
+      assertScript """
+         class A{}
+         class B extends A{def m(){1}}
+         class Category{ static m(A a) {2}}
+         def b = new B()
+         use (Category) {
+           assert b.m() == 1
+         }
+      """
+    }
+    
+    void testCategoryOverridingClassMethod() {
+      assertScript """
+         class A {def m(){1}}
+         class Category{ static m(A a) {2}}
+         def a = new A()
+         use (Category) {
+           assert a.m() == 2
+         }
+      """
+      assertScript """
+         class A {def m(){1}}
+         class B extends A{}
+         class Category{ static m(A a) {2}}
+         def a = new B()
+         use (Category) {
+           assert a.m() == 2
+         }
+      """
+    }
+    
+    void testCategoryWithMixedOverriding() {
+      assertScript """
+         class A{def m(){0}}
+         class B extends A{def m(){1}}
+         class Category{ static m(A a) {2}}
+         def b = new B()
+         use (Category) {
+           assert b.m() == 1
+         }
+      """
+    }
+    
+}
+
+class StringCategory {
+    static String lower(String string) {
+        return string.toLowerCase();
+    }
+}
+
+class IntegerCategory {
+    static Integer inc(Integer i) {
+        return i + 1;
+    }
+}
+
+class CategoryTestPropertyCategory {
+    private static aVal = "hello"
+    static getSomething(Object self) { return aVal }
+    static void setSomething(Object self, newValue) { aVal = newValue }
+}
+
+class CategoryTestHelper {
+    def aProperty = "aValue"
+}
+
+class CategoryTestHelperPropertyReplacer {
+    private static aVal = "anotherValue"
+    static getAProperty(CategoryTestHelper self) { return aVal }
+    static void setAProperty(CategoryTestHelper self, newValue) { aVal = newValue }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ChainedAssignmentTest.groovy b/groovy/src/test/groovy/ChainedAssignmentTest.groovy
new file mode 100644
index 0000000..1c0eb64
--- /dev/null
+++ b/groovy/src/test/groovy/ChainedAssignmentTest.groovy
@@ -0,0 +1,22 @@
+package groovy
+
+class ChainedAssignmentTest extends GroovyTestCase {
+
+    def dummy(v) {
+        print v
+    }
+
+    void testCompare() {
+        def i = 123
+        def s = "hello"
+
+        def i2
+        def i1 = i2 = i;
+        assert i1 == 123
+        assert i2 == 123
+
+        def s1
+        dummy(s1 = s)
+        assert s1 == "hello"
+    }
+}
diff --git a/groovy/src/test/groovy/ClassExpressionTest.groovy b/groovy/src/test/groovy/ClassExpressionTest.groovy
new file mode 100644
index 0000000..7dd2fe5
--- /dev/null
+++ b/groovy/src/test/groovy/ClassExpressionTest.groovy
@@ -0,0 +1,64 @@
+package groovy
+
+/** 
+ * Tests the use of classes as variable expressions
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ClassExpressionTest extends GroovyTestCase {
+
+    void testUseOfClass() {
+        def x = String
+        
+        System.out.println("x: " + x)
+        
+        assert x != null
+
+        assert x.getName().endsWith('String')
+        assert x.name.endsWith('String')
+
+        x = Integer
+        
+        assert x != null
+        assert x.name.endsWith('Integer')
+        
+        x = GroovyTestCase
+        
+        assert x != null
+        assert x.name.endsWith('GroovyTestCase')
+        
+        x = ClassExpressionTest
+        
+        assert x != null
+
+        System.out.println("x: " + x)
+    }
+
+    void testClassPsuedoProperty() {
+
+        def x = "cheese";
+
+        assert x.class != null
+
+        assert x.class == x.getClass();
+
+        System.err.println( "x.class: " + x.class );
+    }
+    
+    void testPrimitiveClasses() {
+        assert void == Void.TYPE
+        assert int == Integer.TYPE
+        assert byte == Byte.TYPE
+        assert char == Character.TYPE
+        assert double == Double.TYPE
+        assert float == Float.TYPE
+        assert long == Long.TYPE
+        assert short == Short.TYPE
+    }
+    
+    void testArrayClassReference() {
+       def foo = int[]
+       assert foo.name == "[I"
+    }
+}
diff --git a/groovy/src/test/groovy/ClassGetSimpleNameTest.groovy b/groovy/src/test/groovy/ClassGetSimpleNameTest.groovy
new file mode 100644
index 0000000..480e70b
--- /dev/null
+++ b/groovy/src/test/groovy/ClassGetSimpleNameTest.groovy
@@ -0,0 +1,67 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy

+

+import static groovy.util.ProxyGenerator.*

+

+/**

+ * This test verifies the behaviour of ProxyGenerator#getSimpleName(Class) used in Map of closures coercion to classes.

+ * The behaviour of ProxyGenerator#getSimpleName(Class) should be the same as Class#getSimpleName().

+ *

+ * TODO remove this class when we can use Class#getSimpleName() in ProxyGenerator#instantiateAggregate(Map map, List interfaces, Class clazz)

+ */

+class ClassGetSimpleNameTest extends GroovyTestCase {

+

+    void testPrimitiveTypes() {

+        assert "boolean" == getSimpleName(boolean)

+        assert "byte"    == getSimpleName(byte)

+        assert "char"    == getSimpleName(char)

+        assert "double"  == getSimpleName(double)

+        assert "float"   == getSimpleName(float)

+        assert "int"     == getSimpleName(int)

+        assert "long"    == getSimpleName(long)

+        assert "short"   == getSimpleName(short)

+        assert "void"    == getSimpleName(void)

+    }

+

+    void testNormalClasses() {

+        assert "java.lang.Class"             == getSimpleName(java.lang.Class)

+        assert "java.lang.String"            == getSimpleName(java.lang.String)

+        assert "java.lang.Runnable"          == getSimpleName(java.lang.Runnable)

+        assert "groovy.SimpleNameDummyClass" == getSimpleName(groovy.SimpleNameDummyClass)

+        assert "java.util.Map.Entry"         == getSimpleName(Map.Entry)

+    }

+

+    void testArrayOfPrimitives() {

+        assert "boolean[]"     == getSimpleName(boolean[])

+        assert "byte[][]"      == getSimpleName(byte[][])

+        assert "char[]"        == getSimpleName(char[])

+        assert "double[][][]"  == getSimpleName(double[][][])

+        assert "float[]"       == getSimpleName(float[])

+        assert "int[]"         == getSimpleName(int[])

+        assert "long[][]"      == getSimpleName(long[][])

+        assert "short[][]"     == getSimpleName(short[][])

+    }

+

+    void testArrayOfNormalClasses() {

+        assert "java.lang.String[]"                == getSimpleName(String[])

+        assert "java.lang.Boolean[][]"             == getSimpleName(Boolean[][])

+        assert "groovy.SimpleNameDummyClass[][][]" == getSimpleName(groovy.SimpleNameDummyClass[][][])

+        assert "java.lang.Integer[][]"             == getSimpleName(java.lang.Integer[][])

+    }

+}

+

+class SimpleNameDummyClass {}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ClassLoaderBug.groovy b/groovy/src/test/groovy/ClassLoaderBug.groovy
new file mode 100644
index 0000000..7d2ecf2
--- /dev/null
+++ b/groovy/src/test/groovy/ClassLoaderBug.groovy
@@ -0,0 +1,13 @@
+package groovy
+
+class ClassLoaderBug extends GroovyTestCase {
+    
+    static void main(args) {
+        def gst = new ClassLoaderBug();
+        gst.testWithOneVariable();
+    }
+
+    void testWithOneVariable() {
+        println("Called method")
+    }
+}
diff --git a/groovy/src/test/groovy/ClassTest.groovy b/groovy/src/test/groovy/ClassTest.groovy
new file mode 100644
index 0000000..a2786b8
--- /dev/null
+++ b/groovy/src/test/groovy/ClassTest.groovy
@@ -0,0 +1,22 @@
+package groovy
+
+class ClassTest extends GroovyTestCase {
+
+    void testClassExpression() {
+    	def c = String.class
+    	println c
+    	assert c instanceof Class
+    	assert c.name == "java.lang.String" , c.name
+    	
+    	c = GroovyTestCase.class
+    	println c
+    	assert c instanceof Class
+    	assert c.name.endsWith("GroovyTestCase") , c.name
+    	
+    	c = ClassTest.class
+    	println c
+    	assert c instanceof Class
+    	assert c.name.endsWith("ClassTest") , c.name
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ClosureAsParamTest.groovy b/groovy/src/test/groovy/ClosureAsParamTest.groovy
new file mode 100644
index 0000000..4b42369
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureAsParamTest.groovy
@@ -0,0 +1,19 @@
+package groovy
+
+/** 
+ * Tests Closures in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureAsParamTest extends GroovyTestCase {
+
+    void testSimpleBlockCall() {
+        assertClosure({owner-> println(owner) })
+    }
+  
+    def assertClosure(Closure block) {
+        assert block != null
+        block.call("hello!")
+    }
+}
diff --git a/groovy/src/test/groovy/ClosureCloneTest.groovy b/groovy/src/test/groovy/ClosureCloneTest.groovy
new file mode 100644
index 0000000..2b55ae7
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureCloneTest.groovy
@@ -0,0 +1,23 @@
+package groovy
+
+/** 
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureCloneTest extends GroovyTestCase {
+
+    void testCloneOfClosure() {
+        def factor = 2
+        def closure = { it * factor }
+
+        def value = closure(5)
+        assert value == 10
+
+        // now lets clone the closure
+        def c2 = closure.clone()
+        assert c2 != null
+
+        value = c2(6)
+        assert value == 12
+    }  
+}
diff --git a/groovy/src/test/groovy/ClosureComparatorTest.groovy b/groovy/src/test/groovy/ClosureComparatorTest.groovy
new file mode 100644
index 0000000..b62b18f
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureComparatorTest.groovy
@@ -0,0 +1,97 @@
+package groovy
+
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+/**
+ * Tests for ClosureComparator
+ *
+ * @author Alexey Verkhovsky
+ * @version $Revision$
+ */
+ class ClosureComparatorTest extends GroovyTestCase {
+
+
+  public void testClosureComparatorForGroovyObjects() {
+
+    def comparator = new ClosureComparator() { one, another ->
+      one.greaterThan(another)
+    }
+
+    def one = new ComparableFoo(5)
+    def another = new ComparableFoo(-5)
+
+    assertEquals(10, comparator.compare(one, another))
+    assertEquals(0, comparator.compare(one, one))
+    assertEquals(-10, comparator.compare(another, one))
+
+  }
+
+  public void testClosureComparatorForNumericTypes() {
+
+    def comparator = new ClosureComparator() { one, another ->
+      one - another
+    }
+
+    assertEquals(1, comparator.compare(Integer.MAX_VALUE, Integer.MAX_VALUE-1))
+    assertEquals(0, comparator.compare(Double.MIN_VALUE, Double.MIN_VALUE))
+    assertEquals(-1, comparator.compare(Long.MIN_VALUE, Long.MIN_VALUE+1))
+  }
+
+}
+
+class ComparableFoo {
+  long value
+
+  public ComparableFoo(long theValue) {
+    this.value = theValue
+  }
+
+  def greaterThan(anotherFoo) {
+    return (this.value - anotherFoo.value)
+  }
+}
+
diff --git a/groovy/src/test/groovy/ClosureCurryTest.groovy b/groovy/src/test/groovy/ClosureCurryTest.groovy
new file mode 100644
index 0000000..d447020
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureCurryTest.groovy
@@ -0,0 +1,106 @@
+package groovy
+
+/** 
+ * @author Hallvard Tr�tteberg
+ * @version $Revision$
+ */
+class ClosureCurryTest extends GroovyTestCase {
+
+    void testCurry() {
+		def clos1 = {s1, s2 -> s1 + s2}
+		def clos2 = clos1.curry("hi")
+		def value = clos2("there")
+		assert value == "hithere"
+
+		def clos3 = {s1, s2, s3 -> s1 + s2 + s3}
+		def clos4 = clos3.curry('a')
+		def clos5 = clos4.curry('b')
+		def clos6 = clos4.curry('x')
+		def clos7 = clos4.curry('f', 'g')
+		value = clos5('c')
+		assert value == "abc"
+		value = clos6('c')
+		assert value == "axc"
+		value = clos4('y', 'z')
+		assert value == "ayz"
+		value = clos7()
+		assert value == "afg"
+
+		clos3 = {s1, s2, s3 -> s1 + s2 + s3}.asWritable()
+		clos4 = clos3.curry('a')
+		clos5 = clos4.curry('b')
+		clos6 = clos4.curry('x')
+		clos7 = clos4.curry('f', 'g')
+		value = clos5('c')
+		assert value == "abc"
+		value = clos6('c')
+		assert value == "axc"
+		value = clos4('y', 'z')
+		assert value == "ayz"
+		value = clos7()
+		assert value == "afg"
+
+		clos3 = {s1, s2, s3 -> s1 + s2 + s3}
+		clos4 = clos3.curry('a').asWritable()
+		clos5 = clos4.curry('b').asWritable()
+		clos6 = clos4.curry('x').asWritable()
+		clos7 = clos4.curry('f', 'g').asWritable()
+		value = clos5('c')
+		assert value == "abc"
+		value = clos6('c')
+		assert value == "axc"
+		value = clos4('y', 'z')
+		assert value == "ayz"
+		value = clos7()
+		assert value == "afg"
+
+		clos3 = {s1, s2, s3 -> s1 + s2 + s3}
+		clos4 = clos3.curry('a').clone()
+		clos5 = clos4.curry('b').clone()
+		clos6 = clos4.curry('x').clone()
+		clos7 = clos4.curry('f', 'g').clone()
+		value = clos5('c')
+		assert value == "abc"
+		value = clos6('c')
+		assert value == "axc"
+		value = clos4('y', 'z')
+		assert value == "ayz"
+		value = clos7()
+		assert value == "afg"
+
+		clos3 = {s1, s2, s3 -> s1 + s2 + s3}
+		clos4 = clos3.curry('a').asWritable().clone()
+		clos5 = clos4.curry('b').asWritable().clone()
+		clos6 = clos4.curry('x').asWritable().clone()
+		clos7 = clos4.curry('f', 'g').asWritable().clone()
+		value = clos5('c')
+		assert value == "abc"
+		value = clos6('c')
+		assert value == "axc"
+		value = clos4('y', 'z')
+		assert value == "ayz"
+		value = clos7()
+		assert value == "afg"
+    }  
+    
+    void testParameterTypes() {
+        def cl1 = { String s1, int i -> return s1 + i }
+        assert "foo5" == cl1("foo", 5)
+        assert [String, int] == cl1.getParameterTypes().toList()
+
+        def cl2 = cl1.curry("bla")
+        assert "bla4" == cl2(4)
+        assert null != cl2.getParameterTypes() 
+        assert [int] == cl2.getParameterTypes().toList() 
+    }    
+    
+    void testDelegate() {
+        def res = null
+        def c = {a -> res = z}
+        def cc = c.curry(1)
+
+        cc.delegate = [z: "goodbye"]
+        cc()
+        assert res == cc.delegate.z
+    }
+}
diff --git a/groovy/src/test/groovy/ClosureDefaultParameterTest.groovy b/groovy/src/test/groovy/ClosureDefaultParameterTest.groovy
new file mode 100644
index 0000000..aaefd03
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureDefaultParameterTest.groovy
@@ -0,0 +1,30 @@
+package groovy
+
+/** 
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureDefaultParameterTest extends GroovyTestCase {
+
+    void testClosureWithDefaultParams() {
+
+        def block = {a = 123, b = 456 -> println "value of a = $a and b = $b" }
+
+        block = { Integer a = 123, String b = "abc" ->
+                  println "value of a = $a and b = $b"; return "$a $b".toString() }
+
+        assert block.call(456, "def") == "456 def"
+        assert block.call() == "123 abc"
+        assert block(456) == "456 abc"
+        assert block(456, "def") == "456 def"
+    }
+    
+    void testClosureWithDefaultParamFromOuterScope() {
+        def y = 555
+        def boo = {x = y -> x}
+        assert boo() == y
+        assert boo(1) == 1
+    }
+
+}
+
diff --git a/groovy/src/test/groovy/ClosureInClosureTest.groovy b/groovy/src/test/groovy/ClosureInClosureTest.groovy
new file mode 100644
index 0000000..df135e3
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureInClosureTest.groovy
@@ -0,0 +1,31 @@
+package groovy
+
+/**
+ * Bug illustrating the nested closures variable scope visibility issue.
+ * l.each is ClosureInClosureBug$1 and it.each is ClosureInClosureBug$2
+ * The variable text is not visible from ClosureInClosureBug$2.
+ * Indeed, a closure can only see the variable defined outside this closure (one level up)
+ * but cannot see what's in the second level.
+ *
+ * In order to make the test work, do not forget to uncomment the line "println(text)"
+ *
+ * @authour Guillaume Laforge
+ */
+class ClosureInClosureTest extends GroovyTestCase {
+	void testInvisibleVariable() {
+		def text = "test "
+
+		def l = [1..11, 2..12, 3..13, 4..14]
+
+		l.each{
+			it.each{
+			    println(text)
+			}
+		}
+	}
+
+	static void main(args) {
+		def bug = new ClosureInClosureTest()
+		bug.testInvisibleVariable()
+	}
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ClosureInStaticMethodTest.groovy b/groovy/src/test/groovy/ClosureInStaticMethodTest.groovy
new file mode 100644
index 0000000..8ffaa05
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureInStaticMethodTest.groovy
@@ -0,0 +1,47 @@
+package groovy
+
+/** 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureInStaticMethodTest extends GroovyTestCase {
+
+    void testClosureInStaticMethod() {
+        def closure = closureInStaticMethod()
+        assertClosure(closure)    
+    }
+
+    void testMethodClosureInStaticMethod() {
+        def closure = methodClosureInStaticMethod()
+        assertClosure(closure)    
+    }
+    
+    static def closureInStaticMethod() {
+        return { println(it) }
+    }
+
+    static def methodClosureInStaticMethod() {
+        System.out.&println
+    }
+    
+    static def assertClosure(Closure block) {
+        assert block != null
+        block.call("hello!")
+    }
+    
+    void testClosureInStaticMethodCallingStaticMethod() {
+       assert doThing(1) == 10
+       assert this.doThing(1) == 10
+       assert ClosureInStaticMethodTest.doThing(1) == 10
+    }
+    
+    
+    static doThing(count) {
+      def ret = count
+      if (count > 2) return ret
+      count.times {
+        ret += doThing(count+it+1)
+      }
+      return ret
+    }
+}
diff --git a/groovy/src/test/groovy/ClosureListenerTest.groovy b/groovy/src/test/groovy/ClosureListenerTest.groovy
new file mode 100644
index 0000000..f48875a
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureListenerTest.groovy
@@ -0,0 +1,57 @@
+package groovy
+
+import javax.swing.JButton
+
+/**
+ * @version $Revision$
+ */
+class ClosureListenerTest extends GroovyTestCase {
+     
+    void testAddingAndRemovingAClosureListener() {
+        def value = System.getProperty('java.awt.headless')
+        println("Value of java.awt.headless = ${value}")
+        
+        def b = new JButton("foo")
+        b.actionPerformed = { println("Found ${it}") }
+
+        def size = b.actionListeners.size()
+        assert size == 1
+        
+        def l = b.actionListeners[0]
+		def code = l.hashCode()
+        
+        println("listener: ${l} with hashCode code ${code}")
+        
+        assert l.toString() != "null"
+        
+        assert l.equals(b) == false
+        assert l.equals(l)
+        
+        assert l.hashCode() != 0
+        
+        b.removeActionListener(l)
+        
+        println(b.actionListeners)
+        
+        size = b.actionListeners.size()
+        assert size == 0
+    }
+    
+    void testGettingAListenerProperty() {
+    	def b = new JButton("foo")
+    	def foo = b.actionPerformed
+    	assert foo == null
+    }
+    
+    void testNonStandardListener() {
+        def myWhat = null
+        def myWhere = null
+
+        def strangeBean = new StrangeBean()
+        strangeBean.somethingStrangeHappened = { what, where -> myWhat = what; myWhere = where}
+        strangeBean.somethingStrangeHappened('?', '!')
+    
+        assert myWhat == '?'
+        assert myWhere == '!'
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ClosureMethodCallTest.groovy b/groovy/src/test/groovy/ClosureMethodCallTest.groovy
new file mode 100644
index 0000000..a9e60cd
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureMethodCallTest.groovy
@@ -0,0 +1,49 @@
+package groovy
+
+/** 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureMethodCallTest extends GroovyTestCase {
+
+    void testCallingClosureWithMultipleArguments() {
+        def foo
+        def closure = { a, b -> foo = "hello ${a} and ${b}".toString() }
+        
+        closure("james", "bob")
+
+        assert foo == "hello james and bob"
+
+        closure.call("sam", "james")
+
+        assert foo == "hello sam and james"
+    }
+    
+    void testClosureCallMethodWithObjectArray() {
+      // GROOVY-2266
+      def args = [1] as Object[]
+      def closure = {x -> x}
+      assert closure.call(args) == 1
+    }
+    
+    void testClosureAsLocalVar() {
+        def local = { Map params -> params.x * params.y  }
+        assert local(x : 2, y : 3) == 6
+    }
+    
+    void testClosureDirectly() {
+        assert { Map params -> params.x * params.y }(x : 2, y : 3) == 6
+    }
+    
+    def attribute
+    
+    void testClosureAsAttribute() {
+        attribute = { Map params ->  params.x * params.y  } 
+        assert attribute(x : 2, y : 3) == 6
+    }
+    
+    void testSystemOutPrintlnAsAClosure() {
+        def closure = System.out.&println
+        closure("Hello world")
+    }
+}
diff --git a/groovy/src/test/groovy/ClosureMethodTest.groovy b/groovy/src/test/groovy/ClosureMethodTest.groovy
new file mode 100644
index 0000000..136a368
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureMethodTest.groovy
@@ -0,0 +1,219 @@
+package groovy
+
+/** 
+ * Tests the various Closure methods in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureMethodTest extends GroovyTestCase {
+
+    void testListCollect() {
+        def list = [1, 2, 3, 4]
+        def answer = list.collect{item -> return item * 2 }
+        assert answer.size() == 4
+        def expected = [2, 4, 6, 8]
+        assert answer == expected
+    }
+
+    void testMapCollect() {
+        def map = [1:2, 2:4, 3:6, 4:8]
+        def answer = map.collect{e-> return e.key + e.value }
+        // lest sort the results since maps are in hash code order
+        answer = answer.sort()
+        assert answer.size() == 4
+        assert answer == [3, 6, 9, 12]
+        assert answer.get(0) == 3
+        assert answer.get(1) == 6
+        assert answer.get(2) == 9
+        assert answer.get(3) == 12
+    }
+
+    void testListFind() {
+        def list = ["a", "b", "c"]
+        def answer = list.find{item-> item == "b" }
+        assert answer == "b"
+        answer = list.find{item-> item == "z" }
+        assert answer == null
+    }
+
+    void testMapFind() {
+        def map = [1:2, 2:4, 3:6, 4:8]
+        def answer = map.find{entry -> entry.value == 6 }
+        assert answer != null
+        assert answer.key == 3
+        assert answer.value == 6
+        answer = map.find{entry -> entry.value == 0 }
+        assert answer == null
+        answer = map.find{ k, v -> v > 5 }
+        assert answer instanceof Map.Entry
+        assert answer.key == 3
+        assert answer.value == 6
+
+        answer = map.find{ k, v -> k == 2 }
+        assert answer instanceof Map.Entry
+        assert answer.key == 2
+        assert answer.value == 4
+    }
+
+    void testListFindAll() {
+        def list = [20, 5, 40, 2]
+        def answer = list.findAll{item -> item < 10 }
+        assert answer.size() == 2
+        assert answer == [5, 2]
+    }
+
+    void testMapFindAll() {
+        def map = [1:2, 2:4, 3:6, 4:8]
+        def answer = map.findAll{ entry -> entry.value > 5 }
+        assert answer.size() == 2
+        def keys = answer.collect { entry -> entry.key }
+        def values = answer.collect { entry -> entry.value }
+        // maps are in hash order so lets sort the results
+        keys.sort()
+        values.sort()
+        assert keys == [3, 4], "Expected [3, 4] but was $keys"
+        assert values == [6, 8], "Expected [6, 8] but was $values"
+    }
+
+    void testMapEach() {
+        def count = 0
+        def map = [1:2, 2:4, 3:6, 4:8]
+        map.each{e-> count = count + e.value }
+        assert count == 20
+        map.each{e-> count = count + e.value + e.key }
+        assert count == 50
+    }
+
+    void testMapEachWith2Params() {
+        def count = 0
+        def map = [1:2, 2:4, 3:6, 4:8]
+        map.each {key, value -> count = count + value }
+        assert count == 20
+        map.each {key, value -> count = count + value + key }
+        assert count == 50
+    }
+
+    void testListEach() {
+        def count = 0
+        def list = [1, 2, 3, 4]
+        list.each({item-> count = count + item })
+        assert count == 10
+        list.each{item-> count = count + item }
+        assert count == 20
+    }
+
+    void testListEvery() {
+        assert [1, 2, 3, 4].every {i-> return i < 5 }
+        assert [1, 2, 7, 4].every {i-> i < 5 } == false
+        assert [a:1, b:2, c:3].every {k,v -> k < 'd' && v < 4 }
+        assert ![a:1, b:2, c:3].every {k,v -> k < 'd' && v < 3 }
+    }
+
+    void testListAny() {
+        assert [1, 2, 3, 4].any {i-> return i < 5 }
+        assert [1, 2, 3, 4].any {i-> i > 3 }
+        assert [1, 2, 3, 4].any {i-> i > 5 } == false
+        assert [a:1, b:2, c:3].any { k,v -> k == 'c' }
+        def isThereAFourValue = [a:1, b:2, c:3].any{ k,v -> v == 4 }
+        assert !isThereAFourValue
+    }
+
+    void testJoin() {
+        def value = [1, 2, 3].join('-')
+        assert value == "1-2-3"
+    }
+
+    void testListReverse() {
+        def value = [1, 2, 3, 4].reverse()
+        assert value == [4, 3, 2, 1]
+    }
+
+    void testListInject() {
+        def value = [1, 2, 3].inject('counting: ') { str, item -> str + item }
+        assert value == "counting: 123"
+        value = [1, 2, 3].inject(0) { c, item -> c + item }
+        assert value == 6
+        value = ([1, 2, 3, 4] as Object[]).inject(0) { c, item -> c + item }
+        assert value == 10
+    }
+
+    void testObjectInject() {
+        def value = [1:1, 2:2, 3:3].inject('counting: ') { str, item -> str + item.value }
+        assert value == "counting: 123"
+        value = [1:1, 2:2, 3:3].inject(0) { c, item -> c + item.value }
+        assert value == 6
+    }
+
+    void testIteratorInject() {
+        def value = [1:1, 2:2, 3:3].iterator().inject('counting: ') { str, item -> str + item.value }
+        assert value == "counting: 123"
+        value = [1:1, 2:2, 3:3].iterator().inject(0) { c, item -> c + item.value }
+        assert value == 6
+    }
+
+    void testDump() {
+        def text = dump()
+        println("Dumping object ${text}")
+        assert text != null && text.startsWith("<")
+    }
+
+    void testInspect() {
+        def text = [1, 2, 'three'].inspect()
+        println("Inspecting ${text}")
+        assert text == '[1, 2, "three"]'
+    }
+
+    void testEachLine() {
+        def file = new File("src/test/groovy/Bar.groovy")
+        if(file.exists() == false) {
+            file = new File("Bar.groovy")
+        }
+        file.eachLine { line -> println(line) }
+    }
+
+    void testForEachLine() {
+        def file = new File("src/test/groovy/Bar.groovy")
+        if(file.exists() == false) {
+            file = new File("Bar.groovy")
+        }
+        shouldFail (DeprecationException) {
+          for (line in file) { println(line) }
+        }
+    }
+
+    void testReadLines() {
+        def file = new File("src/test/groovy/Bar.groovy")
+        if(file.exists() == false) {
+            file = new File("Bar.groovy")
+        }
+        def lines = file.readLines()
+        assert lines != null
+        assert lines.size() > 0, "File has: ${lines.size()} line(s)"
+    }
+
+    void testEachFile() {
+        def file = new File("src/test/groovy")
+        if(!file.exists()) {
+            file = new File(".")
+        }
+        println("Closure loop to display contents of dir: " + file)
+        file.eachFile { f -> println(f.getName()) }
+        println("")
+    }
+    
+    void testTokenize() {
+        def text = "hello-there-how-are-you"
+        def answer = []
+        for (i in text.tokenize('-')) {
+            answer.add(i)
+        }
+        assert answer == ['hello', 'there', 'how', 'are', 'you']
+    }
+    
+    void testUpto() {
+        def answer = []
+        1.upto(5) { answer.add(it) }
+        assert answer == [1, 2, 3, 4, 5]
+    }
+}
diff --git a/groovy/src/test/groovy/ClosureReturnTest.groovy b/groovy/src/test/groovy/ClosureReturnTest.groovy
new file mode 100644
index 0000000..e12ccde
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureReturnTest.groovy
@@ -0,0 +1,34 @@
+package groovy
+
+/** 
+ * Tests Closures in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureReturnTest extends GroovyTestCase {
+
+    void testReturnValues() {
+        def block = {x-> return x > 5}
+
+        def value = block.call(10)
+        assert value
+
+        value = block.call(3)
+        assert value == false
+    }
+
+    void testReturnValueUsingFunction() {
+        def block = {x-> return someFunction(x) }
+        
+        def value = block.call(10)
+        assert value
+
+        value = block.call(3)
+        assert value == false
+    }
+    
+    def someFunction(x) {
+        return x > 5
+    }
+}
diff --git a/groovy/src/test/groovy/ClosureReturnWithoutReturnStatementTest.groovy b/groovy/src/test/groovy/ClosureReturnWithoutReturnStatementTest.groovy
new file mode 100644
index 0000000..38b0960
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureReturnWithoutReturnStatementTest.groovy
@@ -0,0 +1,28 @@
+package groovy
+
+class ClosureReturnWithoutReturnStatementTest extends GroovyTestCase {
+
+    void testReturnValues() {
+        def block = {x-> x > 5}
+
+        def value = block.call(10)
+        assert value
+
+        value = block.call(3)
+        assert value == false
+    }
+
+    void testReturnValueUsingFunction() {
+        def block = {x-> someFunction(x) }
+        
+        def value = block.call(10)
+        assert value
+
+        value = block.call(3)
+        assert value == false
+    }
+    
+    def someFunction(x) {
+        x > 5
+    }
+}
diff --git a/groovy/src/test/groovy/ClosureSugarTest.groovy b/groovy/src/test/groovy/ClosureSugarTest.groovy
new file mode 100644
index 0000000..cdb7bea
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureSugarTest.groovy
@@ -0,0 +1,35 @@
+package groovy
+
+class ClosureSugarTest extends GroovyTestCase {
+
+    def count;
+
+    void testClosureSugar() {
+        count = 11;
+
+        sugar {
+             count = 20;
+        }
+
+        assert count == 20;
+    }
+
+    void testMixedClosureSugar() {
+        def count = 11;
+
+        mixedSugar (5){a->
+             count = count + a;
+        }
+
+        assert count == 16;
+
+    }
+
+    def mixedSugar(incrBy, Closure closure) {
+        closure.call( incrBy ); 
+    }
+
+    def sugar(Closure closure) {
+        closure.call();
+    }
+}
diff --git a/groovy/src/test/groovy/ClosureTest.groovy b/groovy/src/test/groovy/ClosureTest.groovy
new file mode 100644
index 0000000..95a8a30
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureTest.groovy
@@ -0,0 +1,154 @@
+package groovy
+
+/** 
+ * Tests Closures in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureTest extends GroovyTestCase {
+
+    def count
+
+    void testSimpleBlockCall() {
+        count = 0
+
+        def block = {owner-> owner.incrementCallCount() }
+
+        assertClosure(block)
+        assert count == 1
+
+        assertClosure({owner-> owner.incrementCallCount() })
+        assert count == 2
+    }
+
+    void testVariableLengthParameterList() {
+
+        def c1 = {Object[] args -> args.each{count += it}}
+
+        count = 0
+        c1(1, 2, 3)
+        assert count == 6
+
+        count = 0
+        c1(1)
+        assert count == 1
+
+        count = 0
+        c1([1, 2, 3] as Object[])
+        assert count == 6
+
+        def c2 = {a, Object[] args -> count += a; args.each{count += it}}
+
+        count = 0
+        c2(1, 2, 3)
+        assert count == 6
+
+        count = 0
+        c2(1)
+        assert count == 1
+
+        count = 0
+        c2(1, [2, 3] as Object[])
+        assert count == 6
+    }
+
+    void testBlockAsParameter() {
+        count = 0
+
+        callBlock(5, {owner-> owner.incrementCallCount() })
+        assert count == 6
+
+        callBlock2(5, {owner-> owner.incrementCallCount() })
+        assert count == 12
+    }
+  
+    void testMethodClosure() {
+        def block = this.&incrementCallCount
+
+        count = 0
+
+        block.call()
+
+        assert count == 1
+
+        block = System.out.&println
+
+        block.call("I just invoked a closure!")
+    }
+  
+    def incrementCallCount() {
+        //System.out.println("invoked increment method!")
+        count = count + 1
+    }
+
+    def assertClosure(Closure block) {
+        assert block != null
+        block.call(this)
+    }
+
+    protected void callBlock(Integer num, Closure block) {
+        for ( i in 0..num ) {
+            block.call(this)
+        }
+    }
+
+    protected void callBlock2(num, block) {
+        for ( i in 0..num ) {
+            block.call(this)
+        }
+    }
+
+
+    int numAgents = 4
+    boolean testDone = false
+
+    void testIntFieldAccess() {
+        def agents = new ArrayList();
+        numAgents.times {
+            TinyAgent btn = new TinyAgent()
+            testDone = true
+            btn.x = numAgents
+            agents.add(btn)
+        }
+        assert agents.size() == numAgents
+    }
+
+    void testWithIndex() {
+        def str = ''
+        def sum = 0
+        ['a','b','c','d'].eachWithIndex { item, index -> str += item; sum += index }
+        assert str == 'abcd' && sum == 6
+    }
+
+    /**
+    * Test access to Closure's properties
+    * cf GROOVY-2089
+    */
+    void testProperties() {
+        def c = { println it }
+
+        assert 1 == c.getMaximumNumberOfParameters()
+        assert 1 == c.maximumNumberOfParameters
+        shouldFail {
+            assert 1 == c.getMaximumNumberOfParameters // worked in Groovy 1.0 but is wrong
+        }
+        
+        assert 0 == c.getDirective()
+        assert 0 == c.directive
+    }
+    
+    /**
+     * GROOVY-2150 ensure list call is available on closure
+     */
+    void testCallClosureWithlist() {
+      def list = [1,2]
+      def cl = {a,b->a+b }
+      assert cl(list)==3
+    }
+}
+
+public class TinyAgent {
+    int x
+}
+
diff --git a/groovy/src/test/groovy/ClosureUsingOuterVariablesTest.groovy b/groovy/src/test/groovy/ClosureUsingOuterVariablesTest.groovy
new file mode 100644
index 0000000..5bd170c
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureUsingOuterVariablesTest.groovy
@@ -0,0 +1,71 @@
+package groovy
+
+/** 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureUsingOuterVariablesTest extends GroovyTestCase {
+    
+    void testUseOfOuterVariable() {
+        
+        def x = 123
+        def y = "hello"
+        
+        def closure = { i ->
+            println("x ${x}")
+            println("y ${y}")
+            println("i ${i}")
+                
+            assert x == 123
+            assert y == 'hello'
+        }
+        closure.call(321)
+	}
+
+     /*
+     TODO: is this a valid test case?
+     void testInnerVariablesVisibleInOuterScope() {
+                
+        closure = { z = 456 } 
+        closure.call(321)
+        
+        assert z == 456
+    }
+    */
+    
+    void testModifyingOuterVariable() {
+        
+        def m = 123
+        
+        def closure = { m = 456 } 
+        closure.call(321)
+        
+        assert m == 456
+    }
+    
+    void testCounting() {
+        def sum = 0
+
+        [1, 2, 3, 4].each { sum = sum + it }
+
+        assert sum == 10
+    }
+    
+    void testExampleUseOfClosureScopes() {
+        def a = 123
+        def b
+        def c = { b = a + it }
+        c(5)
+        
+        println(b)
+        assert b == a + 5
+    }    
+
+    void testExampleUseOfClosureScopesUsingEach() {
+        def a = 123
+        def b
+        [5].each { b = a + it }
+
+        assert b == a + 5
+    }
+}
diff --git a/groovy/src/test/groovy/ClosureWithDefaultParamTest.groovy b/groovy/src/test/groovy/ClosureWithDefaultParamTest.groovy
new file mode 100644
index 0000000..2faf16d
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureWithDefaultParamTest.groovy
@@ -0,0 +1,160 @@
+package groovy
+
+/** 
+ * Demonstrates the use of the default named parameter in a closure
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureWithDefaultParamTest extends GroovyTestCase {
+
+    void methodWithDefaultParam(example='default'){
+        assert 'default' == example
+    }
+
+    void testListCollect() {
+        def list = [1, 2, 3, 4]
+        def answer = list.collect { it * 2 }
+
+        assert answer.size() == 4
+        
+        def expected = [2, 4, 6, 8]
+        assert answer == expected
+    }
+
+    void testMapCollect() {
+        def map = [1:2, 2:4, 3:6, 4:8]
+        def answer = map.collect { it.key + it.value }
+		
+		// lest sort the results since maps are in hash code order
+		answer = answer.sort()
+		
+        assert answer.size() == 4
+        assert answer == [3, 6, 9, 12]
+        assert answer.get(0) == 3
+        assert answer.get(1) == 6
+        assert answer.get(2) == 9
+        assert answer.get(3) == 12
+    }
+
+    void testListFind() {
+        def list = ["a", "b", "c"]
+        def answer = list.find {it == "b" }
+        assert answer == "b"
+        
+        answer = list.find {it == "z" }
+        assert answer == null
+    }
+    
+    void testMapFind() {
+        def map = [1:2, 2:4, 3:6, 4:8]
+        def answer = map.find {it.value == 6 }
+        assert answer != null
+        assert answer.key == 3
+        assert answer.value == 6
+        
+        answer = map.find {it.value == 0 }
+        assert answer == null
+    }
+
+    void testListFindAll() {
+        def list = [20, 5, 40, 2]
+        def answer = list.findAll {it < 10 }
+
+        assert answer.size() == 2
+        assert answer == [5, 2]
+    }
+    
+    void testMapFindAll() {
+        def map = [1:2, 2:4, 3:6, 4:8]
+        def answer = map.findAll {it.value > 5 }
+
+        assert answer.size() == 2
+        
+        def keys = answer.collect {it.key }
+        def values = answer.collect {it.value }
+
+        System.out.println("keys " + keys + " values " + values)
+		
+        // maps are in hash order so lets sort the results       
+        keys.sort() 
+        values.sort() 
+        
+        assert keys == [3, 4]
+        assert values == [6, 8]
+    }
+
+    void testListEach() {
+        def count = 0
+
+        def list = [1, 2, 3, 4]
+        list.each { count = count + it }
+		
+        assert count == 10
+
+        list.each { count = count + it }
+		
+        assert count == 20
+    }
+
+    void testMapEach() {
+        def count = 0
+
+        def map = [1:2, 2:4, 3:6, 4:8]
+        map.each { count = count + it.value }
+
+        assert count == 20
+    }
+    
+    void testListEvery() {
+        assert [1, 2, 3, 4].every { it < 5 }
+        assert [1, 2, 7, 4].every { it < 5 } == false
+    }
+
+    void testListAny() {
+        assert [1, 2, 3, 4].any { it < 5 }
+        assert [1, 2, 3, 4].any { it > 3 }
+        assert [1, 2, 3, 4].any { it > 5 } == false
+    }
+    
+    void testJoin() {
+        def value = [1, 2, 3].join('-')
+        assert value == "1-2-3"
+    }
+    
+    void testListReverse() {
+        def value = [1, 2, 3, 4].reverse()
+        assert value == [4, 3, 2, 1]
+    }
+    
+    void testEachLine() {
+        def file = new File("src/test/groovy/Bar.groovy")
+        
+        System.out.println("Contents of file: " + file)
+        
+        file.eachLine { println(it) }
+        
+        println("")
+    }
+    
+    void testReadLines() {
+        def file = new File("src/test/groovy/Bar.groovy")
+
+		def lines = file.readLines()
+		
+		assert lines != null
+		assert lines.size() > 0
+
+        System.out.println("File has number of lines: " + lines.size())
+    }
+    
+    void testEachFile() {
+        def file = new File("src/test/groovy")
+        
+        System.out.println("Contents of dir: " + file)
+        
+        file.eachFile { println(it.getName()) }
+        
+        println("")
+    }
+}
diff --git a/groovy/src/test/groovy/ClosureWithEmptyParametersTest.groovy b/groovy/src/test/groovy/ClosureWithEmptyParametersTest.groovy
new file mode 100644
index 0000000..2cf8deb
--- /dev/null
+++ b/groovy/src/test/groovy/ClosureWithEmptyParametersTest.groovy
@@ -0,0 +1,21 @@
+package groovy
+
+/** 
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @version $Revision$
+ */
+class ClosureWithEmptyParametersTest extends GroovyTestCase {
+
+    void testNoParams() {
+
+        def block = {-> println "hey I'm a closure!" }
+
+        println "About to call closure"
+
+        block.call()
+
+        println "Done"
+    }
+
+}
+
diff --git a/groovy/src/test/groovy/CompareEqualsTest.groovy b/groovy/src/test/groovy/CompareEqualsTest.groovy
new file mode 100644
index 0000000..65dbcfa
--- /dev/null
+++ b/groovy/src/test/groovy/CompareEqualsTest.groovy
@@ -0,0 +1,30 @@
+package groovy
+
+class CompareEqualsTest extends GroovyTestCase {
+    void testEqualsOperatorIsMultimethodAware() {
+        assert new Xyz() == new Xyz()
+
+        assertEquals new Xyz(), new Xyz()
+
+        shouldFail {
+          assert new Xyz() == 239
+        }
+        assert new Xyz () == "GROOVY.XYZ"
+    }
+}
+
+class Xyz {
+    boolean equals(Xyz other) {
+        println "${other.class} TRUE"
+        true
+    }
+
+    boolean equals(Object other) {
+        println "${other.class} FALSE"
+        null
+    }
+
+    boolean equals(String str) {
+        str.equalsIgnoreCase this.class.getName()
+    }
+}
diff --git a/groovy/src/test/groovy/CompareToTest.groovy b/groovy/src/test/groovy/CompareToTest.groovy
new file mode 100644
index 0000000..962048f
--- /dev/null
+++ b/groovy/src/test/groovy/CompareToTest.groovy
@@ -0,0 +1,39 @@
+package groovy
+
+class CompareToTest extends GroovyTestCase {
+
+    void testCompareTo() {
+
+        def a = 12
+        def b = 20
+        def c = 30
+        
+        def result = a <=> b
+        assert result < 0
+
+        result = a <=> 12
+        assert result == 0
+
+        result = c <=> b
+        assert result > 0
+        
+        assert (a <=> b) < 0
+        assert a <=> 12 == 0
+        assert (c <=> b) > 0
+    }
+
+    void testNullCompares() {
+    
+    	def a = 123
+    	def b = null
+    	
+    	def result = a <=> b
+    	assert result > 0
+    	
+    	result = b <=> a
+    	assert result < 0
+    	
+    	result = b <=> null
+    	assert result == 0
+    }
+}
diff --git a/groovy/src/test/groovy/CompareTypesTest.groovy b/groovy/src/test/groovy/CompareTypesTest.groovy
new file mode 100644
index 0000000..c7749d8
--- /dev/null
+++ b/groovy/src/test/groovy/CompareTypesTest.groovy
@@ -0,0 +1,58 @@
+package groovy
+
+/**
+ * @version $Revision$
+ */
+class NumberTest extends GroovyTestCase { 
+    void testCompareByteToInt() { 
+        Byte a = 12
+        Integer b = 10
+        
+        assert a instanceof Byte
+        assert b instanceof Integer
+        
+        assert a > b
+    } 
+    
+    void testCompareByteToDouble() { 
+        Byte a = 12
+        Double b = 10
+        
+        assert a instanceof Byte
+        assert b instanceof Double
+        
+        assert a > b
+    } 
+     
+    void testCompareLongToDouble() { 
+        Long a = 12
+        Double b = 10
+        
+        assert a instanceof Long
+        assert b instanceof Double
+        
+        assert a > b
+    } 
+     
+    void testCompareLongToByte() { 
+        Long a = 12
+        Byte b = 10
+        
+        assert a instanceof Long
+        assert b instanceof Byte
+        
+        assert a > b
+    } 
+     
+    void testCompareIntegerToByte() { 
+        Integer a = 12
+        Byte b = 10
+        
+        assert a instanceof Integer
+        assert b instanceof Byte
+        
+        assert a > b
+    } 
+} 
+
+
diff --git a/groovy/src/test/groovy/CompileOrderTest.groovy b/groovy/src/test/groovy/CompileOrderTest.groovy
new file mode 100644
index 0000000..89ac13e
--- /dev/null
+++ b/groovy/src/test/groovy/CompileOrderTest.groovy
@@ -0,0 +1,35 @@
+package groovy

+

+class CompileOrderTest extends GroovyTestCase {

+   public void testCompileOrder() {

+      def interfaceFile = File.createTempFile("TestOrderInterface", ".groovy", new File("target"))

+      def concreteFile = File.createTempFile("TestOrderConcrete", ".groovy", new File("target"))

+

+      def cl = new GroovyClassLoader(this.class.classLoader);

+      def currentDir = concreteFile.parentFile.absolutePath

+      println currentDir

+      cl.addClasspath(currentDir)

+      cl.shouldRecompile = true

+

+      try {

+         // Create the interface

+         println "a"

+         interfaceFile.deleteOnExit()

+         def interfaceName = interfaceFile.name - ".groovy"

+         interfaceFile.write "interface $interfaceName { }\n"

+

+         // Create a concrete class which implements the interface

+         concreteFile.deleteOnExit()

+         def concreteName = concreteFile.name - ".groovy"

+         concreteFile.write "class $concreteName implements $interfaceName { }\n"

+

+         // We're testing whether this fails:

+         def groovyClass = cl.loadClass(concreteName,true,false)

+         // Create an object, just for good measure.

+         def object = groovyClass.newInstance()

+      } finally {

+         interfaceFile.delete()

+         concreteFile.delete()

+      }

+   }

+}

diff --git a/groovy/src/test/groovy/CompilerErrorTest.groovy b/groovy/src/test/groovy/CompilerErrorTest.groovy
new file mode 100644
index 0000000..7553f9f
--- /dev/null
+++ b/groovy/src/test/groovy/CompilerErrorTest.groovy
@@ -0,0 +1,39 @@
+package groovy
+
+class CompilerErrorTest extends GroovyTestCase {
+
+    void testBadMethodName() {
+
+        shouldFail {
+            println "About to call shell script"
+            println "Really am about to call shell script"
+
+            def shell = new GroovyShell()
+            def text = 'badMethod(); println "Called method"'
+            println "About to test script ${text}"
+            shell.evaluate(text)
+        }
+    }
+
+    void testBadPropertyName() {
+
+        shouldFail {
+            def shell = new GroovyShell()
+            shell.evaluate """
+                def x = [:]
+                x.\$foo = 123
+            """
+        }
+    }
+
+    void testBadVariableName() {
+
+        shouldFail {
+            def shell = new GroovyShell()
+            shell.evaluate """
+                def \$x = 123
+            """
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/Constructor2Test.groovy b/groovy/src/test/groovy/Constructor2Test.groovy
new file mode 100644
index 0000000..25c027d
--- /dev/null
+++ b/groovy/src/test/groovy/Constructor2Test.groovy
@@ -0,0 +1,15 @@
+package groovy
+
+class Constructor2Test extends GroovyTestCase {
+
+    Constructor2Test() {
+        println "Hey"
+    }
+
+    void testConstructor() {
+        def foo = new Constructor2Test()
+        assert foo != null
+        println foo
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ConstructorTest.groovy b/groovy/src/test/groovy/ConstructorTest.groovy
new file mode 100644
index 0000000..723d655
--- /dev/null
+++ b/groovy/src/test/groovy/ConstructorTest.groovy
@@ -0,0 +1,15 @@
+package groovy
+
+class ConstructorTest extends GroovyTestCase {
+
+    public ConstructorTest() {
+        println "Hey"
+    }
+
+    public void testConstructor() {
+        def foo = new ConstructorTest()
+        assert foo != null
+        println foo
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/CurlyBracketLayoutTest.groovy b/groovy/src/test/groovy/CurlyBracketLayoutTest.groovy
new file mode 100644
index 0000000..1c0e0db
--- /dev/null
+++ b/groovy/src/test/groovy/CurlyBracketLayoutTest.groovy
@@ -0,0 +1,24 @@
+package groovy
+
+class CurlyBracketLayoutTest extends GroovyTestCase
+{
+    void testBracketPlacement()
+    {
+        def foo = "abc"
+
+        if (foo.contains("b"))
+        {
+            println "Worked a treat. foo = $foo"
+        }
+        else
+        {
+            fail("Should have found 'b' inside $foo")
+        }
+
+        def list = [1, 2, 3]
+        list.each
+        {
+            println it
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/DateTest.groovy b/groovy/src/test/groovy/DateTest.groovy
new file mode 100644
index 0000000..aec2cf8
--- /dev/null
+++ b/groovy/src/test/groovy/DateTest.groovy
@@ -0,0 +1,33 @@
+package groovy
+
+class DateTest extends GroovyTestCase {
+  
+    void testNextPrevious() {
+        def x = new Date()
+        def y = x + 2
+        
+        assert x < y
+        ++x
+        --y
+        
+        assert x == y
+        x += 2
+        assert x > y
+        
+        println "have dates ${x} and ${y}"
+    }
+    
+    void testDateRange() {
+        
+        def today = new Date()
+        def later = today + 3
+        
+        def expected = [today, today + 1, today + 2, today + 3]
+        
+        def list = []
+        for (d in today..later) {
+            list << d
+        }
+        assert list == expected
+    }
+}
diff --git a/groovy/src/test/groovy/DefaultParamClosureTest.groovy b/groovy/src/test/groovy/DefaultParamClosureTest.groovy
new file mode 100644
index 0000000..ed1115f
--- /dev/null
+++ b/groovy/src/test/groovy/DefaultParamClosureTest.groovy
@@ -0,0 +1,44 @@
+package groovy
+
+class DefaultParamClosureTest extends GroovyTestCase {
+
+    void testDefaultParameters() {
+        // Default parameters working for closures 
+	def doSomething = { a, b = 'defB', c = 'defC' ->
+			println "Called with a: ${a}, b ${b}, c ${c}"
+			return a + "-" + b + "-" + c
+		}
+
+    	def value = doSomething("X", "Y", "Z")
+    	assert value == "X-Y-Z"
+
+    	value = doSomething("X", "Y")
+    	assert value == "X-Y-defC"
+
+    	value = doSomething("X")
+    	assert value == "X-defB-defC"
+
+    	shouldFail { doSomething() }
+    }
+
+    void testDefaultTypedParameters() {
+	// Handle typed parameters
+	def doTypedSomething = { String a = 'defA', String b = 'defB', String c = 'defC' ->
+			println "Called typed method with a: ${a}, b ${b}, c ${c}"
+			return a + "-" + b + "-" + c
+		}
+	
+    	def value = doTypedSomething("X", "Y", "Z")
+    	assert value == "X-Y-Z"
+    	
+    	value = doTypedSomething("X", "Y")
+    	assert value == "X-Y-defC"
+    	
+    	value = doTypedSomething("X")
+    	assert value == "X-defB-defC"
+    	
+    	value = doTypedSomething()
+    	assert value == "defA-defB-defC"
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/DefaultParamTest.groovy b/groovy/src/test/groovy/DefaultParamTest.groovy
new file mode 100644
index 0000000..75cccb2
--- /dev/null
+++ b/groovy/src/test/groovy/DefaultParamTest.groovy
@@ -0,0 +1,90 @@
+package groovy
+
+class DefaultParamTest extends GroovyTestCase {
+
+    //GROOVY-2191
+
+    def m2191a(String one, String two = "two") {"$one $two"}
+    def m2191a(String one, String two = "two", String three = "three") {"$one $two $three"}
+    
+    def m2191b(String one, String two = "two", String three = "three") {"$one $two $three"}
+    def m2191b(String one, String two = "two") {"$one $two"}
+    
+    void test2191() {
+      assert m2191a("bar") == "bar two"
+      assert m2191b("bar") == "bar two three"
+    } 
+
+    void testDefaultParameters() {
+    
+    	def value = doSomething("X", "Y", "Z")
+    	assert value == "X-Y-Z"
+    	
+    	value = doSomething("X", "Y")
+    	assert value == "X-Y-defC"
+    	
+    	value = doSomething("X")
+    	assert value == "X-defB-defC"
+    	
+    	shouldFail { doSomething() }
+    }
+
+    void testDefaultTypedParameters() {
+    	def value = doTypedSomething("X", "Y", "Z")
+    	assert value == "X-Y-Z"
+    	
+    	value = doTypedSomething("X", "Y")
+    	assert value == "X-Y-defC"
+    	
+    	value = doTypedSomething("X")
+    	assert value == "X-defB-defC"
+    	
+    	value = doTypedSomething()
+    	assert value == "defA-defB-defC"
+    }
+
+    void testDefaultTypedParametersAnother() {
+    	def value = doTypedSomethingAnother("X", "Y", "Z")
+    	assert value == "X-Y-Z"
+    	
+    	value = doTypedSomethingAnother("X", "Z")
+    	assert value == "X-defB-Z"
+    	
+    	value = doTypedSomethingAnother("Z")
+    	assert value == "defA-defB-Z"
+    	
+    	shouldFail{ value = doTypedSomethingAnother() }
+    }
+
+
+    def doSomething(a, b = 'defB', c = 'defC') {
+        println "Called with a: ${a}, b ${b}, c ${c}"
+
+        return a + "-" + b + "-" + c
+    }
+
+    String doTypedSomething(String a = 'defA', String b = 'defB', String c = 'defC') {
+        println "Called typed method with a: ${a}, b ${b}, c ${c}"
+
+        return a + "-" + b + "-" + c
+    }
+
+    String doTypedSomethingAnother(String a = 'defA', String b = 'defB', String c) {
+        println "Called typed method with a: ${a}, b ${b}, c ${c}"
+
+        return a + "-" + b + "-" + c
+    }
+    
+    void testConstructor() {
+        assert DefaultParamTestTestClass.declaredConstructors.size() == 2
+        def foo = new DefaultParamTestTestClass()
+        assert foo.j == 1
+        foo = new DefaultParamTestTestClass(2)
+        assert foo.j == 2
+    }
+}
+
+class DefaultParamTestTestClass {
+  def j
+  DefaultParamTestTestClass(int i = 1){j=i}
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/DoWhileLoopTest.groovy b/groovy/src/test/groovy/DoWhileLoopTest.groovy
new file mode 100644
index 0000000..4913bfd
--- /dev/null
+++ b/groovy/src/test/groovy/DoWhileLoopTest.groovy
@@ -0,0 +1,24 @@
+package groovy
+
+class DoWhileLoopTest extends GroovyTestCase {
+
+    void testDoWhileWhile() {
+
+    /**
+
+      We currently do not support do ... while in the JSR syntax
+
+        def x = 0
+        def y = 5
+
+        do {
+            x = x + 1
+            y = y - 1
+        }
+        while ( y > 0 )
+
+        assert x == 5
+    */
+            
+    }
+}
diff --git a/groovy/src/test/groovy/DollarEscapingTest.groovy b/groovy/src/test/groovy/DollarEscapingTest.groovy
new file mode 100644
index 0000000..6b11cd6
--- /dev/null
+++ b/groovy/src/test/groovy/DollarEscapingTest.groovy
@@ -0,0 +1,14 @@
+package groovy
+
+class DollarEscapingTest extends GroovyTestCase {
+
+    void testEscaping() {
+        def foo = "hello \${foo}"
+        
+        assert foo instanceof String
+        
+        def c = foo.count('$')
+        
+        assert c == 1 , foo
+    }
+}
diff --git a/groovy/src/test/groovy/DownUpStepTest.groovy b/groovy/src/test/groovy/DownUpStepTest.groovy
new file mode 100644
index 0000000..ed997e9
--- /dev/null
+++ b/groovy/src/test/groovy/DownUpStepTest.groovy
@@ -0,0 +1,35 @@
+package groovy
+
+public class DownUpStepTest extends GroovyTestCase {
+
+    void testDownto() {
+        def z = []
+        (10.5).downto(5.9) { z << it }
+        assertEquals( [10.5, 9.5, 8.5, 7.5, 6.5], z)
+    }
+
+    void testBigIntegerDowntoBigDecimal() {
+        def z = []
+        10G.downto(5.9G) { z << it }
+        assertEquals( [10G, 9G, 8G, 7G, 6G], z)
+    }
+
+    void testUpto() {
+        def z = 0.0
+        (3.1).upto(7.2) { z += it }
+        assert z == 3.1 + 4.1 + 5.1 + 6.1 + 7.1
+        assert z == 25.5
+    }
+
+    void testStep() {
+        def z = 0.0
+        (1.2).step(3.9, 0.1) { z += it }
+        assert z == 67.5
+    }
+
+    void testDownStep() {
+        def z = 0.0
+        (3.8).step(1.1, -0.1) { z += it }
+        assert z == 67.5
+    }
+}
diff --git a/groovy/src/test/groovy/DummyInterface.java b/groovy/src/test/groovy/DummyInterface.java
new file mode 100644
index 0000000..37f9b3e
--- /dev/null
+++ b/groovy/src/test/groovy/DummyInterface.java
@@ -0,0 +1,6 @@
+package groovy;
+
+public interface DummyInterface {
+
+    void methodWithArrayParam(String[] args);
+}
diff --git a/groovy/src/test/groovy/DummyMethodsGroovy.groovy b/groovy/src/test/groovy/DummyMethodsGroovy.groovy
new file mode 100644
index 0000000..6abf14c
--- /dev/null
+++ b/groovy/src/test/groovy/DummyMethodsGroovy.groovy
@@ -0,0 +1,25 @@
+/**
+ * methods with specific parameters (e.g. primitives)
+ * for use with groovy tests
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+ 
+package groovy;
+
+public class DummyMethodsGroovy {
+    public static void main(String[] args) {
+        DummyMethodsGroovy tmp = new DummyMethodsGroovy();
+        String answer = tmp.foo("Hey", 1, 2);
+        System.out.println("Answer: " + answer);
+    }
+
+    public String foo(String a, float b, float c) {
+    	return "float args";
+    }
+
+    public String foo(String a, int b, int c) {
+    	return "int args";
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/DummyMethodsJava.java b/groovy/src/test/groovy/DummyMethodsJava.java
new file mode 100644
index 0000000..325f913
--- /dev/null
+++ b/groovy/src/test/groovy/DummyMethodsJava.java
@@ -0,0 +1,18 @@
+/**
+ * methods with specific parameters (e.g. primitives)
+ * for use with groovy tests
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+package groovy;
+
+public class DummyMethodsJava {
+    public String foo(String a, float b, float c) {
+        return "float args";
+    }
+
+    public String foo(String a, int b, int c) {
+        return "int args";
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/DynamicMemberTest.groovy b/groovy/src/test/groovy/DynamicMemberTest.groovy
new file mode 100644
index 0000000..3c6e3b8
--- /dev/null
+++ b/groovy/src/test/groovy/DynamicMemberTest.groovy
@@ -0,0 +1,49 @@
+package groovy
+
+class DynamicMemberTest extends GroovyTestCase {
+  def aTestMethod(o){o}
+  def aProperty
+  
+  public void testGStringMethodCall(){
+    def name = "aTestMethod"
+    assert this."$name"(1) == 1
+    assert this."${name}"(2) == 2
+    assert "$name"(3) == 3
+    assert "${name}"(4) == 4
+    name = "TestMethod"
+    assert this.("a"+"TestMethod")(5) == 5
+    assert this.("a"+name)(6) == 6
+  }
+  
+  public void testGStringPropertyAccess(){
+    def name = "aProperty"
+    this.aProperty = "foo"
+    assert this."$name" == "foo"
+    assert this."${name}" == "foo"
+    assert "$name" == "aProperty"
+    assert "${name}" == "aProperty"
+  }
+  
+  public void testStringMethodCallAndAttributeAccess() {
+    this.aProperty = "String"
+    assert this."aProperty" == "String"
+    assert this."aTestMethod"("String") == "String"
+    assert "aTestMethod"("String") == "String"
+  }
+  
+  public void testDynamicAttributeAccess() {
+    this.aProperty = "tada"
+    def name = "aProperty"
+    assert this.@"$name" == "tada"
+    assert this.@"${name}" == "tada"
+  }
+  
+  public void testDynamicMethodClosure() {
+    def cl = this.&"aTestMethod"
+    assert cl("String") == "String"
+    def name ="aTestMethod"
+    cl = this.&"$name"
+    assert cl("String") == "String"
+  }
+  
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/EscapedUnicodeTest.groovy b/groovy/src/test/groovy/EscapedUnicodeTest.groovy
new file mode 100644
index 0000000..5b0c8a4
--- /dev/null
+++ b/groovy/src/test/groovy/EscapedUnicodeTest.groovy
@@ -0,0 +1,26 @@
+\u0063\u006c\u0061\u0073\u0073\u0020\u0045\u0073\u0063\u0061\u0070\u0065\u0064\u0055\u006e\u0069\u0063\u006f\u0064\u0065\u0054\u0065\u0073\u0074\u0020\u0065\u0078\u0074\u0065\u006e\u0064\u0073\u0020\u0047\u0072\u006f\u006f\u0076\u0079\u0054\u0065\u0073\u0074\u0043\u0061\u0073\u0065\u0020\u007b
+
+\u0020\u0020\u0020\u0020\u0076\u006f\u0069\u0064\u0020\u0074\u0065\u0073\u0074\u0041\u0073\u0073\u0065\u0072\u0074\u0028\u0029\u0020\u007b
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0064\u0065\u0066\u0020\u0078\u0020\u003d\u0020\u0022\u0061\u0062\u0063\u0022
+
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0061\u0073\u0073\u0065\u0072\u0074\u0020\u0078\u0020\u0021\u003d\u0020\u0022\u0066\u006f\u006f\u0022
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0061\u0073\u0073\u0065\u0072\u0074\u0020\u0078\u0020\u0021\u003d\u0020\u0020\u006e\u0075\u006c\u006c
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0061\u0073\u0073\u0065\u0072\u0074\u0020\u0078\u0020\u0021\u003d\u0020\u0022\u0064\u0065\u0066\u0022
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0061\u0073\u0073\u0065\u0072\u0074\u0020\u0078\u0020\u003d\u003d\u0020\u0022\u0061\u0062\u0063\u0022
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0061\u0073\u0073\u0065\u0072\u0074\u0020\u0078\u002e\u0065\u0071\u0075\u0061\u006c\u0073\u0028\u0022\u0061\u0062\u0063\u0022\u0029
+\u0009\u007d
+\u0020\u0020\u0020\u0020
+\u0020\u0020\u0020\u0020\u0076\u006f\u0069\u0064\u0020\u0074\u0065\u0073\u0074\u0055\u006e\u006b\u006e\u006f\u0077\u006e\u0056\u0061\u0072\u0069\u0061\u0062\u006c\u0065\u0028\u0029\u0020\u007b
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0074\u0072\u0079\u0020\u007b
+\u0009\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0064\u0065\u0066\u0020\u0079\u0020\u003d\u0020\u0074\u0068\u0069\u0073\u002e\u0078
+\u0009\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0066\u0061\u0069\u006c\u0028\u0022\u0078\u0020\u0069\u0073\u0020\u0075\u006e\u0064\u0065\u0066\u0069\u006e\u0065\u0064\u002c\u0020\u0073\u0068\u006f\u0075\u006c\u0064\u0020\u0074\u0068\u0072\u006f\u0077\u0020\u0061\u006e\u0020\u0065\u0078\u0063\u0065\u0070\u0074\u0069\u006f\u006e\u0022\u0029
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u007d
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0063\u0061\u0074\u0063\u0068\u0020\u0028\u004d\u0069\u0073\u0073\u0069\u006e\u0067\u0050\u0072\u006f\u0070\u0065\u0072\u0074\u0079\u0045\u0078\u0063\u0065\u0070\u0074\u0069\u006f\u006e\u0020\u0065\u0029\u0020\u007b
+\u0009\u0009\u0009\u0061\u0073\u0073\u0065\u0072\u0074\u0020\u0065\u002e\u0067\u0065\u0074\u0050\u0072\u006f\u0070\u0065\u0072\u0074\u0079\u0028\u0029\u0020\u003d\u003d\u0020\u0022\u0078\u0022\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0064\u0065\u0066\u0020\u0074\u0065\u0078\u0074\u0020\u003d\u0020\u0065\u002e\u006d\u0065\u0073\u0073\u0061\u0067\u0065
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0061\u0073\u0073\u0065\u0072\u0074\u0020\u0074\u0065\u0078\u0074\u0020\u003d\u003d\u0020\u0022\u004e\u006f\u0020\u0073\u0075\u0063\u0068\u0020\u0070\u0072\u006f\u0070\u0065\u0072\u0074\u0079\u003a\u0020\u0078\u0020\u0066\u006f\u0072\u0020\u0063\u006c\u0061\u0073\u0073\u003a\u0020\u0045\u0073\u0063\u0061\u0070\u0065\u0064\u0055\u006e\u0069\u0063\u006f\u0064\u0065\u0054\u0065\u0073\u0074\u0022
+\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u0020\u007d
+\u0020\u0020\u0020\u0020\u007d
+\u0009\u0020\u0020\u0020\u0020
+\u007d
diff --git a/groovy/src/test/groovy/ExceptionInClosureTest.groovy b/groovy/src/test/groovy/ExceptionInClosureTest.groovy
new file mode 100644
index 0000000..c365469
--- /dev/null
+++ b/groovy/src/test/groovy/ExceptionInClosureTest.groovy
@@ -0,0 +1,26 @@
+package groovy
+
+/** 
+ * Tests exception handling inside of a closure
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ExceptionInClosureTest extends GroovyTestCase {
+
+    void testCallingOfFailedClosure() {
+        def closure = { it -> it.foo() }
+        
+        try {
+	        closure.call("cheese")
+	        
+	        fail("Should have thrown an exception by now")
+        }
+        catch (MissingMethodException e) {
+   			System.out.println("Caught: " + e)    
+   			
+   			assert e.method == "foo"
+			assert e.type == String   			
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/ExpandoPropertyTest.groovy b/groovy/src/test/groovy/ExpandoPropertyTest.groovy
new file mode 100644
index 0000000..c5c1a74
--- /dev/null
+++ b/groovy/src/test/groovy/ExpandoPropertyTest.groovy
@@ -0,0 +1,100 @@
+package groovy
+
+class ExpandoPropertyTest extends GroovyTestCase {
+
+    void testExpandoProperty() {
+        def foo = new Expando()
+        
+        foo.cheese = "Cheddar"
+        foo.name = "Gromit"
+        
+        assert foo.cheese == "Cheddar"
+        assert foo.name == "Gromit"
+        
+        assert foo.properties.size() == 2
+    }
+    
+    void testExpandoMethods() {
+        def foo = new Expando()
+
+        foo.cheese = "Cheddar"
+        foo.fullName = "Gromit"
+        foo.nameLength = { return fullName.length() }
+        foo.multiParam = { a, b, c -> println("Called with ${a}, ${b}, ${c}"); return a + b + c }
+
+        assert foo.cheese == "Cheddar"
+        assert foo.fullName == "Gromit"
+        assert foo.nameLength() == 6 , foo.nameLength()
+        assert foo.multiParam(1, 2, 3) == 6
+        
+        // lets test using wrong number of parameters
+        shouldFail { foo.multiParam(1) }
+        shouldFail { foo.nameLength(1, 2) }
+    }
+    
+    void testExpandoConstructorAndToString() {
+        def foo = new Expando(type:"sometype", value:42)
+        println foo
+        assert foo.toString() == "{type=sometype, value=42}"
+        assert "${foo}" == "{type=sometype, value=42}"
+    }
+
+    void testExpandoMethodOverrides() {
+        def equals = { Object obj -> return obj.value == value }
+        def foo = new Expando(type:"myfoo", value:42, equals:equals)
+        def bar = new Expando(type:"mybar", value:43, equals:equals)
+        def zap = new Expando(type:"myzap", value:42, equals:equals)
+        println(foo)
+	
+        assert foo.equals(bar) == false
+        assert foo.equals(zap) == true
+	
+        def list = []
+        list << foo
+        list << bar
+        println list
+	
+        assert list.contains(foo) == true
+        assert list.contains(bar) == true
+        assert list.contains(zap) == true
+        assert list.indexOf(bar) == 1
+        assert list.indexOf(foo) == 0
+	
+        println "hashCode: " + foo.hashCode()
+	
+        foo.hashCode = { return value }
+        println("hashCode: " + foo.hashCode())
+	
+        assert foo.hashCode() == foo.value
+        println("toString: " + foo.toString())
+	
+        foo.toString = { return "Type: ${type}, Value: ${value}" }
+        println("toString: " + foo.toString())
+        assert foo.toString() == "Type: myfoo, Value: 42"
+    }
+    
+    void testArrayAccessOnThis() {
+        def a = new FancyExpando([a:1,b:2])
+        a.update([b:5,a:2])
+        
+        assert a.a == 2
+        assert a.b == 5
+    }
+    
+    void testExpandoClassProperty() {
+        def e = new Expando()
+        e.class = "hello world"
+        
+        assert e.class == "hello world"
+    }
+}
+
+class FancyExpando extends Expando {
+ FancyExpando(args) { super(args) }
+ 
+ def update(args) {
+   for (e in args) this[e.key] = e.value // using 'this'
+ }
+ 
+ String toString() { dump() }
+}
diff --git a/groovy/src/test/groovy/FileTest.groovy b/groovy/src/test/groovy/FileTest.groovy
new file mode 100644
index 0000000..168399e
--- /dev/null
+++ b/groovy/src/test/groovy/FileTest.groovy
@@ -0,0 +1,103 @@
+package groovy
+
+/**
+ * Unit test for File GDK methods
+ *
+ * @author Marc Guillemot
+ * @version $Revision: 4996 $
+ */
+class FileTest extends GroovyTestCase {
+
+	def baseDir = new File("target/test-resources/filetest")
+
+	void setUp()
+	{
+		createFolder "emptyFolder"
+    	createFile "folder1/Readme"
+    	createFile "folder1/build.xml"
+    	createFile "folder2/myDoc.doc"
+    	createFile "folder2/myDoc.odt"
+    	createFile "folder2/subfolder/file1.groovy"
+    	createFile "folder2/subfolder/file2.groovy"
+    	createFile "folder3/subfolder/file3.groovy"
+    	createFile "foo"
+    	createFile "foo.txt"
+	}
+
+	void testEachFile() {
+		def collectedFiles = []
+		baseDir.eachFile { it -> collectedFiles << it.name }
+		collectedFiles.sort() // needs to sort as there is no guaranty on the order within a folder
+
+    	def expected = ["emptyFolder", "folder1", "folder2", "folder3", "foo", "foo.txt"]
+    	
+    	assertEquals expected, collectedFiles
+    }
+
+	void testEachDir() {
+		def collectedFiles = []
+		baseDir.eachDir { it -> collectedFiles << it.name }
+		collectedFiles.sort() // needs to sort as there is no guaranty on the order within a folder
+
+    	def expected = ["emptyFolder", "folder1", "folder2", "folder3"]
+    	
+    	assertEquals expected, collectedFiles
+    }
+
+	void testEachFileMatch() {
+		def collectedFiles = []
+		baseDir.eachFileMatch ~/fo.*/, { it -> collectedFiles << it.name }
+		collectedFiles.sort() // needs to sort as there is no guaranty on the order within a folder
+
+    	def expected = ["folder1", "folder2", "folder3", "foo", "foo.txt"]
+    	
+    	assertEquals expected, collectedFiles
+    }
+
+	void testEachDirMatch() {
+		def collectedFiles = []
+		baseDir.eachDirMatch ~/fo.*/, { it -> collectedFiles << it.name }
+		collectedFiles.sort() // needs to sort as there is no guaranty on the order within a folder
+
+    	def expected = ["folder1", "folder2", "folder3"]
+    	
+    	assertEquals expected, collectedFiles
+    }
+
+	void testEachFileRecurse() {
+		def collectedFiles = []
+		baseDir.eachFileRecurse { it -> collectedFiles << it.name }
+		collectedFiles.sort() // needs to sort as there is no guaranty on the order within a folder
+
+    	def expected = ["Readme", "build.xml", "emptyFolder",  
+    	 "file1.groovy", "file2.groovy", "file3.groovy", "folder1", "folder2", "folder3", 
+    	 "foo", "foo.txt",
+    	 "myDoc.doc", "myDoc.odt", 
+    	 "subfolder", "subfolder"]
+    	
+    	assertEquals expected, collectedFiles
+    }
+	
+	void testEachDirRecurse() {
+		def collectedFiles = []
+		baseDir.eachDirRecurse { it -> collectedFiles << it.name }
+		collectedFiles.sort() // needs to sort as there is no guaranty on the order within a folder
+
+    	def expected = ["emptyFolder", "folder1", "folder2", "folder3", "subfolder", "subfolder",]
+    	
+    	assertEquals expected, collectedFiles
+    }
+
+	def createFile(path)
+	{
+		def f = new File(baseDir, path)
+		f.parentFile.mkdirs()
+		f.createNewFile()
+	}
+
+	def createFolder(path)
+	{
+		def f = new File(baseDir, path)
+		f.mkdirs()
+	}
+}
diff --git a/groovy/src/test/groovy/FilterLineTest.groovy b/groovy/src/test/groovy/FilterLineTest.groovy
new file mode 100644
index 0000000..28ce75a
--- /dev/null
+++ b/groovy/src/test/groovy/FilterLineTest.groovy
@@ -0,0 +1,44 @@
+package groovy
+
+/**
+ * check that the new filterLine() method on InputStream is ok
+ * (and indirectly test newReader() method on InputStream)
+ * as specified in GROOVY-624 and GROOVY-625
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+class FilterLineTest extends GroovyTestCase {
+	def myFile
+	def myInput
+	def myOutput
+
+	void setUp() {
+	    myFile = new File("src/test/groovy/FilterLineTest.groovy")
+		myInput = new FileInputStream(myFile)
+		myOutput = new CharArrayWriter()
+	}
+
+	void testFilterLineOnFileReturningAWritable() {
+		def writable = myFile.filterLine() {it.contains("testFilterLineOnFileReturningAWritable")}
+		writable.writeTo(myOutput)
+		assert 3 == myOutput.toString().count("testFilterLineOnFileReturningAWritable")
+	}
+
+	void testFilterLineOnFileUsingAnOutputStream() {
+		myFile.filterLine(myOutput) {it.contains("testFilterLineOnFileUsingAnOutputStream")}
+		assert 3 == myOutput.toString().count("testFilterLineOnFileUsingAnOutputStream")
+	}
+
+	void testFilterLineOnInputStreamReturningAWritable() {
+		def writable = myInput.filterLine() {it.contains("testFilterLineOnInputStreamReturningAWritable")}
+		writable.writeTo(myOutput)
+		assert 3 == myOutput.toString().count("testFilterLineOnInputStreamReturningAWritable")
+	}
+
+	void testFilterLineOnInputStreamUsingAnOutputStream() {
+		myInput.filterLine(myOutput) {it.contains("testFilterLineOnInputStreamUsingAnOutputStream")}
+		assert 3 == myOutput.toString().count("testFilterLineOnInputStreamUsingAnOutputStream")
+	}
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/FinallyTest.groovy b/groovy/src/test/groovy/FinallyTest.groovy
new file mode 100644
index 0000000..60ce434
--- /dev/null
+++ b/groovy/src/test/groovy/FinallyTest.groovy
@@ -0,0 +1,133 @@
+package groovy

+

+class FinallyTest extends GroovyTestCase{

+ 

+  void testBreakInTry() {

+    def called = false

+    while (true){

+      try {

+        break

+      } finally {

+        called = true

+      }

+    }

+    assert called, "finally block was not called"

+  }

+  

+  void testBreakInFinally() {

+    def called = false

+    while (true){

+      try {

+        throw new Exception("foo")

+      } catch (e) {

+        assert e.message == "foo"

+      } finally {

+        called = true

+        break

+      }

+    }

+    assert called, "finally block was not called"

+  }

+  

+  void testContinueInTry() {

+    def called = false

+    boolean b = true

+    while (b){

+      try {

+        b=false

+        continue

+      } finally {

+        called = true

+      }

+    }

+    assert called, "finally block was not called"

+  }

+  

+  void testContinueInFinally() {

+    def called = false

+    boolean b = true

+    while (b){

+      try {

+        throw new Exception("foo")

+      } catch (e) {

+        assert e.message == "foo"

+      } finally {

+        b=false

+        called = true

+        continue

+      }

+    }

+    assert called, "finally block was not called"

+  }

+  

+  void testReturn() {

+    def map = methodWithReturnInTry()

+    assert map.called, "finally block was not called"

+    def called = methodWithReturnInFinally()

+    assert called, "finally block was not called"

+  }

+  

+  def methodWithReturnInTry(){

+    def map = [:]

+    try {

+      return map

+    } finally {

+	  map.called = true

+    }

+  }

+  

+  def methodWithReturnInFinally(){

+    try {

+      return false

+    } finally {

+	  return true

+    }

+  }

+  

+  void testStackeFinally(){

+    def calls = methodWithStackedFinally()

+    if (calls==12) {

+      assert false,"wrong order of finally blocks"

+    }

+    assert calls==102 

+  }

+

+  def methodWithStackedFinally(){

+    def calls = 0

+    def first = true;

+    try {

+      try {

+        calls = 0

+      } finally {

+        calls++

+        if (first) {

+          first = false

+        } else {

+          calls += 10

+        }

+      }

+    } finally {

+      calls++

+      if (first) {

+        first = false

+      } else {

+        calls += 100

+      }

+    }

+    return calls

+  }

+  

+  def multipleReturn() {

+    try { 

+        if (0 == 1) return 1

+        return 2

+    }

+    finally { 

+        return 3

+    }

+  }

+  

+  void testMultipleReturn(){

+    assert multipleReturn() == 3

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/Foo.groovy b/groovy/src/test/groovy/Foo.groovy
new file mode 100644
index 0000000..04e0642
--- /dev/null
+++ b/groovy/src/test/groovy/Foo.groovy
@@ -0,0 +1,54 @@
+package groovy
+
+/** 
+ * A dummy bean for testing the use of properties in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class Foo implements Serializable {
+
+    // public properties
+    def name = "James"
+    def count
+    String location = "London"
+    
+    // declare private variables
+    private blah = 9
+    private invisible = "invisible"
+
+    // declare a protected variable
+    protected prot = "hide me!"
+
+    // declare a bean with explicit typing
+    private String body
+
+    static void main(args) {
+        def f = new Foo()
+        println f
+    }
+    
+    // provide a getter method
+    def getCount() {
+         if (count == null) {
+             count = 1
+         }
+         return count
+    }
+     
+    def getBlah() {
+         return blah
+    }
+
+    public String getBody() {
+        return this.body ? this.body : 'null'
+    }
+
+    public void setBody(String body) {
+        this.body = body
+    }
+
+    String toString() {
+        return super.toString() + " name: ${name} location: ${location}"
+    }
+}
diff --git a/groovy/src/test/groovy/ForLoopTest.groovy b/groovy/src/test/groovy/ForLoopTest.groovy
new file mode 100644
index 0000000..2c5b73f
--- /dev/null
+++ b/groovy/src/test/groovy/ForLoopTest.groovy
@@ -0,0 +1,139 @@
+package groovy
+
+import groovy.bugs.TestSupport
+
+class ForLoopTest extends GroovyTestCase {
+
+    def x
+	
+    void testRange() {
+        x = 0
+
+        for ( i in 0..9 ) {
+            x = x + i
+        }
+
+        assert x == 45
+    }
+
+    void testRangeWithType() {
+        x = 0
+
+        for ( Integer i in 0..9 ) {
+            assert i.getClass() == Integer
+            x = x + i
+        }
+
+        assert x == 45
+    }
+
+    /** TODO - no longer applicable?
+
+    void testRangeWithJdk15Style() {
+        x = 0
+
+        for ( i : 0..9 ) {
+            x = x + i
+        }
+
+        assert x == 45
+	}
+	
+	void testRangeWithJdk15StyleAndType() {
+        x = 0
+
+        for ( Integer i : 0..9 ) {
+            assert i.getClass() == Integer
+            x = x + i
+        }
+
+        assert x == 45
+    }
+    */
+	
+    void testList() {
+        x = 0
+		
+        for ( i in [0, 1, 2, 3, 4] ) {
+            x = x + i
+        }
+
+        assert x == 10
+    }
+
+    void testArray() {
+        def array = (0..4).toArray()
+        
+        println "Class: ${array.getClass()} for array ${array}"
+        
+        x = 0
+        
+        for ( i in array ) {
+            x = x + i
+        }
+
+        assert x == 10
+    }
+    
+    void testIntArray() {
+        def array = TestSupport.getIntArray()
+        
+        println "Class: ${array.getClass()} for array ${array}"
+        
+        x = 0
+        
+        for ( i in array ) {
+            x = x + i
+        }
+
+        assert x == 15
+    }
+    
+    void testString() {
+        def text = "abc"
+        
+        def list = []
+        for (c in text) {
+            list.add(c)
+        }
+        
+        assert list == ["a", "b", "c"]
+    }
+    
+    void testVector() {
+        def vector = new Vector()
+        vector.addAll( [1, 2, 3] )
+        
+        def answer = []
+        for (i in vector.elements()) {
+            answer << i
+        }
+        assert answer == [1, 2, 3]
+    }
+    
+    void testClassicFor() {
+       def sum = 0
+       for (int i=0; i<10; i++) {
+         sum++
+       }
+       assert sum==10
+       
+       def list = [1,2]
+       sum=0
+       for (Iterator i = list.iterator();i.hasNext();){
+         sum += i.next()
+       }
+       assert sum==3
+    }
+    
+    void testClassicForNested() {
+       def sum = 0
+       for (int i=0; i<10; i++) {
+         for (int j=0; j<10; j++) {
+           sum++
+         }
+       }
+       assert sum==100
+    }
+
+}
diff --git a/groovy/src/test/groovy/ForLoopWithLocalVariablesTest.groovy b/groovy/src/test/groovy/ForLoopWithLocalVariablesTest.groovy
new file mode 100644
index 0000000..5f2ea9a
--- /dev/null
+++ b/groovy/src/test/groovy/ForLoopWithLocalVariablesTest.groovy
@@ -0,0 +1,17 @@
+package groovy
+
+/**
+ * Tests iterating with local variables
+ */
+class ForLoopWithLocalVariablesTest extends GroovyTestCase {
+
+    void testForLoop() {
+        def x = null
+
+        for ( i in 0..9 ) {
+            x = i
+        }
+
+        assert x == 9
+	}
+}
diff --git a/groovy/src/test/groovy/GStringTest.groovy b/groovy/src/test/groovy/GStringTest.groovy
new file mode 100644
index 0000000..a4ec8c8
--- /dev/null
+++ b/groovy/src/test/groovy/GStringTest.groovy
@@ -0,0 +1,497 @@
+class GStringTest extends GroovyTestCase {
+
+    void check(template, teststr) {
+        assert template instanceof GString
+
+        def count = template.getValueCount()
+        assert count == 1
+        assert template.getValue(0) == "Bob"
+
+        def string = template.toString()
+        assert string == teststr
+    }
+
+    void testWithOneVariable() {
+        def name = "Bob"
+        def teststr = "hello Bob how are you?"
+
+        check("hello $name how are you?", teststr)
+        check("hello ${name} how are you?", teststr)
+        check("hello ${println "feep"; name} how are you?", teststr)
+        check(/hello $name how are you?/, teststr)
+        check(/hello ${name} how are you?/, teststr)
+        check(/hello ${println "feep"; name} how are you?/, teststr)
+    }
+
+    void testWithVariableAtEnd() {
+        def name = "Bob"
+        def teststr = "hello Bob"
+
+        check("hello $name", teststr)
+        check("hello ${name}", teststr)
+        check(/hello $name/, teststr)
+        check(/hello ${name}/, teststr)
+    }
+
+    void testWithVariableAtBeginning() {
+        def name = "Bob"
+        def teststr = "Bob hey"
+        check("$name hey", teststr)
+        check("${name} hey", teststr)
+        name = ""
+        check("${name += "Bob"; name} hey", teststr)
+        assert name == "Bob"
+        check(/$name hey/, teststr)
+        check(/${name} hey/, teststr)
+        name = ""
+        check(/${name += "Bob"; name} hey/, teststr)
+    }
+
+    void testWithJustVariable() {
+        def teststr
+        def name = teststr = "Bob"
+        check("$name", teststr)
+        check("${name}", teststr)
+        check("${assert name == "Bob"; name}", teststr)
+        // Put punctuation after the variable name:
+        check("$name.", "Bob.")
+        check("$name...", "Bob...")
+        check("$name?", "Bob?")
+
+        check(/$name/, teststr)
+        check(/${name}/, teststr)
+        check(/${assert name == "Bob"; name}/, teststr)
+        // Put punctuation after the variable name:
+        check(/$name./, "Bob.")
+        check(/$name.../, "Bob...")
+        check(/$name?/, "Bob?")
+        check(/$name\?/, "Bob\\?")
+        check(/$name$/, "Bob\$")
+
+        def guy = [name: name]
+        check("${guy.name}", "Bob")
+        check("$guy.name", "Bob")
+        check("$guy.name.", "Bob.")
+        check("$guy.name...", "Bob...")
+        check("$guy.name?", "Bob?")
+        check(/$guy.name/, "Bob")
+        check(/$guy.name./, "Bob.")
+        check(/$guy.name.../, "Bob...")
+        check(/$guy.name?/, "Bob?")
+        check(/$guy.name\?/, "Bob\\?")
+        check(/$guy.name$/, "Bob\$")
+    }
+
+    void testWithTwoVariables() {
+        def name = "Bob"
+        def template = "${name}${name}"
+        def string = template.toString()
+
+        assert string == "BobBob"
+    }
+
+    void testWithTwoVariablesWithSpace() {
+        def name = "Bob"
+        def template = "${name} ${name}"
+        def string = template.toString()
+
+        assert string == "Bob Bob"
+    }
+
+    void testAppendString() {
+        def a = "dog"
+        def b = "a ${a}"
+        def c = b + " cat"
+        assert c.toString() == "a dog cat", c
+
+        b += " cat"
+        assert b.toString() == "a dog cat", b
+    }
+
+    void testAppendGString() {
+        def a = "dog"
+        def b = "a ${a}"
+        b += " cat${a}"
+
+        assert b.toString() == "a dog catdog", b
+    }
+
+    void testReturnString() {
+        def value = dummyMethod()
+        assert value == "Hello Gromit!"
+    }
+
+    String dummyMethod() {
+        def name = "Gromit"
+        return "Hello ${name}!"
+    }
+
+    void testCoerce() {
+        def enc = "US-ASCII"
+        def value = "test".getBytes("${enc}")
+         assert value == [116, 101, 115, 116]
+    }
+
+    void testGroovy441() {
+        def arg = "test"
+        def content = "${arg} ="
+        if (arg != "something") {
+            content += "?"
+        }
+        content += "= ${arg}."
+        assert content == "test =?= test."
+    }
+
+    void testTwoStringsInMiddle() {
+        def a = "---"
+        def b = "${a} :"
+        b += "<<"
+        b += ">>"
+        b += ": ${a}"
+        assert b == "--- :<<>>: ---"
+    }
+
+    void testAlternatingGStrings() {
+        def a = "---"
+        def b = "${a} :"
+        b += "<<"
+        b += " [[${a}]] "
+        b += ">>"
+        b += ": ${a}"
+        assert b == "--- :<< [[---]] >>: ---"
+    }
+
+     // Test case for GROOVY-599
+    void testGStringInStaticMethod() {
+        int value = 2
+        String str = "1${value}3"
+        int result = Integer.parseInt(str)
+        assert result == 123
+        result = Integer.parseInt("1${value}3")
+        assert result == 123
+    }
+
+    // Test case for GROOVY-2275
+    void testGetAtWithRange() {
+        def number = 1234567
+        def numberString = "${number}"
+        def realString = "1234567"
+        assert numberString[0..-1] == '1234567'
+        assert realString[0..-1] == '1234567'
+    }
+
+    void testEmbeddedClosures() {
+        def c1 = {-> "hello"}
+        def c2 = {out -> out << "world"}
+        def c3 = {a, b -> b << a}
+        def c4 = c3.curry(5)
+
+        def g1 = "${-> "hello"} ${out -> out << "world"}"
+        def g2 = "$c1 $c2"
+        def g3 = "${-> c1} ${-> c2}"
+        def g4 = "$c4"
+        def g5 = "$c3"
+
+        def w = new StringWriter()
+        w << g1
+        assertEquals(w.buffer.toString(), "hello world")
+        assertEquals(g1.toString(), "hello world")
+        w = new StringWriter()
+        w << g2
+        assertEquals(w.buffer.toString(), "hello world")
+        assertEquals(g2.toString(), "hello world")
+        w = new StringWriter()
+        w << g3
+        assert w.buffer.toString().contains("closure")
+        assert g3.toString().contains("closure")
+        w = new StringWriter()
+        w << g4
+        assertEquals(w.buffer.toString(), "5")
+        assertEquals(g4.toString(), "5")
+        try {
+            println g5
+            fail("should throw a GroovyRuntimeException")
+        } catch (GroovyRuntimeException e) {
+        }
+        try {
+            println g5.toString()
+            fail("should throw a GroovyRuntimeException")
+        } catch (GroovyRuntimeException e) {
+        }
+    }
+
+    /**
+     * Tests comparing two strings which have the same string representation but
+     * only one of which uses a template. This is a test for GROOVY-626.
+     */
+    void testEqualsTemplateToLiteral() {
+        def template = "${2}"
+        def literal = "2"
+
+        // succeeds
+        assertTrue("template == literal false", template == literal)
+        assertTrue("literal == template false", literal == template)
+
+        // these fail
+        assertFalse("literal not equal to template", literal.equals(template))
+        assertFalse("template not equal to literal", template.equals(literal))
+        assertTrue("hash codes not equal", literal.hashCode() != template.hashCode())
+    }
+
+    /**
+     * Tests getting a character by index where the index is a reference instead of a constant.
+     * This is a test for GROOVY-1139.
+     */
+    void testCharAtWithIntegerReference() {
+        def literal = "0123456789";
+        def template = "${literal}";
+
+        def i = 0
+        assertEquals("wrong character at position 0", '0', literal[i]);
+        assertEquals("wrong character at position 0", '0', template[i]);
+
+        i = 5
+        assertEquals("wrong character at position 5", '5', literal[i]);
+        assertEquals("wrong character at position 5", '5', template[i]);
+
+        i = 9
+        assertEquals("wrong character at position 9", '9', literal[i]);
+        assertEquals("wrong character at position 9", '9', template[i]);
+    }
+
+    /**
+     * Tests getting a character by index, counting from the beginning of the string.
+     */
+    void testCharAtFromStart() {
+        def literal = "0123456789";
+        def template = "${literal}";
+
+        assertEquals("wrong character at position 0", '0', literal[0]);
+        assertEquals("wrong character at position 5", '5', literal[5]);
+        assertEquals("wrong character at position 9", '9', literal[9]);
+
+        assertEquals("wrong character at position 0", '0', template[0]);
+        assertEquals("wrong character at position 5", '5', template[5]);
+        assertEquals("wrong character at position 9", '9', template[9]);
+    }
+
+    /**
+     * Tests getting a character by index, counting from the end of the string.
+     */
+    void testCharAtFromEnd() {
+        def literal = "0123456789";
+        def template = "${literal}";
+
+        assertEquals("wrong character at position -1", '9', literal[-1]);
+        assertEquals("wrong character at position -5", '5', literal[-5]);
+        assertEquals("wrong character at position -10", '0', literal[-10]);
+
+        assertEquals("wrong character at position -1", '9', template[-1]);
+        assertEquals("wrong character at position -5", '5', template[-5]);
+        assertEquals("wrong character at position -10", '0', template[-10]);
+    }
+
+    /**
+     * Tests extracting a range which starts at position zero and ends before the
+     * end of the string.
+     */
+    private void doTestExtractRangeStartToBeforeEnd(string) {
+        // inclusive
+        assertEquals("string[0..-3]", "01234567", string[0..-3]);
+
+        // exclusive
+        assertEquals("string[0..<-3]", "0123456", string[0..<-3]);
+    }
+
+    /**
+     * Tests extracting a range which starts before the end of the string and ends at 0.
+     * For an inclusive range, this reverses the string.  For an exclusive range, this
+     * counts from the end to the last character.
+     */
+    void doTestExtractRangeBeforeEndToStart(string) {
+        // inclusive
+        assertEquals("string[-3..0]", "76543210", string[-3..0]);
+
+        // exclusive
+        assertEquals("string[-3..<0]", "7654321", string[-3..<0]);
+    }
+
+    /**
+     * Calls <code>doTestExtractRangeStartToBeforeEnd</code> with a non-template string.
+     *
+     * GROOVY-781
+     */
+    void fixmeTestExtractRangeStartToBeforeEndLiteral() {
+        def literal = "0123456789";
+        doTestExtractRangeStartToBeforeEnd(literal);
+    }
+
+    /**
+     * Calls <code>doTestExtractRangeStartToBeforeEnd</code> with a template string.
+     *
+     * GROOVY-781
+     */
+    void fixmeTestExtractRangeStartToBeforeEndTemplate() {
+        def literal = "0123456789";
+        def template = "${literal}";
+
+        doTestExtractRangeStartToBeforeEnd(template);
+    }
+
+    /**
+     * Calls <code>doTestExtractRangeBeforeEndToStart</code> with a non-template string.
+     *
+     * GROOVY-781
+     */
+    void fixmeTestExtractRangeBeforeEndToStartLiteral() {
+        def literal = "0123456789";
+        doTestExtractRangeBeforeEndToStart(literal);
+    }
+
+    /**
+     * Calls <code>doTestExtractRangeBeforeEndToStart</code> with a template string.
+     *
+     * GROOVY-781
+     */
+    void fixmeTestExtractRangeBeforeEndToStartTemplate() {
+        def literal = "0123456789";
+        def template = "${literal}";
+
+        doTestExtractRangeBeforeEndToStart(template);
+    }
+
+    /**
+     * Tests replacing a value in a string with an undefined variable.
+     */
+    void testReplaceValueWithUndefinedVariable() {
+        try {
+            def str = "replace <${undefined}>"
+            fail("undefined value not detected");
+        }
+        catch (MissingPropertyException e) {
+        }
+    }
+
+    /**
+     * Tests replacing a value with a null expression.
+     */
+    void testReplaceValueWithNullExpression() {
+        def str = "replace <${null}>";
+        assertEquals(str, 'replace <null>');
+    }
+
+    /**
+     * Tests replacing a value with a compound expression.
+     */
+    void testReplaceValueWithCompoundExpression() {
+        def i = 1, j = 2
+        def str = "replace <${i; j}>"
+        assertEquals('value replaced', 'replace <2>', str)
+    }
+
+    /**
+     * Tests incrementing the variable being substituted after creating the string.  This
+     * shouldn't have any effect because the string holds a reference to the original value.
+     */
+    void testIncrementAfterCreatingString() {
+        def i = 1
+        def str = "replace <${i}> <${i * 2}>"
+        assertEquals("value ok", "replace <1> <2>", str)
+        i++
+        assertEquals("value ok", "replace <1> <2>", str)
+    }
+
+    /**
+     * Tests evaluating a closure embedded in a string.
+     */
+    void testClosureInString() {
+        def i = 1
+        def closure = {i};
+        def str = "<${closure()}>"
+        assertEquals('closure replacement ok', '<1>', str)
+
+        // this has no effect because the closure is only evaluated once when the string
+        // is created
+        i++
+        assertEquals('closure replacement ok', '<1>', str)
+    }
+
+    /**
+     * Tests embedding a mutable object in a string.
+     */
+    void testEmbedMutableObject() {
+        def buffer = new StringBuffer("value")
+        def stringValue = "value";
+        def str = "buffer: <${buffer}>"
+        assertEquals("buffer: <value>", str)
+    }
+
+    /**
+     * Tests modifying a string embedded in another string, which should have no effect.
+     */
+    void testAppendToEmbeddedStringValue() {
+        def stringValue = "value";
+        def str = "string: <${stringValue}>"
+        assertEquals("string: <value>", str)
+
+        // this has no effect because the string contains a reference stringValue
+        // and += for strings creates a new string instead of modifying the existing value.
+        stringValue += " more"
+        assertEquals("string: <value>", str)
+    }
+
+    /**
+     * Tests including a map in a string
+     */
+    void testMapInString() {
+        def map = ["key": 1];
+        def str = "map.key: <${map.key}>; map: <${map}>";
+        assertEquals("map replacement ok", 'map.key: <1>; map: <["key":1]>', str)
+        map.key++;
+
+        // The map shows the effects of the change because the string holds a reference
+        // to the mutable map.  map.key doesn't show the effect of the change because
+        // in this slot the string holds a reference to the original value and
+        // map.kep++ created a new value which was stored in the map.
+        assertEquals("map replacement ok", 'map.key: <1>; map: <["key":2]>', str)
+    }
+
+    /**
+     * Tests including a string in itself recursively.
+     */
+    void testRecursiveReplacement() {
+        def str = "1";
+        str = "<${str}>";
+        assertEquals("recursive string replaced", '<1>', str);
+    }
+
+    /**
+     * Void method
+     */
+    void doNothing()
+    {
+    }
+
+    /**
+     * Tests replacing a value with a void statement.
+     */
+    void testReplaceValueWithVoidStatement() {
+        def str = "replace <${doNothing()}>"
+        assertEquals('value replaced', 'replace <null>', str)
+    }
+
+    /**
+     * Tests replacing a value with an empty statement.
+     */
+    void testReplaceValueWithEmptyStatement() {
+        def str = "replace <${;}>"
+        assertEquals('value replaced', 'replace <null>', str)
+    }
+
+    /**
+     * Tests replacing a value with an empty expression.
+     */
+    void testReplaceValueWithEmptyExpression() {
+        assertEquals('replace <null>', "replace <${}>")
+    }
+}
diff --git a/groovy/src/test/groovy/GeneratorTest.groovy b/groovy/src/test/groovy/GeneratorTest.groovy
new file mode 100644
index 0000000..a135e61
--- /dev/null
+++ b/groovy/src/test/groovy/GeneratorTest.groovy
@@ -0,0 +1,50 @@
+package groovy
+
+class GeneratorTest extends GroovyTestCase {
+
+    void testGenerator() {
+        def x = this.&sampleGenerator
+        //System.out.println("x: " + x)
+		
+        def result = ''
+        for (i in x) {
+            result = result + i
+        }
+	    
+        assert result == "ABC"
+    }
+
+    void testFindAll() {
+        def x = this.&sampleGenerator
+ 	    
+        def value = x.findAll { item -> return item == "C" }
+        assert value == ["C"]
+
+        value = x.findAll { item -> return item != "B" }
+        assert value == ["A", "C"]
+    }
+    
+	
+    void testEach() {
+        def x = this.&sampleGenerator
+ 	    
+        def value = x.each { println(it) }
+    }
+    
+	
+    void testMissingThisBug() {
+        def result = ''
+        for (i in this.&sampleGenerator) {
+            result = result + i
+        }
+	    
+        assert result == "ABC"
+    }
+	
+    void sampleGenerator(closure) {
+        // kinda like yield statements
+        closure.call("A")
+        closure.call("B")
+        closure.call("C")
+    }
+}
diff --git a/groovy/src/test/groovy/GlobalPrintlnTest.groovy b/groovy/src/test/groovy/GlobalPrintlnTest.groovy
new file mode 100644
index 0000000..732e4a6
--- /dev/null
+++ b/groovy/src/test/groovy/GlobalPrintlnTest.groovy
@@ -0,0 +1,13 @@
+package groovy
+
+class GlobalPrintlnTest extends GroovyTestCase {
+
+    void testGlobalPrintln() {
+        println("Hello World!")
+	}
+
+    void testGlobalPrint() {
+        print("Hello ")
+        println("World!")
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/GroovyClosureMethodsTest.groovy b/groovy/src/test/groovy/GroovyClosureMethodsTest.groovy
new file mode 100644
index 0000000..010213e
--- /dev/null
+++ b/groovy/src/test/groovy/GroovyClosureMethodsTest.groovy
@@ -0,0 +1,172 @@
+package groovy
+
+/**
+ * Test case for the eachObject method on a file containing
+ * zero, one or more objects (object stream).  Also test cases
+ * for eachDir, eachFileMatch and runAfter methods.
+ *
+ * @author Hein Meling
+ */
+class GroovyClosureMethodsTest extends GroovyTestCase {
+
+    private String dirname_target = "target"
+    private String dirname_source = "src/test/groovy"
+
+    private String filename = "${dirname_target}/GroovyClosureMethodsTest.each.object"
+
+    void testEachObjectMany() {
+        def file = new File(filename)
+        def oos = new ObjectOutputStream(new FileOutputStream(file))
+        def list = [1, 2, 3, "foo", 9, "bar", 191, file, 9129]
+        list.each {
+            oos.writeObject(it)
+        }
+
+        println("Contents of file with multiple objects: " + file)
+        int c = 0
+        file.eachObject {
+            print "${it} "
+            c++
+        }
+        assert list.size() == c
+        println ""
+        //ensure to remove the created file
+        file.delete()
+    }
+
+    void testEachObjectOne() {
+        def file = new File(filename)
+        def oos = new ObjectOutputStream(new FileOutputStream(file))
+        oos.writeObject(file)
+
+        println("Contents of file with one object: " + file)
+        int c = 0
+        file.eachObject {
+            print "${it} "
+            c++
+        }
+        assert c == 1
+        println ""
+        //ensure to remove the created file
+        file.delete()
+    }
+
+    void testEachObjectEmptyFile() {
+        def file = new File(filename)
+        def oos = new ObjectOutputStream(new FileOutputStream(file))
+
+        println("Contents of empty file: " + file)
+        int c = 0
+        file.eachObject {
+            print "${it} "
+            c++
+        }
+        assert c == 0
+        println ""
+        //ensure to remove the created file
+        file.delete()
+    }
+
+    void testEachObjectNullFile() {
+        def file = new File(filename)
+        def oos = new ObjectOutputStream(new FileOutputStream(file))
+        oos.writeObject(null)
+        oos.writeObject("foo")
+        oos.writeObject(null)
+
+        println("Contents of null file: " + file)
+        int c = 0
+        file.eachObject {
+            print "${it} "
+            c++
+        }
+        assert c == 3
+        println ""
+        //ensure to remove the created file
+        file.delete()
+    }
+
+    void testEachDir() {
+        def dir = new File(dirname_source)
+
+        println("Directories in: " + dir)
+        int c = 0
+        dir.eachDir {
+            print "${it} "
+            c++
+        }
+        println ""
+        assert c > 0
+    }
+
+    void testEachFileMatch() {
+        def file = new File(dirname_source)
+
+        print "Files with the text Groovy: "
+        file.eachFileMatch(~"^Groovy.*") {
+            print "${it} "
+        }
+        println ""
+
+        print "Files with the text Closure: "
+        file.eachFileMatch(~"^Closure.*") {
+            print "${it} "
+        }
+        println ""
+
+        print "This file is here: "
+        int c = 0
+        file.eachFileMatch(~"^GroovyClosureMethodsTest.groovy") {
+            print "${it} "
+            c++
+        }
+        assert c == 1
+        println ""
+    }
+
+    void testEachFileOnNonExistingDir() {
+        shouldFail {
+            File dir = new File("SomeNonExistingDir")
+            dir.eachFile {
+                println "${it} "
+            }
+        }
+    }
+
+    void testEachFileOnNonDirFile() {
+        shouldFail {
+            File dir = new File("${dirname_source}/GroovyClosureMethodsTest.groovy")
+            dir.eachFile {
+                println "${it} "
+            }
+        }
+    }
+
+    void testRunAfter() {
+        def timer = new Timer()
+        boolean status = false
+        timer.runAfter(2000) {
+            println "Running after 2 seconds wait"
+            status = true
+        }
+        println "I should run first"
+        assert status == false
+        Thread.sleep(3000)
+        println "I should run last"
+        assert status == true
+    }
+
+    void testSplitEachLine() {
+        String s = """A B C D
+E F G H
+1 2 3 4
+"""
+        Reader reader = new StringReader(s)
+        def all_lines = []
+        reader.splitEachLine(" ") { list ->
+            all_lines << list
+        }
+        assert all_lines == [["A", "B", "C", "D"], ["E", "F", "G", "H"], ["1", "2", "3", "4"]]
+    }
+
+}
diff --git a/groovy/src/test/groovy/GroovyInterceptableTest.groovy b/groovy/src/test/groovy/GroovyInterceptableTest.groovy
new file mode 100644
index 0000000..9e6ca6c
--- /dev/null
+++ b/groovy/src/test/groovy/GroovyInterceptableTest.groovy
@@ -0,0 +1,40 @@
+package groovy
+
+import org.codehaus.groovy.runtime.ReflectionMethodInvoker
+
+class GroovyInterceptableTest extends GroovyTestCase {
+
+    void testMethodInterception() {
+        def g = new GI()
+        assert g.someInt() == 2806
+        assert g.someUnexistingMethod() == 1
+        assert g.toString() == "invokeMethodToString"
+    }
+
+    void testProperties() {
+        def g = new GI()
+        assert g.foo == 89
+        g.foo = 90
+        assert g.foo == 90
+        // should this be 1 or 90?
+        assert g.getFoo() == 1
+    }
+}
+
+class GI implements GroovyInterceptable {
+
+    def foo = 89
+
+    int someInt() { 2806 }
+    String toString() { "originalToString" }
+
+    Object invokeMethod(String name, Object args) {
+        if ("toString" == name)
+            return "invokeMethodToString"
+        else if ("someInt" == name)
+            return ReflectionMethodInvoker.invoke(this, name, args)
+        else
+            return 1
+    }
+}
+
diff --git a/groovy/src/test/groovy/GroovyMethodsTest.groovy b/groovy/src/test/groovy/GroovyMethodsTest.groovy
new file mode 100644
index 0000000..d916833
--- /dev/null
+++ b/groovy/src/test/groovy/GroovyMethodsTest.groovy
@@ -0,0 +1,465 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy
+
+import java.awt.Dimension
+import org.codehaus.groovy.runtime.typehandling.GroovyCastException
+
+/** 
+ * Tests the various new Groovy methods
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Guillaume Laforge
+ * @author Dierk Koenig
+ * @author Paul King
+ * @author Joachim Baumann
+ * @version $Revision$
+ */
+class GroovyMethodsTest extends GroovyTestCase {
+    void testCollect() {
+        assert [2, 4, 6].collect {it * 2} == [4, 8, 12]
+        def answer = [2, 4, 6].collect(new Vector()) {it * 2}
+        assert answer[0] == 4
+        assert answer[1] == 8
+        assert answer[2] == 12
+        assert [1: 'a', 2: 'b', 3: 'c'].collect {k, v -> k + v} == ['1a', '2b', '3c']
+        assert [1: 'a', 2: 'b', 3: 'c'].collect {it.getKey() + "*" + it.getValue()} == ['1*a', '2*b', '3*c']
+    }
+
+    void testAsCoercion() {
+        def d0 = new Dimension(100, 200)
+        assert d0 == new Dimension(width: 100, height: 200)
+        assert d0 == [100, 200] as Dimension
+        assert d0 == [width: 100, height: 200] as Dimension
+    }
+
+    void testCombinations() {
+        def lists = [['a', 'b'], [1, 2, 3]]
+        assert lists.combinations() as Set ==
+                        [['a', 1], ['a', 2], ['a', 3],
+                        ['b', 1], ['b', 2], ['b', 3]] as Set
+    }
+
+    void testTranspose() {
+        def lists = [['a', 'b'], [1, 2, 3]]
+        assert lists.transpose() == [['a', 1], ['b', 2]]
+    }
+
+    void testSum() {
+        assert [].sum() == null
+        assert [null].sum() == null
+        assert [1].sum() == 1
+        assert [1, 2, 3].sum() == 6
+        assert [1, 2, 3].sum("") == "123"
+        assert [[1, 2], [3, 4], [5, 6]].sum() == [1, 2, 3, 4, 5, 6]
+        assert [[1, 2], [3, 4], [5, 6]].sum("") == "[1, 2][3, 4][5, 6]"
+
+        assert [].sum {it.length()} == null
+        assert [null].sum {it.toString()} == 'null'
+        assert ["abc"].sum {it.length()} == 3
+        assert ["a", "bc", "def"].sum {it.length()} == 6
+        assert ["a", "bc", "def"].sum("") {it.length()} == "123"
+        assert [[1, 2], [3, 4], [5, 6]].sum {it.size()} == 6
+        assert [[1, 2], [3, 4], [5, 6]].sum {list -> list.collect {it * 2}} == [2, 4, 6, 8, 10, 12]
+        def result = []
+        [[1, 2], [3, 4], [5, 6]].each {list -> result << list.collect {it * 2}}
+        assert result.sum() == [2, 4, 6, 8, 10, 12]
+        assert [[1, 2], [3, 4], [5, 6]].sum {list -> list.collect {it * 2}} == [2, 4, 6, 8, 10, 12]
+    }
+
+    void testJoin() {
+        assert [2, 4, 6].join("-") == "2-4-6"
+        assert ["edam", "cheddar", "brie"].join(", ") == 'edam, cheddar, brie'
+        assert ["abc", 5, 2.34].join(", ") == "abc, 5, 2.34"
+    }
+
+    void testTimes() {
+        def count = 0
+        5.times {i -> count = count + i}
+        assert count == 10
+        count = 0
+        def temp = 5
+        temp.times {i -> count = count + i}
+        assert count == 10
+    }
+
+    void testArraySubscript() {
+        def list = [1, 2, 3, 4]
+        def array = list.toArray()
+        def value = array[2]
+        assert value == 3
+        array[0] = 9
+        assert array[0] == 9
+        assert array[0..<0] == []
+    }
+
+    void testToCharacterMethod() {
+        def s = 'c'
+        def x = s.toCharacter()
+        assert x instanceof Character
+    }
+
+    void testPutAtRange() {
+        def list
+        list = ['a', 'b', 'c']; list[4] = 'x'; assert list == ["a", "b", "c", null, "x"]
+        list = ['a', 'b', 'c']; list[1..2] = ['x', 'y']; assert list == ["a", "x", "y"]
+        list = ['a', 'b', 'c']; list[1..2] = 'x'; assert list == ["a", "x"]
+        list = ['a', 'b', 'c']; list[4..5] = ['x', 'y']; assert list == ["a", "b", "c", null, "x", "y"]
+        list = ['a', 'b', 'c']; list[4..5] = 'x'; assert list == ["a", "b", "c", null, "x"]
+    }
+
+    void testGetAtRange() {
+        def list = ['a', 'b', 'c']
+        assert list[1..2] == ['b', 'c']
+        assert list[0..<0] == []
+    }
+
+    void testCharSequenceGetAt() {
+        def x = "matrix"
+        assert x[0, 5..0] == 'mxirtam'
+        assert x[3..0, 0..3] == 'rtammatr'
+        assert x[2..-4, 3..-4, 3..-3] == 'trtr'
+        assert x[-1..-3, -3..-1] == 'xirrix'
+        assert x[0..<0] == ''
+    }
+
+    void testListGrep() {
+        def list = ["James", "Bob", "Guillaume", "Sam"]
+        def answer = list.grep(~".*a.*")
+        assert answer == ["James", "Guillaume", "Sam"]
+        answer = list.grep(~"B.b")
+        assert answer == ["Bob"]
+    }
+
+    void testCollectionToList() {
+        def c = [1, 2, 3, 4, 5] // but it's a list
+        def l = c.toList()
+        assert l.containsAll(c)
+        assert c.size() == l.size()
+    }
+
+    void testIteratorToList() {
+        def c = [1, 2, 3, 4, 5]
+        def l = c.iterator().toList()
+        assert l.containsAll(c)
+        assert c.size() == l.size()
+    }
+
+    void testEnumerationToList() {
+        def c = [1, 2, 3, 4, 5]
+        def v = new Vector()
+        c.each {
+            v.add(it)
+        }
+        def l = v.elements().toList()
+        assert l.containsAll(c)
+        assert c.size() == l.size()
+    }
+
+    void testStringToList() {
+        String s = 'hello 10'
+        def gs = "hello ${5+5}"
+        def expected = ["h", "e", "l", "l", "o", " ", "1", "0"]
+        assert s.toList() == expected
+        assert s as String[] == expected
+        assert gs as String[] == expected
+    }
+
+    void testCollectionAsList() {
+        Integer[] nums = [1, 2, 3, 4, 5]
+        def numList = nums as List
+        nums.each {assert numList.contains(it)}
+        assert nums.size() == numList.size()
+    }
+
+    void testCollectionAsLinkedList() {
+        Integer[] nums = [1, 2, 3, 4, 5]
+        def numList = nums as LinkedList
+        nums.each {assert numList.contains(it)}
+        assert nums.size() == numList.size()
+        assert numList.class == LinkedList.class
+    }
+
+    void testArrayListAsLinkedList() {
+        ArrayList nums = [1, 2, 3, 4, 5]
+        shouldFail(GroovyCastException.class) {
+            def numList = nums as LinkedList
+        }
+    }
+
+    void testFileSize() {
+        assert new File('build.properties').size()
+    }
+
+    void testMatcherSize() {
+        assertEquals 3, ('aaa' =~ /./).count
+        assertEquals 3, ('aaa' =~ /./).size()
+        assertEquals 1, ('a' =~ /./).size()
+        assertEquals 0, ('a' =~ /x/).size()
+    }
+
+    void testJoinString() {
+        String[] arr = ["a", "b", "c", "d"]
+        def joined = arr.join(", ")
+        assert joined == "a, b, c, d"
+    }
+
+    void testReverseEach() {
+        def l = ["cheese", "loves", "Guillaume"]
+        def expected = ["Guillaume", "loves", "cheese"]
+        def answer = []
+        l.reverseEach {answer << it}
+        assert answer == expected
+    }
+
+    void testGrep() {
+        def list = ["Guillaume", "loves", "cheese"]
+        def answer = list.grep(~".*ee.*")
+        assert answer == ["cheese"]
+        list = [123, "abc", 4.56]
+        answer = list.grep(String)
+        assert answer == ["abc"]
+        list = [4, 2, 7, 3, 6, 2]
+        answer = list.grep(2..3)
+        assert answer == [2, 3, 2]
+    }
+
+    void testMapGetWithDefault() {
+        def map = [:]
+        assert map.foo == null
+        map.get("foo", []).add(123)
+        assert map.foo == [123]
+        map.get("bar", [:]).get("xyz", [:]).cheese = 123
+        assert map.bar.xyz.cheese == 123
+        assert map.size() == 2
+    }
+
+    String getCmd() {
+        def cmd = "ls -l"
+        if (System.properties.'os.name'.contains('Win')) {
+            cmd = "cmd /c dir"
+        }
+        return cmd
+    }
+
+    void testExecuteCommandLineProcessUsingAString() {
+        println "executing command: ${cmd}"
+        def process = cmd.execute()
+        // lets have an easier way to do this!
+        def count = 0
+        println "Read the following lines..."
+        /** @todo we should simplify the following line!!! */
+        new InputStreamReader(process.in).eachLine {line ->
+            println line
+            ++count
+        }
+        println ""
+        process.waitFor()
+        def value = process.exitValue()
+        println "Exit value of command line is ${value}"
+        assert count > 1
+    }
+
+    /*
+    void testExecuteCommandLineProcessAndUseWaitForOrKill_FAILS_ON_WINDOWS() {
+        if (System.properties.'os.name'.contains('Windows') && notYetImplemented()) return
+
+        println "executing command: ${cmd}"
+
+        def process = cmd.execute()
+
+        process.consumeProcessOutput()
+        process.waitForOrKill(1000)
+        def value = process.exitValue()
+        println "Exit value of command line is ${value}"
+
+        process = cmd.execute()
+
+        process.consumeProcessOutput()
+        process.waitForOrKill(10) // This fails on RLW's workstation with parameter 1, >=8 is required.
+        value = process.exitValue()
+        println "Exit value of command line is ${value}"
+        
+    }
+    */
+
+    void testExecuteCommandLineUnderWorkingDirectory_FAILS() {if (notYetImplemented()) return
+
+        def envp = java.util.Array.newInstance(String, 0)
+        def workDir = new File(".")
+
+        println "executing command: ${cmd} under the directory ${workDir.canonicalPath}"
+
+        def process = cmd.execute(envp, workDir)
+
+        // lets have an easier way to do this!
+        def count = 0
+
+        println "Read the following lines under the directory ${workDir} ..."
+
+        /** @todo we should simplify the following line!!! */
+        new InputStreamReader(process.in).eachLine {line ->
+            println line
+            ++count
+        }
+        println ""
+
+        process.waitFor()
+        def value = process.exitValue()
+        println "Exit value of command line is ${value}"
+
+        assert count > 1
+    }
+
+    void testDisplaySystemProperties() {
+        println "System properties are..."
+        def properties = System.properties
+        def keys = properties.keySet().sort()
+        for (k in keys) {
+            println "${k} = ${properties[k]}"
+        }
+    }
+
+    void testMax() {
+        assert [-5, -3, -1, 0, 2, 4].max {it * it} == -5
+    }
+
+    void testMin() {
+        assert [-5, -3, -1, 0, 2, 4].min {it * it} == 0
+    }
+
+    void testSort() {
+        assert [-5, -3, -1, 0, 2, 4].sort {it * it} == [0, -1, 2, -3, 4, -5]
+    }
+
+    void testReplaceAllClosure() {
+        assert "1 a 2 b 3 c 4".replaceAll("\\p{Digit}") {it * 2} == "11 a 22 b 33 c 44"
+    }
+
+    void testObjectSleep() {
+        long start = System.currentTimeMillis()
+        sleep 1000
+        long slept = System.currentTimeMillis() - start
+        long epsilon = 120
+        assert (slept > 1000 - epsilon) && (slept < 1000 + epsilon):  \
+              "should have slept for 1s (+/- " + epsilon + "ms) but was ${slept}ms"
+    }
+
+    void testObjectSleepInterrupted() {
+        def interruptor = new groovy.TestInterruptor(Thread.currentThread())
+        new Thread(interruptor).start()
+        long start = System.currentTimeMillis()
+        sleep 1000
+        long slept = System.currentTimeMillis() - start
+        long epsilon = 150
+        assert (slept > 1000 - epsilon) && (slept < 1000 + epsilon):  \
+              "should have slept for 1s (+/- " + epsilon + "ms) but was ${slept}ms"
+    }
+
+    void testObjectSleepWithOnInterruptHandler() {
+        def log = ''
+        def interruptor = new groovy.TestInterruptor(Thread.currentThread())
+        new Thread(interruptor).start()
+        long start = System.currentTimeMillis()
+        sleep(2000) {log += it.toString()}
+        long slept = System.currentTimeMillis() - start
+        assert slept < 2000, "should have been interrupted but slept ${slept}ms > 2s"
+        assertEquals 'java.lang.InterruptedException: sleep interrupted', log.toString()
+    }
+
+    void testObjectSleepWithOnInterruptHandlerContinueSleeping() {
+        def log = ''
+        def interruptor = new groovy.TestInterruptor(Thread.currentThread())
+        new Thread(interruptor).start()
+        long start = System.currentTimeMillis()
+        sleep(2000) {
+            log += it.toString()
+            false // continue sleeping
+        }
+        long slept = System.currentTimeMillis() - start
+        short allowedError = 2 // ms
+        assert slept + allowedError >= 2000, "should have slept for at least 2s but only slept for ${slept}ms"
+        assertEquals 'java.lang.InterruptedException: sleep interrupted', log.toString()
+    }
+
+    void testObjectIdentity() {
+        def a = new Object()
+        def b = a
+        assert a.is(b)
+        assert !a.is(null)
+        assert !1.is(2)
+        // naive impl would fall for this trap
+        assert !new WackyHashCode().is(new WackyHashCode())
+    }
+
+    void testGroupByList() {
+        def expected = [Integer: [1, 2], String: ["a", "b"], BigDecimal: [3.5, 4.6]]
+        def list = [1, "a", 2, "b", 3.5, 4.6]
+        def result = list.groupBy {it.class}
+        assert [1, 2] == result[Integer]
+        assert ["a", "b"] == result[String]
+        assert [3.5, 4.6] == result[BigDecimal]
+        assert 3 == result.size()
+    }
+
+    void testGroupByMapEntry() {
+        def expectedKeys = [Integer: [1, 3], String: [2, 4], BigDecimal: [5, 6]]
+        def expectedVals = [Integer: [1, 2], String: ["a", "b"], BigDecimal: [3.5, 4.6]]
+        def map = [1: 1, 2: "a", 3: 2, 4: "b", 5: 3.5, 6: 4.6]
+        def result = map.groupBy {entry -> entry.value.class}
+        assert expectedKeys.Integer == result[Integer].collect {it.key}
+        assert expectedVals.Integer == result[Integer].collect {it.value}
+        assert expectedKeys.String == result[String].collect {it.key}
+        assert expectedVals.String == result[String].collect {it.value}
+        assert expectedKeys.BigDecimal == result[BigDecimal].collect {it.key}
+        assert expectedVals.BigDecimal == result[BigDecimal].collect {it.value}
+        assert 3 == result.size()
+    }
+
+    def leftCol = ["2"]
+    def rightCol = ["1", "2", "3"]
+
+    void testList() {
+        def lst = [] as LinkedList
+        doIt(lst)
+    }
+
+    void testSetWithExplicitCoercion() {
+        def set = [] as HashSet
+        doIt(set)
+    }
+
+    void testSetWithImplicitCoercion() {
+        Set set = []
+        doIt(set)
+    }
+
+    void testVector() {
+        def vctr = [] as Vector
+        doIt(vctr)
+    }
+
+    void doIt(col) {
+        col.clear();
+        col.addAll(leftCol);
+        // not really concerned about  correctness, rather that the method can be called, however..
+        assert col.intersect(rightCol) == ["2"]
+    }
+}
+
+class WackyHashCode {
+    int hashCode() {return 1;}
+}
diff --git a/groovy/src/test/groovy/GroovyTruthTest.groovy b/groovy/src/test/groovy/GroovyTruthTest.groovy
new file mode 100644
index 0000000..5baa59a
--- /dev/null
+++ b/groovy/src/test/groovy/GroovyTruthTest.groovy
@@ -0,0 +1,49 @@
+package groovy
+
+class GroovyTruthTest extends GroovyTestCase {
+
+    void testTruth() {
+        testFalse null
+
+        assertTrue Boolean.TRUE
+        testTrue true
+        testFalse Boolean.FALSE
+        testFalse false
+
+        testFalse ""
+        testTrue "bla"
+        testTrue "true"
+        testTrue "TRUE"
+        testTrue "false"
+        testFalse ''
+        testTrue 'bla'
+        testTrue new StringBuffer('bla')
+        testFalse new StringBuffer()
+
+        testFalse Collections.EMPTY_LIST
+        testFalse([])
+        testTrue([1])
+        testFalse([].toArray())
+
+        testFalse [:]
+        testTrue([bla: 'some value']) 
+        testTrue 1234
+        testFalse 0
+        testTrue 0.3f
+        testTrue new Double(3.0f)
+        testFalse 0.0f
+        testTrue new Character((char) 1)
+        testFalse new Character((char) 0)
+    }
+    
+    protected testTrue(someObj)
+    {
+    	assertTrue someObj ? true : false
+    }
+
+    protected testFalse(someObj)
+    {
+    	assertFalse someObj ? true : false
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/HeredocsTest.groovy b/groovy/src/test/groovy/HeredocsTest.groovy
new file mode 100644
index 0000000..1bf7ae6
--- /dev/null
+++ b/groovy/src/test/groovy/HeredocsTest.groovy
@@ -0,0 +1,34 @@
+package groovy
+
+class HeredocsTest extends GroovyTestCase {
+
+    void testHeredocs() {
+        def name = "James"
+        def s = """
+abcd
+efg
+
+hijk
+     
+hello ${name}
+        
+"""
+        assert s != null
+        assert s instanceof GString
+        assert s.contains("i")
+        assert s.contains("James")
+        def numlines = s.count('\n')
+        assert numlines == 8
+    }
+
+    void testDollarEscaping() {
+        def s = """
+hello \${name}
+"""
+        assert s != null
+        assert s.contains('$')
+        def c = s.count('$')
+        assert c == 1
+        assert s == '\nhello ${name}\n'
+    }
+}
diff --git a/groovy/src/test/groovy/HomepageTest.groovy b/groovy/src/test/groovy/HomepageTest.groovy
new file mode 100644
index 0000000..c494f3c
--- /dev/null
+++ b/groovy/src/test/groovy/HomepageTest.groovy
@@ -0,0 +1,8 @@
+package groovy
+
+class HomepageTest extends GroovyTestCase {
+
+	void testHomePage() {
+	    
+	}
+}
diff --git a/groovy/src/test/groovy/IdentityClosureTest.groovy b/groovy/src/test/groovy/IdentityClosureTest.groovy
new file mode 100644
index 0000000..9233601
--- /dev/null
+++ b/groovy/src/test/groovy/IdentityClosureTest.groovy
@@ -0,0 +1,74 @@
+package groovy
+
+/**
+ * Check that Object.identity(Closure) method works as expected
+ *
+ * @author Jeremy Rayner
+ * @author Guillaume Laforge
+ */
+class IdentityClosureTest extends GroovyTestCase {
+    
+    def foo = [[1,2,3],[4,5,6],[7,8,9]]
+    def bar = " bar "
+    def mooky = 1
+
+    /** most useful perceived usecase, almost like with(expr) */
+    void testIdentity0() {
+        assert " bar " == bar
+
+        bar.toUpperCase().trim().identity{
+            assert "BAR" == it
+            assert 3 == it.size()
+            assert it.indexOf("A") > 0
+        }
+    }  
+
+    /** check the basics */
+    void testIdentity1() {
+        mooky.identity{ spooky->
+            assert spooky == mooky
+        }
+    }
+
+    /** test temp shortcut to an element of an array */
+    void testIdentity2() {
+        assert 6 == foo[1][2]
+        
+        foo[1].identity{ myArray->
+            myArray[2] = 12
+        }
+        
+        assert 12 == foo[1][2]
+    }
+
+    /** check nested identity usage */
+    void testIdentity3() {
+        mooky.toString().identity{ m->
+            assert "1" == m
+            m += "234567890"
+            m.identity{ n->
+                assert "1234567890" == n
+            }
+        }
+    }
+
+    /** Test the closure delegate */
+    void testClosureDelegate1() {
+        bar.toUpperCase().trim().identity{
+            assert "BAR" == it
+            assert 3 == size()
+            assert indexOf("A") > 0
+        }
+    }
+
+    /** Test the closure delegate with Expandos */
+    void testClosureDelegate2() {
+        def a = new Expando()
+        a.foobar = "foobar"
+        a.barfoo = 555
+        a.identity{
+            assert foobar == "foobar"
+            assert barfoo == 555
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/IfElseCompactTest.groovy b/groovy/src/test/groovy/IfElseCompactTest.groovy
new file mode 100644
index 0000000..87710e9
--- /dev/null
+++ b/groovy/src/test/groovy/IfElseCompactTest.groovy
@@ -0,0 +1,25 @@
+package groovy
+
+class IfElseCompactTest extends GroovyTestCase {
+
+    void testIf_NoElse() {
+
+        def x = false
+
+        if ( true ) {x = true}
+
+        assert x == true
+    }
+
+    void testIf_WithElse_MatchIf() {
+
+        def x = false
+        def y = false
+
+        if ( true ) {x = true} else {y = true}
+
+        assert x == true
+        assert y == false
+
+    }
+}
diff --git a/groovy/src/test/groovy/IfElseTest.groovy b/groovy/src/test/groovy/IfElseTest.groovy
new file mode 100644
index 0000000..3fc18ba
--- /dev/null
+++ b/groovy/src/test/groovy/IfElseTest.groovy
@@ -0,0 +1,133 @@
+package groovy
+
+class IfElseTest extends GroovyTestCase {
+
+    void testIf_NoElse() {
+
+        def x = false
+
+        if ( true ) {
+            x = true
+        }
+
+        assert x == true
+    }
+
+    void testIf_WithElse_MatchIf() {
+
+        def x = false
+        def y = false
+
+        if ( true ) {
+            x = true
+        } else {
+            y = true
+        }
+
+        assert x == true
+        assert y == false
+
+    }
+
+    void testIf_WithElse_MatchElse() {
+
+        def x = false
+        def y = false
+
+        if ( false ) {
+            x = true
+        } else {
+            y = true
+        }
+
+        assertEquals( false, x )
+        assertEquals( true, y )
+    }
+
+    void testIf_WithElseIf_MatchIf() {
+
+        def x = false
+        def y = false
+
+        if ( true ) {
+            x = true
+        } else if ( false ) {
+            y = true
+        }
+
+        assert x == true
+        assert y == false
+    }
+
+    void testIf_WithElseIf_MatchElseIf() {
+
+        def x = false
+        def y = false
+
+        if ( false ) {
+            x = true
+        } else if ( true ) {
+            y = true
+        }
+
+        assertEquals( false, x )
+        assertEquals( true, y )
+    }
+
+    void testIf_WithElseIf_WithElse_MatchIf() {
+
+        def x = false
+        def y = false
+        def z = false
+
+        if ( true ) {
+            x = true
+        } else if ( false ) {
+            y = true
+        } else {
+            z = true
+        }
+
+        assert x == true
+        assert y == false
+        assertEquals( false, z )
+    }
+
+    void testIf_WithElseIf_WithElse_MatchElseIf() {
+
+        def x = false
+        def y = false
+        def z = false
+
+        if ( false ) {
+            x = true
+        } else if ( true ) {
+            y = true
+        } else {
+            z = true
+        }
+
+        assertEquals( false, x )
+        assertEquals( true, y )
+        assertEquals( false, z )
+    }
+
+    void testIf_WithElseIf_WithElse_MatchElse() {
+
+        def x = false
+        def y = false
+        def z = false
+
+        if ( false ) {
+            x = true
+        } else if ( false ) {
+            y = true
+        } else {
+            z = true
+        }
+
+        assertEquals( false, x )
+        assert y == false
+        assertEquals( true, z )
+    }
+}
diff --git a/groovy/src/test/groovy/IfPropertyTest.groovy b/groovy/src/test/groovy/IfPropertyTest.groovy
new file mode 100644
index 0000000..d0f2872
--- /dev/null
+++ b/groovy/src/test/groovy/IfPropertyTest.groovy
@@ -0,0 +1,40 @@
+package groovy
+
+class IfPropertyTest extends GroovyTestCase {
+	
+    def dummy
+    
+	// This is because normal classes are not extensible, but scripts are extensible by default.
+    Object get(String key) {
+        println("asking for def " + key)
+        return dummy
+    }
+
+    void set(Object key, Object value) {
+        println("setting the def " + key + " to: " + value)
+        dummy = value
+    }
+
+    void testIfNullPropertySet() {
+        def cheese = null
+        if (cheese == null) {
+            cheese = 1
+        }
+        if (cheese != 1) {
+            fail("Didn't change cheese")
+        }
+        assert cheese == 1
+    }
+    
+    void testIfNullPropertySetRecheck() {
+        def cheese = null
+        if (cheese == null) {
+            cheese = 1
+        }
+        if (cheese == 1) {
+            cheese = 2
+        }
+        assert cheese == 2
+    }
+    
+}
diff --git a/groovy/src/test/groovy/IfTest.groovy b/groovy/src/test/groovy/IfTest.groovy
new file mode 100644
index 0000000..3de971f
--- /dev/null
+++ b/groovy/src/test/groovy/IfTest.groovy
@@ -0,0 +1,45 @@
+package groovy
+
+class IfTest extends GroovyTestCase {
+
+    void testUsingNumber() {
+        def x = 1
+
+        if (x) {
+            println "${x} is true"
+        }
+        else {
+            fail("should not be false")
+        }
+
+        x = 0
+
+        if (x) {
+            fail("should not be true")
+        }
+        else {
+            println "${x} is false"
+        }
+
+    }
+
+    void testUsingString() {
+        def x = "abc"
+
+        if (x) {
+            println "${x} is true"
+        }
+        else {
+            fail("should not be false")
+        }
+
+        x = ""
+
+        if (x) {
+            fail("should not be true")
+        }
+        else {
+            println "${x} is false"
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/IfWithMethodCallTest.groovy b/groovy/src/test/groovy/IfWithMethodCallTest.groovy
new file mode 100644
index 0000000..da8a93c
--- /dev/null
+++ b/groovy/src/test/groovy/IfWithMethodCallTest.groovy
@@ -0,0 +1,15 @@
+package groovy
+
+class IfWithMethodCallTest extends GroovyTestCase {
+
+    void testIfWithMethodCall() {
+        def x = ["foo", "cheese"]
+
+        if ( x.contains("cheese") ) {
+            // ignore
+        }
+        else {
+            assert false , "x should contain cheese!"
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/ImmutableModificationTest.groovy b/groovy/src/test/groovy/ImmutableModificationTest.groovy
new file mode 100644
index 0000000..fd062b8
--- /dev/null
+++ b/groovy/src/test/groovy/ImmutableModificationTest.groovy
@@ -0,0 +1,26 @@
+package groovy
+
+/**
+ * check that the new asImmutable() method works
+ * as specified in GROOVY-623
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+class ImmutableModificationTest extends GroovyTestCase {
+    void testCollectionAsImmutable() {
+        def challenger = ["Telson", "Sharna", "Darv", "Astra"]
+        def hopefullyImmutable = challenger.asImmutable()
+        try {
+            challenger.add("Angel One")
+            challenger << "Angel Two"
+
+            // @todo fail("'challenger' is supposed to be an immutable collection.")
+
+        } catch (UnsupportedOperationException e) {
+            // success if this exception is thrown
+            assert 4 == challenger.size()
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ImportTest.groovy b/groovy/src/test/groovy/ImportTest.groovy
new file mode 100644
index 0000000..05f56ad
--- /dev/null
+++ b/groovy/src/test/groovy/ImportTest.groovy
@@ -0,0 +1,25 @@
+package groovy
+
+class ImportTest extends GroovyTestCase {
+
+    void testImportAll() {
+        def file = new File("foo.txt")
+        assert file instanceof File
+        assert file.getClass().name == "java.io.File"
+    }
+    
+    void testImportByName() {
+        def x = [:]
+        assert x instanceof Map
+        /**
+         * For maps, map.getClass() should be used instead of map.class,
+         * when map has no member, named as "class"
+         */
+        assert x.getClass() != null
+        assert x.getClass().name.startsWith("java.util.")
+        
+        def y = [1, 2, 3]
+        assert y instanceof List
+        assert y.getClass().name.startsWith("java.util.")
+    }
+}
diff --git a/groovy/src/test/groovy/InstanceofTest.groovy b/groovy/src/test/groovy/InstanceofTest.groovy
new file mode 100644
index 0000000..78312b6
--- /dev/null
+++ b/groovy/src/test/groovy/InstanceofTest.groovy
@@ -0,0 +1,54 @@
+package groovy
+
+class InstanceofTest extends GroovyTestCase {
+
+    void testTrue() {
+
+        def x = false
+        def o = 12
+        
+        if ( o instanceof Integer ) {
+            x = true
+        }
+
+        assert x == true
+    }
+    
+    void testFalse() {
+
+        def x = false
+        def o = 12
+        
+        if ( o instanceof Double ) {
+            x = true
+        }
+
+        assert x == false
+    }
+    
+    void testImportedClass() {
+        def m = ["xyz":2]
+        assert m instanceof Map
+        assert !(m instanceof Double)
+        
+        assertTrue(m instanceof Map)
+        assertFalse(m instanceof Double)
+    }
+    
+    void testFullyQualifiedClass() {
+        def l = [1, 2, 3]
+        assert l instanceof java.util.List
+        assert !(l instanceof Map)
+        
+        assertTrue(l instanceof java.util.List)
+        assertFalse(l instanceof Map)
+    }
+    
+    void testBoolean(){
+       assert true instanceof Object
+       assert true==true instanceof Object
+       assert true==false instanceof Object
+       assert true==false instanceof Boolean
+       assert !new Object() instanceof Boolean
+    }
+}
diff --git a/groovy/src/test/groovy/InvokeNormalMethodsFirstTest.groovy b/groovy/src/test/groovy/InvokeNormalMethodsFirstTest.groovy
new file mode 100644
index 0000000..9272db2
--- /dev/null
+++ b/groovy/src/test/groovy/InvokeNormalMethodsFirstTest.groovy
@@ -0,0 +1,61 @@
+package groovy
+
+/**
+ * Invoke normal methods first: if no statically typed method exist, use invokeMethod().
+ *
+ * @author Guillaume Laforge
+ */
+class InvokeNormalMethodsFirstTest extends GroovyTestCase {
+
+    void testPrintln() {
+        println "call global println function"
+    }
+
+    void testStaticMethodOnJdkObject() {
+        def myString = " static method "
+        def newString = myString.trim()
+
+        assert newString == "static method"
+    }
+
+    void testCallClosure() {
+        def clos = { msg -> msg + " is Groovy" }
+        def str = clos("Guillaume")
+
+        assert str == "Guillaume is Groovy"
+    }
+
+    void testCallNormalMethodFromAGroovyDefinedClass() {
+        def p = new Printer()
+        def str = "Guillaume"
+        def result = p.returnSelf(str)
+
+        assert result == str
+    }
+
+    void testCallNormalMethodFirstFromWackyObject() {
+        def w = new Wacky()
+        def str = "Groovy"
+        def staticResult = w.returnSelf(str)
+        def invokeResult = w.nonExistingMethod(str)
+
+        assert staticResult == str
+        assert invokeResult == "invokerMethod call"
+    }
+}
+
+class Printer {
+    String returnSelf(msg) {
+        return msg
+    }
+}
+
+class Wacky {
+    String returnSelf(msg) {
+        return msg
+    }
+
+    Object invokeMethod(String name, Object args) {
+        return "invokerMethod call"
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/JavaClass.java b/groovy/src/test/groovy/JavaClass.java
new file mode 100644
index 0000000..8464395
--- /dev/null
+++ b/groovy/src/test/groovy/JavaClass.java
@@ -0,0 +1,22 @@
+package groovy;
+
+public class JavaClass {
+
+    public static class StaticInner {
+        int getResult () {
+            return 239;
+        }
+
+         static long getIt () {
+            return 30;
+         }
+
+        public static class Inner2 {}
+    }
+
+    class NonStaticInner {
+
+    }
+
+    static final StaticInner CONST = new StaticInner();
+}
diff --git a/groovy/src/test/groovy/LeftShiftTest.groovy b/groovy/src/test/groovy/LeftShiftTest.groovy
new file mode 100644
index 0000000..0a2586c
--- /dev/null
+++ b/groovy/src/test/groovy/LeftShiftTest.groovy
@@ -0,0 +1,35 @@
+package groovy
+
+class LeftShiftTest extends GroovyTestCase {
+
+    def foo = [1, 2, 3]
+
+    void testShift() {
+        def x = 4
+
+        def y = x << 2
+
+        println "Value is $y"
+
+        assert y == 16
+
+        assert x << 2 == 16
+    }
+
+    void testShiftList() {
+        def list = []
+
+        for (i in 1..10) {
+            list << i
+        }
+
+        println "List is $list"
+    }
+
+    void testLeftShiftOnExpression() {
+        this.foo << 4
+
+        assert foo == [1, 2, 3, 4]
+    }
+
+}
diff --git a/groovy/src/test/groovy/ListIteratingTest.groovy b/groovy/src/test/groovy/ListIteratingTest.groovy
new file mode 100644
index 0000000..3599e5b
--- /dev/null
+++ b/groovy/src/test/groovy/ListIteratingTest.groovy
@@ -0,0 +1,59 @@
+package groovy
+
+/** 
+ * Tests iterating using Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ListIteratingTest extends GroovyTestCase {
+
+/** @todo parser
+    testIteratingWithTuples() {
+        def s = 1, 2, 3, 4
+        assertSequence(s)
+    }
+
+    testIteratingWithTuplesAsParameter() {
+        assertSequence(1, 2, 3, 4)
+    }
+*/
+
+    void testIteratingWithSequences() {
+        def s = [1, 2, 3, 4 ]
+        assertSequence(s)
+    }
+    
+    void testIteratingWithSequencesAsParameter() {
+        assertSequence([1, 2, 3, 4 ])
+    }
+    
+    def testIteratingWithList() {
+        def s = new ArrayList()
+        s.add(1)
+        s.add(2)
+        s.add(3)
+        s.add(4)
+        assertSequence(s)
+    }
+
+    protected void assertSequence(s) {
+        def result = 0
+        for ( i in s ) {
+            result = result + i
+        }
+	    
+        assert(result == 10)
+        assert(s.size() == 4)
+        
+        assert(s[2] == 3)
+        // @todo parser (Why @todo here?)
+        //   s[1,2] or s[1..2] should be used instead of s[1:2],
+        //   since [1:2] is a map literal syntax.
+        result = 0
+        for ( i in s[1,2] ) {    // or s[1..2]
+            result += i
+        }
+        assert(result == 2+3)
+    }
+}
diff --git a/groovy/src/test/groovy/ListTest.groovy b/groovy/src/test/groovy/ListTest.groovy
new file mode 100644
index 0000000..1b8ecb7
--- /dev/null
+++ b/groovy/src/test/groovy/ListTest.groovy
@@ -0,0 +1,426 @@
+package groovy
+
+// TODO: split a couple of Set tests into their own class?
+class ListTest extends GroovyTestCase {
+
+    void testList() {
+        def x = [10, 11]
+
+        assert x.size() == 2
+
+        x.add("cheese")
+
+        assert x.size() == 3
+
+        assert x.contains(10)
+        assert x.contains(11)
+        assert x.contains("cheese")
+
+        assert x.get(0) == 10
+        assert x.get(1) == 11
+        assert x.get(2) == "cheese"
+
+        // subscript operator
+        assert x[0] == 10
+        assert x[1] == 11
+        assert x[2] == "cheese"
+
+        x[3] = 12
+
+        assert x[3] == 12
+
+        if ( x.contains("cheese") ) {
+            // ignore
+        } else {
+            assert false , "x should contain cheese!"
+        }
+
+        if ( x.contains(10) ) {
+            // ignore
+        } else {
+            assert false , "x should contain 1!"
+        }
+    }
+    
+    void testEmptyList() {
+        def x = []
+        
+        assert x.size() == 0
+        
+        x.add("cheese")
+
+        assert x.get(0) == "cheese"
+
+        assert x.size() == 1
+
+        assert x[0] == "cheese"
+    }
+    
+    void testSubscript() {
+        def x = []
+        x[1] = 'cheese'
+        
+        assert x[0] == null
+        assert x[1] == 'cheese'
+        
+        x[2] = 'gromit'
+        x[3] = 'wallace'
+        
+        assert x.size() == 4
+        
+        x[-1] = 'nice'
+        
+        assert x[3] == 'nice'
+        
+        x[-2] = 'cheese'
+        
+        assert x[2] == 'cheese'
+    }
+    
+    void testClosure() {
+        def l = [1, 2, 3, "abc"]
+        def block = {i -> println(i) }
+        l.each(block)
+
+        l.each {i-> println(i) }
+    }
+    
+    void testMax() {
+        def l = [1, 2, 5, 3, 7, 1]
+        assert l.max() == 7
+        
+        l = [7, 2, 3]
+        assert l.max() == 7
+        
+        l = [1, 2, 7]
+        assert l.max() == 7
+
+        // GROOVY-1006        
+        l = [1, 3.2, 4L, (short)7]
+        assert l.max() == (short)7
+    }
+    
+    void testMin() {
+        def l = [6, 4, 5, 1, 7, 2]
+        assert l.min() == 1
+        
+        l = [7, 1, 3]
+        assert l.min() == 1
+        
+        l = [1, 2, 7]
+        assert l.min() == 1
+
+        // GROOVY-1006        
+        l = [(long)1, 3.2, 4L, (short)7]
+        assert l.min() == (long)1
+    }
+    
+    void testPlus() {
+        def l1 = [6, 4, 5, 1, 7, 2]
+        def l2 = [6, 4, 5, 1, 7, [4,5]]
+        def l3 = l1 + l2
+        assert l3 == [6, 4, 5, 1, 7, 2, 6, 4, 5, 1, 7, [4,5]]
+
+        l1 = [1, 5.2, 9]
+        l2 = [3, 4L]
+        l3 = [1, 5.2, 9, 3, 4L]
+        assert l1 + l2 == l3
+    }
+
+    void testSetPlus() {
+        Set s1 = [6, 4, 5, 1, 7, 2]
+        def s2 = [6, 4, 5, 1, 7, [4,5]]
+        def s3 = s1 + s2
+        assert s3 == [1, 2, 4, 5, 6, 7, [4,5]] as Set
+    }
+
+    void testPlusOneElement() {
+        def l1 = [6, 4, 5, 1, 7, 2]
+        def l2 = "erererer"
+        assert l1 + l2 == [6, 4, 5, 1, 7, 2, "erererer"]            
+    }
+
+    void testListAppend() {
+        def list = [1, 2]
+        
+        list << 3 << 4 << 5
+        
+        assert list == [1, 2, 3, 4, 5]
+        
+        def x = [] << 'a' << 'hello' << [2, 3] << 5
+        
+        assert x == ['a', 'hello', [2, 3], 5]
+    }
+
+    void testTimes() {
+        def l = [4,7,8]
+        assert l * 3 == [4, 7, 8, 4, 7, 8, 4, 7, 8]
+    }
+
+    // GROOVY-1006
+    void testMinus() {
+        def l1 = [1, 1, 2, 2, 3, 3, 3, 4, 5, 3, 5]
+        def l2 = [1, 2.0, 4L]
+        assert l1 - l2 == [3, 3, 3, 5, 3, 5] 
+    }
+
+    void testSetSimpleMinus() {
+        Set s1 = [1, 1, 2, 2, 3, 3, 3, 4, 5, 3, 5]
+        def s2 = s1 - [1, 4]
+        assert s2 == [2, 3, 5] as Set
+        def s3 = s1 - 4.0
+        assert s3 == [1, 2, 3, 5] as Set
+    }
+
+    // GROOVY-1006
+    void testMinusDifferentTypes() {
+        def l1 = [1, 1, "wrer", 2, 3, 3, "wrewer", 4, 5, "w", "w"]
+        def l2 = [1, 2, "w"]
+        assert l1 - l2 == ["wrer", 3, 3, "wrewer", 4, 5] 
+    }
+
+    void testMinusEmptyCollection(){
+        // GROOVY-790
+        def list = [1,1]
+        assert list - [] == list
+
+        // GROOVY-1006    
+        list = [1,2,2,3,1]
+        assert list - [] == list
+    }
+     
+    void testIntersect() {
+        def l1 = [1, 1, "wrer", 2, 3, 3, "wrewer", 4, 5, "w", "w"]
+        def l2 = [1, 2, "f", "w"]
+        assert l1.intersect(l2) == [1, 2, "w"] 
+
+        // GROOVY-1006    
+        l1 = [1, 1.0, "wrer", 2, 3, 3L, "wrewer", 4, 5, "w", "w"]
+        l2 = [(double)1, 2L, "f", "w"]
+        assert l1.intersect(l2) == [1, 2, "w"] 
+    }
+      
+    // GROOVY-1006
+    void testListEqual() {
+        assert [1, 2.0, 3L, (short)4] == [1, 2, 3, 4]
+    }
+      
+    // GROOVY-1006
+    void testSortNumbersMixedType() {
+        assert [1, (short)3, 4L, 2.9, (float)5.2].sort() == [1, 2.9, (short)3, 4L, (float)5.2] 
+    }
+      
+    // GROOVY-1006
+    void testUnique() {
+        def a = [1, 4L, 1.0]
+        def b = a.unique()
+        assert (b == a && a == [1, 4])
+        a =[1, "foo", (short)3, 4L, 1.0, (float)3.0]
+        b = a.unique()
+        assert (b == a && a == [1, "foo", (short)3, 4L])
+    }
+
+    void testListFlatten() {
+        def l = [[[4, 5, 6, [46, 7, "erer"]], 4, [3, 6, 78]], 4]
+        assert l.flatten() == [4, 5, 6, 46, 7, "erer", 4, 3, 6, 78, 4]
+    }
+    
+    void testSetFlatten() {
+        Set l = [[[4, 5, 6, [46, 7, "erer"] as Set] as Set, 4, [3, 6, 78] as Set] as Set, 4]
+        assert l.flatten() == [3, 4, 5, 6, 7, 46, 78, "erer"] as Set
+    }
+
+    void testFlattenWithRanges() {
+        def flat = [1, 3, 20..24, 33].flatten()
+        assert flat == [1, 3, 20, 21, 22, 23, 24, 33]
+    }
+    
+    void testListsAndRangesCompare() {
+        def l = [1, 2, 3]
+        def r = 1..3
+        assert r == l
+        assert l == r
+    }
+    
+    void testRemove() {
+        def l = ['a', 'b', 'c']
+        l.remove(1)
+        assert l == ['a', 'c']
+        l.remove(0)
+        assert l == ['c']
+        assert l.size() == 1
+    }
+    
+    void testPop() {
+        def l = []
+        l << 'a' << 'b'
+        def value = l.pop()
+        assert value == 'b'
+        assert l == ['a']
+        
+        l.add('c')
+        value = l.pop()
+        assert value == 'c'
+        value = l.pop()
+        assert value == 'a'
+        try {
+            value = l.pop()
+            fail("Should have thrown an exception")
+        }
+        catch (NoSuchElementException e) {
+            println "Worked: caught expected exception: ${e}"
+        }
+    }
+
+    void testBoolCoerce() {
+        // Explicit coercion
+        assertFalse((Boolean) [])
+        assertTrue((Boolean) [1])
+
+        // Implicit coercion in statements
+        List list = null
+        if (list) {
+            fail("null should have evaluated to false, but didn't")
+        }
+        list = []
+        if (list) {
+            fail("[] should have evaluated to false, but didn't")
+        }
+        list = [1]
+        if (list) {
+            // OK
+        } else {
+            fail("[] should have evaluated to false, but didn't")
+        }
+    }
+
+    // see also SubscriptTest
+    void testGetAtRange(){
+        def list = [0,1,2,3]
+        assert list[0..3] == list           , 'full list'
+        assert list[0..0] == [0]            , 'one element range'
+        assert list[0..<0] == []            , 'empty range'
+        assert list[3..0] == [3,2,1,0]      , 'reverse range'
+        assert list[3..<0] == [3,2,1]       , 'reverse exclusive range'
+        assert list[-2..-1] == [2,3]        , 'negative index range'
+        assert list[-2..<-1] == [2]         , 'negative index range exclusive'
+        assert list[-1..-2] == [3,2]        , 'negative index range reversed'
+        assert list[-1..<-2] == [3]         , 'negative index range reversed exclusive'  // aaaahhhhh !
+        assert list[0..-1] == list          , 'pos - neg value'
+        assert list[0..<-1] == [0]          , 'pos - neg value exclusive -> empty'
+        assert list[0..<-2] == list         , 'pos - neg value exclusive -> full'
+        // TODO reinstate this test
+        //shouldFail (NullPointerException.class)      { list[null] }
+        shouldFail (IndexOutOfBoundsException.class) { list[5..6] }
+    }
+
+    void testPutAtSplice(){
+        // usual assignments
+        def list = [0,1,2,3]
+        list[1,2] = [11,12]
+        assert list == [0, 11, 12, 3 ]      , 'same length assignment'
+        list = [0,1,2,3]
+        list[1,1] = [11]
+        assert list == [0, 11, 2, 3 ]       , 'length 1 assignment'
+        list = [0,1,2,3]
+        list[1,0] = [ ]
+        assert list == [0, 1, 2, 3 ]        , 'length 0 assignment, empty splice'
+        // assignments at bounds
+        list = [0,1,2,3]
+        list[0,0] = [10]
+        assert list == [10, 1, 2, 3 ]       , 'left border assignment'
+        list = [0,1,2,3]
+        list[3,3] = [13]
+        assert list == [0, 1, 2, 13 ]       , 'right border assignment'
+        // assignments outside current bounds
+        list = [0,1,2,3]
+        list[-1,-1] = [-1]
+        assert list == [0, 1, 2, -1]        , 'left of left border'
+        list = [0,1,2,3]
+        shouldFail (IndexOutOfBoundsException.class) {
+            list[3,4] = [3,4]
+            assert list == [0, 1, 2, 3, 4]
+        }
+        // structural changes
+        list = [0,1,2,3]
+        list[1,2] = ['x']
+        assert list == [0, 'x', 3]          , 'compacting'
+        list = [0,1,2,3]
+        list[1,2] = ['x','x','x']
+        assert list == [0, 'x','x','x', 3]  , 'extending'
+    }
+
+    void testPutAtRange(){
+        // usual assignments
+        def list = [0,1,2,3]
+        list[1..2] = [11,12]
+        assert list == [0, 11, 12, 3 ]      , 'same length assignment'
+        list = [0,1,2,3]
+        list[1..1] = [11]
+        assert list == [0, 11, 2, 3 ]       , 'length 1 assignment'
+        list = [0,1,2,3]
+        list[0..<0] = []
+        assert list == [0, 1, 2, 3 ]        , 'length 0 assignment, empty splice'
+        // assignments at bounds
+        list = [0,1,2,3]
+        list[0..0] = [10]
+        assert list == [10, 1, 2, 3 ]       , 'left border assignment'
+        list = [0,1,2,3]
+        list[3..3] = [13]
+        assert list == [0, 1, 2, 13 ]       , 'right border assignment'
+        // assignments outside current bounds
+        list = [0,1,2,3]
+        list[-1..-1] = [-1]
+        assert list == [0, 1, 2, -1]        , 'left of left border'
+        list = [0,1,2,3]
+        list[3..4] = [3,4]
+        assert list == [0, 1, 2, 3, 4]
+        // structural changes
+        list = [0,1,2,3]
+        list[1..2] = ['x']
+        assert list == [0, 'x', 3]          , 'compacting'
+        list = [0,1,2,3]
+        list[1..2] = ['x','x','x']
+        assert list == [0, 'x','x','x', 3]  , 'extending'
+    }
+
+    void testCrazyPutAtRange(){
+        def list = []
+        list[0..<0] = [0,1,2,3]
+        assert list == [0, 1, 2, 3 ]        , 'fill by empty Range'
+        list = [0,1,2,3]
+        list[3..0] = []
+        assert list == []                   , 'delete by reverse Range'
+        list = [0,1,2,3]
+        list[-4..-1] = []
+        assert list == []                   , 'delete by negativ Range'
+        list = [0,1,2,3]
+        list[0..-1] = []
+        assert list == []                   , 'delete by pos-negativ Range'
+    }
+
+    // GROOVY-1128
+    void testAsSynchronized() {
+        def synclist = [].asSynchronized() << 1
+        assert synclist == [1]
+    }
+
+    // GROOVY-1128
+    void testAsImmutable() {
+        def immlist = [1,2,3].asImmutable()
+        assert immlist == [1,2,3]
+        def testlist = ['a','b','c','d','e']
+        assert testlist[immlist] == ['b','c','d']
+        assert immlist[0] == 1
+        assert immlist[0..-1] == immlist
+        shouldFail(UnsupportedOperationException.class){
+            immlist << 1
+        }
+        shouldFail(UnsupportedOperationException.class){
+            immlist[0..<0] = [0]
+        }
+        shouldFail(UnsupportedOperationException.class){
+            immlist[0] = 1
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/LiteralTypesTest.groovy b/groovy/src/test/groovy/LiteralTypesTest.groovy
new file mode 100644
index 0000000..2eff88b
--- /dev/null
+++ b/groovy/src/test/groovy/LiteralTypesTest.groovy
@@ -0,0 +1,145 @@
+package groovy
+
+/**
+ * Test numeric literal types (with and without suffixes)
+ * @see org.codehaus.groovy.syntax.parser/ASTBuilder#createIntegralNumber()
+ * @see org.codehaus.groovy.syntax.parser/ASTBuilder#createDecimalNumber()
+ *
+ * @author Brian Larson
+ */
+class LiteralTypesTest extends GroovyTestCase {
+
+    void testIntegral() {
+        def x = 42;
+        assert x instanceof Integer;
+
+        x = 42I;
+        assert x instanceof Integer;
+
+        x = 42i;
+        assert x instanceof Integer;
+
+        x = 42L;
+        assert x instanceof Long;
+
+        x = 42G;
+        assert x instanceof BigInteger;
+
+        x = 0xFF; //Hex
+        assert x instanceof Integer;
+        assert x == new Integer("255");
+
+        x = 0xFFL; //Hex
+        assert x instanceof Long;
+        assert x == new Long("255");
+
+        x = 0xFFG; //Hex
+        assert x instanceof BigInteger;
+        assert x == new BigInteger("FF",16);
+
+        x = 0x9000000000000000;
+        assert x instanceof BigInteger;
+        assert x == new BigInteger("9000000000000000",16);
+
+        x = 077; //octal
+        assert x instanceof Integer;
+        assert x == new Integer("63");
+
+        x = 077l; //octal
+        assert x instanceof Long;
+        assert x == new Long("63");
+
+        x = 077g; //octal
+        assert x instanceof BigInteger;
+        assert x == new BigInteger("77",8);
+
+        x = 2147483647;           // max integer value
+        assert x instanceof Integer;
+        assert x == new Integer("2147483647");
+
+        x = -2147483648;          // min integer constant
+        assert x < 0
+        assert x == new Integer("-2147483648");
+        assert x instanceof Integer, x.class;
+
+        x = -2147483649;          // min integer value - 1
+        assert x == new Long("-2147483649");
+        assert x instanceof Long;
+
+        x = 2147483648;           // 1 + max integer value
+        assert x == new Long("2147483648");
+        assert x instanceof Long;
+
+        x = 9223372036854775807;  // max long value
+        assert x == new Long("9223372036854775807");
+        assert x instanceof Long;
+
+        x = -9223372036854775808; // min long value
+        assert x == new Long("-9223372036854775808");
+        assert x instanceof Long;
+
+        x = -9223372036854775809; // min long value - 1
+        assert x == new BigInteger("-9223372036854775809");
+        assert x instanceof BigInteger;
+
+        x = 9223372036854775808;  // 1 + max long value
+        assert x == new BigInteger("9223372036854775808");
+        assert x instanceof BigInteger;
+    }
+
+    void testDecimal() {
+        def x = 3.2;
+        assert x instanceof BigDecimal;
+        assert x == new BigDecimal("3.2");
+
+        x = 3.2G;
+        assert x instanceof BigDecimal;
+        assert x == new BigDecimal("3.2");
+
+        x = 3.2g;
+        assert x instanceof BigDecimal;
+        assert x == new BigDecimal("3.2");
+
+        x = -3.2;
+        assert x instanceof BigDecimal;
+        assert x == new BigDecimal("-3.2");
+
+        x = 3.2D;
+        assert x instanceof Double;
+        assert x == new Double("3.2");
+
+        x = -3.2d;
+        assert x instanceof Double;
+        assert x == new Double("-3.2");
+
+        x = 3.2F;
+        assert x instanceof Float;
+        assert x == new Float("3.2");
+
+        x = -3.2f;
+        assert x instanceof Float;
+        assert x == new Float("-3.2");
+    }
+
+    void testExponential() {
+        def x = 3.1415926535e42;
+        assert x instanceof BigDecimal;
+        assert x == new BigDecimal("3.1415926535e42");
+
+        x = 3.2e+2;
+        assert x instanceof BigDecimal;
+        assert x == new BigDecimal("3.2e+2");
+
+        x = 3.2e-2;
+        assert x instanceof BigDecimal;
+        assert x == new BigDecimal("3.2e-2");
+
+        x = 3.2e2d;
+        assert x instanceof Double;
+        assert x == new Double("3.2e2");
+
+        x = 3.2e2f;
+        assert x instanceof Float;
+        assert x == new Float("3.2e2");
+    }
+}
diff --git a/groovy/src/test/groovy/LittleClosureTest.groovy b/groovy/src/test/groovy/LittleClosureTest.groovy
new file mode 100644
index 0000000..c4a9d6c
--- /dev/null
+++ b/groovy/src/test/groovy/LittleClosureTest.groovy
@@ -0,0 +1,8 @@
+package groovy
+
+class LittleClosureTest extends GroovyTestCase {
+
+    void testClosure() {
+        def block = {x-> return x > 5}
+    }
+}
diff --git a/groovy/src/test/groovy/LocalFieldTest.groovy b/groovy/src/test/groovy/LocalFieldTest.groovy
new file mode 100644
index 0000000..72a3835
--- /dev/null
+++ b/groovy/src/test/groovy/LocalFieldTest.groovy
@@ -0,0 +1,13 @@
+package groovy
+
+class LocalFieldTest extends GroovyTestCase {
+
+    private def x
+	
+    void testAssert() {
+        this.x = "abc"
+
+        assert this.x == "abc"
+        assert this.x != "def"
+    }
+}
diff --git a/groovy/src/test/groovy/LocalPropertyTest.groovy b/groovy/src/test/groovy/LocalPropertyTest.groovy
new file mode 100644
index 0000000..8e59bf8
--- /dev/null
+++ b/groovy/src/test/groovy/LocalPropertyTest.groovy
@@ -0,0 +1,20 @@
+package groovy
+
+class LocalPropertyTest extends GroovyTestCase {
+
+    def x
+    
+	void testNormalPropertyAccess() {
+	    x = "abc"
+	    
+	    assert x == "abc"
+        assert x != "def"
+	}
+	
+	void testPropertyWithThis() {
+        this.x = "abc"
+	    
+	    assert this.x == "abc"
+	    assert this.x != "def"
+	}
+}
diff --git a/groovy/src/test/groovy/LocalVariableTest.groovy b/groovy/src/test/groovy/LocalVariableTest.groovy
new file mode 100644
index 0000000..1e532d4
--- /dev/null
+++ b/groovy/src/test/groovy/LocalVariableTest.groovy
@@ -0,0 +1,25 @@
+package groovy
+
+class LocalVariableTest extends GroovyTestCase {
+
+    void testAssert() {
+        def x = "abc"
+
+        assert x != "foo"
+        assert x !=  null
+        assert x != "def"
+        assert x == "abc"
+        
+        assert x.equals("abc")
+    }
+    
+    void testUnknownVariable() {
+
+        shouldFail {
+            def shell = new GroovyShell()
+            shell.evaluate """
+                def y = x
+            """
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/LogTest.groovy b/groovy/src/test/groovy/LogTest.groovy
new file mode 100644
index 0000000..5343ab1
--- /dev/null
+++ b/groovy/src/test/groovy/LogTest.groovy
@@ -0,0 +1,26 @@
+package groovy
+
+/** 
+ * Tests the use of GroovyLog
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class LogTest extends GroovyTestCase {
+
+    void testUseLog() {
+        def file = "something.txt"
+
+        def log = GroovyLog.newInstance(getClass())
+        
+        log.starting("Hey I'm starting up...")
+        
+        log.openFile("Am about to open file ${file}")
+
+        // ...
+
+        log.closeFile("Have closed the file ${file}")
+
+        log.stopping("..Finished")
+	}
+}
diff --git a/groovy/src/test/groovy/LogicTest.groovy b/groovy/src/test/groovy/LogicTest.groovy
new file mode 100644
index 0000000..8bd2a25
--- /dev/null
+++ b/groovy/src/test/groovy/LogicTest.groovy
@@ -0,0 +1,77 @@
+package groovy
+
+class LogicTest extends GroovyTestCase {
+
+    void testAndWithTrue() {
+
+        def x = false
+        def n = 2
+        
+        if ( n > 1 && n < 10 ) {
+            x = true
+        }
+
+        assert x == true
+    }
+
+    void testAndWithFalse() {
+
+        def x = false
+        def n = 20
+        
+        if ( n > 1 && n < 10 ) {
+            x = true
+        }
+
+        assert x == false
+
+        n = 0
+        
+        if ( n > 1 && n < 10 ) {
+            x = true
+        }
+
+        assert x == false
+    }
+
+    void testOrWithTrue() {
+
+        def x = false
+        def n = 2
+        
+        if ( n > 1 || n < 10 ) {
+            x = true
+        }
+
+        assert x == true
+
+        x = false
+        n = 0
+        
+        if ( n > 1 || n == 0 ) {
+            x = true
+        }
+
+        assert x == true
+    }
+
+    void testOrWithFalse() {
+
+        def x = false
+        def n = 11
+        
+        if ( n < 10 || n > 20 ) {
+            x = true
+        }
+
+        assert x == false
+
+        n = 11
+        
+        if ( n < 10 || n > 20 ) {
+            x = true
+        }
+    
+        assert x == false
+    }
+}
diff --git a/groovy/src/test/groovy/LoopBreakTest.groovy b/groovy/src/test/groovy/LoopBreakTest.groovy
new file mode 100644
index 0000000..e1c8a71
--- /dev/null
+++ b/groovy/src/test/groovy/LoopBreakTest.groovy
@@ -0,0 +1,53 @@
+package groovy
+
+class LoopBreakTest extends GroovyTestCase {
+
+    void testWhileWithBreak() {
+        def x = 0
+        while (true) {
+            if (x == 5) {
+                break
+            }
+            ++x
+
+            assert x < 10 , "Should never get here"
+        }
+        
+        println "worked: while completed with value ${x}"
+    }
+    
+    /**
+
+      We currently do not support do ... while in the JSR syntax
+
+    void testDoWhileWithBreak() {
+        def x = 0
+        do {
+            //println "in do-while loop and x = ${x}"
+
+            if (x == 5) {
+                break
+            }
+            ++x
+
+            assert x < 10 , "Should never get here"
+        }
+        while (true)
+
+        println "worked: do-while completed with value ${x}"
+    }
+    */
+
+    void testForWithBreak() {
+        def returnValue
+        for (x in 0..20) {
+            if (x == 5) {
+                returnValue = x
+                break
+            }
+            assert x < 10 , "Should never get here"
+        }
+        
+        println "worked: for loop completed with value ${returnValue}"
+    }
+ }
diff --git a/groovy/src/test/groovy/MapConstructionTest.groovy b/groovy/src/test/groovy/MapConstructionTest.groovy
new file mode 100644
index 0000000..6d53c55
--- /dev/null
+++ b/groovy/src/test/groovy/MapConstructionTest.groovy
@@ -0,0 +1,52 @@
+package groovy
+
+/** 
+ * Tests creating Maps in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class MapConstructionTest extends GroovyTestCase {
+
+    void testMap() {
+        def m = [ 1 : 'abc', 2 : 'def', 3 : 'xyz' ]
+
+        println(m)
+
+        def mtoo = [ 1 : [ "innerKey" : "innerValue" ], 2 : m ]
+
+        println(mtoo)
+
+        assertMap(m)
+    }
+
+    def testMapAsParameter() {
+        assertMap([ 1 : 'abc', 2 : 'def', 3 : 'xyz' ])
+    }
+
+    def testMapViaHashMap() {
+        def m = new HashMap()
+        m.put(1, 'abc')
+        m.put(2, 'def')
+        m.put(3, 'xyz')
+        assertMap(m)
+    }
+
+    void assertMap(m) {
+        assert m instanceof Map
+        // do not test the final type, i.e. assumiong m is a HashMap
+
+        def result = 0
+        def text = ""
+        for ( e in m ) {
+            result = result + e.key
+            text = text + e.value
+        }
+        assert result == 6
+        assert text == "abcdefxyz"
+	    
+        assert m.size() == 3
+
+        assert m[2] == 'def'
+    }
+}
diff --git a/groovy/src/test/groovy/MapPropertyTest.groovy b/groovy/src/test/groovy/MapPropertyTest.groovy
new file mode 100644
index 0000000..e44cf47
--- /dev/null
+++ b/groovy/src/test/groovy/MapPropertyTest.groovy
@@ -0,0 +1,54 @@
+package groovy
+
+/** 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class MapPropertyTest extends GroovyTestCase {
+
+    void testGetAndSetProperties() {
+        def m = [ 'name' : 'James', 'location' : 'London', 'id':1 ]
+        
+        assert m.name == 'James'
+        assert m.location == 'London'
+        assert m.id == 1
+        
+        m.name = 'Bob'
+        m.location = 'Atlanta'
+        m.id = 2
+        
+        assert m.name == 'Bob'
+        assert m.location == 'Atlanta'
+        assert m.id == 2
+    }
+
+    void testSetupAndEmptyMap() {
+        def m = [:]
+        
+        m.name = 'Bob'
+        m.location = 'Atlanta'
+        m.id = 2
+        
+        assert m.name == 'Bob'
+        assert m.location == 'Atlanta'
+        assert m.id == 2
+    }
+    
+    void testMapSubclassing() {
+        def c = new MyClass()
+
+        c.id = "hello"
+        c.class = 1
+        c.myMethod()
+        assert c.id == "hello"
+        assert c.class == 1
+        assert c.getClass() != 1
+    }
+}
+
+class MyClass extends HashMap {
+    def myMethod() {
+        assert id == "hello"
+        assert this.class == 1
+    }
+}
diff --git a/groovy/src/test/groovy/MapTest.groovy b/groovy/src/test/groovy/MapTest.groovy
new file mode 100644
index 0000000..7fcd0a0
--- /dev/null
+++ b/groovy/src/test/groovy/MapTest.groovy
@@ -0,0 +1,110 @@
+package groovy
+
+class MapTest extends GroovyTestCase {
+
+    void testMap() {
+
+        def m = [1:'one', '2':'two', 3:'three']
+
+        assert m.size() == 3
+        assert m.get(1) == 'one'
+        assert m.get('2') == 'two'
+        assert m.get(3) == 'three'
+        
+        assert m.containsKey(1)
+        assert m.containsKey('2')
+        assert m.containsKey(3)
+
+        assert m.containsValue('one')
+        assert m.containsValue('two')
+        assert m.containsValue('three')
+
+        assert m.keySet().size() == 3
+        assert m.values().size() == 3
+        assert m.keySet().contains(1)
+        assert m.values().contains('one')
+
+        m.remove(1)
+        m.remove('2')
+
+        assert m.size() == 1
+        assert m.get('1') == null
+        assert m.get('2') == null
+        
+        m.put('cheese', 'cheddar')
+
+        assert m.size() == 2
+
+        assert m.containsKey("cheese")
+        assert m.containsValue("cheddar")
+
+
+        if ( m.containsKey("cheese") ) {
+            // ignore
+        }
+        else {
+            assert false , "should contain cheese!"
+        }
+
+        if ( m.containsKey(3) ) {
+            // ignore
+        }
+        else {
+            assert false , "should contain 3!"
+        }
+    }
+    
+    void testEmptyMap() {
+        def m = [:]
+
+        assert m.size() == 0
+        assert !m.containsKey("cheese")
+
+        m.put("cheese", "cheddar")
+
+        assert m.size() == 1
+        assert m.containsKey("cheese")
+    }
+    
+    void testMapMutation() {    
+        def m = [ 'abc' : 'def', 'def' : 134, 'xyz' : 'zzz' ]
+
+        assert m['unknown'] == null
+
+        assert m['def'] == 134
+
+        m['def'] = 'cafebabe'
+
+        assert m['def'] == 'cafebabe'
+
+        assert m.size() == 3
+
+        m.remove('def')
+
+        assert m['def'] == null
+        assert m.size() == 2
+        
+        def foo = m['def'] = 5
+        assert m['def'] == 5
+        assert foo == null
+    }
+
+    void testFindAll(){
+        assert [a:1] == ['a':1, 'b':2].findAll {it.value == 1}
+        assert [a:1] == ['a':1, 'b':2].findAll {it.key == 'a'}
+        assert [a:1] == ['a':1, 'b':2].findAll {key,value -> key == 'a'}
+        assert [a:1] == ['a':1].findAll {true}
+        assert [:]   == ['a':1].findAll {false}
+    }
+
+    void testMapAddition() {
+        def left = [a:1, b:2]
+        def right = [c:3]
+        assert left + right == [a:1, b:2, c:3], "should contain all entries from both maps"
+        assert left == [a:1, b:2] && right == [c:3], "LHS/RHS should not be modified"
+
+        left = [a:1, b:1]
+        right = [a:2]
+        assert left + right == [a:2, b:1], "RHS should take precedence when entries have same key"
+    }
+}
diff --git a/groovy/src/test/groovy/MethodCallTest.groovy b/groovy/src/test/groovy/MethodCallTest.groovy
new file mode 100644
index 0000000..71bb1e3
--- /dev/null
+++ b/groovy/src/test/groovy/MethodCallTest.groovy
@@ -0,0 +1,31 @@
+package groovy
+
+class MethodCallTest extends GroovyTestCase {
+
+    void testMethodCall() {
+        System.out.print("hello")
+        println("world!")
+    }
+
+    void testObjectMethodCall() {
+        def c = getClass()
+        assert c != null
+        assert c.name.endsWith("MethodCallTest")
+        assert c.getName().endsWith("MethodCallTest")
+    }
+
+    void testObjectMethodCall2() {
+        def s = "hello"
+        def c = s.getClass()
+        assert c != null
+        assert c.name == "java.lang.String"
+        assert c.getName() == "java.lang.String"
+    }
+
+    void testGetNameBug() {
+        def c = getClass()
+        def n = c.getName()
+        assert c.getName().endsWith("MethodCallTest")
+        assert n.endsWith("MethodCallTest")
+    }
+}
diff --git a/groovy/src/test/groovy/MethodCallWithoutParenthesisTest.groovy b/groovy/src/test/groovy/MethodCallWithoutParenthesisTest.groovy
new file mode 100644
index 0000000..a81c3b0
--- /dev/null
+++ b/groovy/src/test/groovy/MethodCallWithoutParenthesisTest.groovy
@@ -0,0 +1,49 @@
+package groovy
+
+class MethodCallWithoutParenthesisTest extends GroovyTestCase {
+
+    def flag
+
+    void testMethodCallWithOneParam() {
+        flag = false
+        
+        methodWithOneParam "hello"
+        
+        assert flag
+    }
+    
+    void testMethodCallWithOneParamUsingThis() {
+        flag = false
+        
+        this.methodWithOneParam "hello"
+        
+        assert flag
+    }
+    
+    void methodWithOneParam(text) {
+        println("Called method with parameter ${text}")
+        assert text == "hello"
+        flag = true
+    }
+    
+    void testMethodCallWithTwoParams() {
+        methodWithTwoParams 5, 6
+
+        // not allowed in New Groovy
+        // value = methodWithTwoParams 5, 6
+        def value = methodWithTwoParams(5, 6)
+
+        assert value == 11
+    }
+    
+    void testMethodCallWithTwoParamsUsingThis() {
+        def value = this.methodWithTwoParams(5, 6)
+        
+        assert value == 11
+    }
+
+    def methodWithTwoParams(a, b) {
+        println("Called method with parameters ${a} and ${b}")
+        return a + b
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/MethodParameterAccessWithinClosureTest.groovy b/groovy/src/test/groovy/MethodParameterAccessWithinClosureTest.groovy
new file mode 100644
index 0000000..5b4cfc4
--- /dev/null
+++ b/groovy/src/test/groovy/MethodParameterAccessWithinClosureTest.groovy
@@ -0,0 +1,76 @@
+package groovy
+
+/**
+ * To test access to method scoped variable within closure
+ * 
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+
+class MethodParameterAccessWithinClosureTest extends GroovyTestCase { 
+    def cheese
+    def shop
+       
+    void setUp() {
+        cheese = null
+        shop = ["wensleydale"]
+    }              
+    void testSimpleMethodParameterAccess() { 
+        assert "wensleydale" == vendor1("wensleydale") 
+    }
+    void testMethodParameterWithDifferentNameToPropertyUsingClosure() {
+        assert "wensleydale" == vendor2("wensleydale")
+    }
+    void testMethodParameterWithSameNameAsPropertyUsingClosure() {
+        assert "wensleydale" == vendor3("wensleydale")
+    }
+    
+    void testOptionalMethodParameterUsedInClosure() {
+        assert "wensleydale" == vendor4("wensleydale")
+        assert null == vendor4()
+    }
+    
+    void testDoubleParameterAndsingleParameterUsedInClosure() {
+         assert vendor5(5.0d,2) == 7.0d
+    }
+    
+    void testAccessToMethodParameterInOverwrittenMethodCalledBySuper() {
+         //  GROOVY-2107
+         assertScript """
+           class A {
+             // the closure is accessing the parameter
+             def foo(x){ return {x}}
+           }
+           class B extends A {
+              def foo(y) {
+                 super.foo(y+1)
+              }
+           }
+           def b = new B()
+           assert b.foo(1).call() == 2
+        """        
+    }
+    
+    private String vendor1(cheese) {
+        cheese
+    }
+    
+    private String vendor2(aCheese) {
+        shop.find() {it == aCheese}
+    }
+    
+    private String vendor3(cheese) {
+        shop.find() {it == cheese}
+    }
+    
+    /** note: cheese is a field, that is intended **/
+    private vendor4(aCheese=cheese) {
+        shop.find() {it == aCheese}
+    }
+    
+    private vendor5(double a, int b) {
+        b.times {a++}
+        return a
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/MinMaxTest.groovy b/groovy/src/test/groovy/MinMaxTest.groovy
new file mode 100644
index 0000000..5e148bf
--- /dev/null
+++ b/groovy/src/test/groovy/MinMaxTest.groovy
@@ -0,0 +1,52 @@
+package groovy
+
+/** 
+ * Tests the min() and max() functions
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class MinMaxTest extends GroovyTestCase {
+
+    void testSimpleMinMax() {
+        def list = [5, 2, 6, 1, 9, 8]
+        
+        def n = list.min()
+        assert n == 1
+        
+        n = list.max()
+        assert n == 9
+    }
+    
+    void testMinMaxWithComparator() {
+        def people = getPeople()
+
+        // lets find the maximum by name
+
+        def order = new OrderBy( { it.get('@cheese') } )
+
+        println("People ${people}")
+
+        def p = people.min(order)
+
+        println("Found ${p}")
+
+        assert p.get("@name") == "Joe" , "found person ${p}"
+
+        p = people.max(order)
+        assert p.get("@name") == "Chris" , "found person ${p}"
+    }
+    
+    def getPeople() {
+        def builder = new NodeBuilder()
+        def tree = builder.people {
+            person(name:'James', cheese:'Edam', location:'London')
+            person(name:'Bob', cheese:'Cheddar', location:'Atlanta')
+            person(name:'Chris', cheese:'Red Leicester', location:'London')
+            person(name:'Joe', cheese:'Brie', location:'London')
+        }
+        
+        return tree.children()
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/MinusEqualsTest.groovy b/groovy/src/test/groovy/MinusEqualsTest.groovy
new file mode 100644
index 0000000..fab6263
--- /dev/null
+++ b/groovy/src/test/groovy/MinusEqualsTest.groovy
@@ -0,0 +1,47 @@
+package groovy
+
+class MinusEqualsTest extends GroovyTestCase {
+
+    void testIntegerMinusEquals() {
+        def x = 4
+        def y = 2
+        x -= y
+        
+        assert x == 2
+        
+        y -= 1
+        
+        assert y == 1
+    }
+
+    void testCharacterMinusEquals() {
+        Character x = 4
+        Character y = 2
+        x -= y
+        
+        assert x == 2
+        
+        y -= 1
+        
+        assert y == 1
+    }
+    
+    void testNumberMinusEquals() {
+        def x = 4.2
+        def y = 2
+        x -= y
+        
+        assert x == 2.2
+        
+        y -= 0.1
+        
+        assert y == 1.9
+    }
+    
+    void testStringMinusEquals() {
+        def foo = "nice cheese"
+        foo -= "cheese"
+        
+        assert foo == "nice "
+    }
+}
diff --git a/groovy/src/test/groovy/ModuloTest.groovy b/groovy/src/test/groovy/ModuloTest.groovy
new file mode 100644
index 0000000..300b652
--- /dev/null
+++ b/groovy/src/test/groovy/ModuloTest.groovy
@@ -0,0 +1,24 @@
+package groovy
+
+class ModuloTest extends GroovyTestCase {
+  int modulo = 100
+
+  void testModuloLesser() {
+    for (i in 0..modulo-1) {
+      assert (i%modulo)==i
+    }
+  }
+
+  void testModuloEqual() {
+    for (i in 0..modulo) {
+      assert ((i*modulo) % modulo)==0
+    }
+  }
+
+  void testModuloBigger() {
+    for (i in 0..modulo-1) {
+      assert ((i*modulo+i) % modulo)==i
+    }
+  }
+
+}
diff --git a/groovy/src/test/groovy/MultiDimArraysTest.groovy b/groovy/src/test/groovy/MultiDimArraysTest.groovy
new file mode 100644
index 0000000..2f50420
--- /dev/null
+++ b/groovy/src/test/groovy/MultiDimArraysTest.groovy
@@ -0,0 +1,61 @@
+/**
+ * Expose how to deal with multi-dimensional Arrays until this is supported at the language level.
+ * @author Dierk Koenig
+ * @author Jochen Theodorou
+ */
+ 
+package groovy;
+
+public class MultiDimArraysTest extends GroovyTestCase {
+
+    // todo: enable as soon as multi dims are supported
+    void testCallTwoDimStringArray(){
+        def someArrayOfStringArrays =  new SomeClass().anArrayOfStringArrays()
+        assert 1 == someArrayOfStringArrays.size()
+    }
+    
+    void testCallTwoDimStringArrayWorkaround(){
+        def someArrayOfStringArrays =  new SomeClass().anArrayOfStringArraysWorkaround()
+        assert 1 == someArrayOfStringArrays.size()
+        assert "whatever" == someArrayOfStringArrays[0][0]
+        for (i in 0..<someArrayOfStringArrays.size()) {
+            assert someArrayOfStringArrays[i]
+        }
+    }
+
+    void testCallTwoDimStringArrayWorkaroundWithNull(){
+        def someArrayOfStringArrays =  new SomeClass().anArrayOfStringArraysWorkaround()
+        assert 1 == someArrayOfStringArrays.size()
+        assert "whatever" == someArrayOfStringArrays[0][0]
+        someArrayOfStringArrays.each(){ assert it}
+    }
+
+    void testInsideGroovyMultiDimReplacement(){
+        Object[] someArrayOfStringArrays = [["a","a","a"],["b","b","b",null]]
+        assert "a" == someArrayOfStringArrays[0][0]
+        someArrayOfStringArrays.each(){ assert it}
+    }
+    
+    void testMultiDimCreationWithSizes(){
+        Object[][] objectArray = new Object[2][5]
+        assert objectArray.length == 2
+        objectArray.each { 
+          assert it.length == 5 
+          it.each { assert it == null }
+        }
+    }
+    
+    void testMultiDimCreationWithoutSizeAtEnd() {
+       def array = new int[5][6][]
+       assert array.class.name == "[[[I"
+       assert array[0].class.name == "[[I"
+       assert array[0][0] == null
+    }
+    
+    void testMultiDimArrayForCustomClass() {
+		def ff = new MultiDimArraysTest[3][4]
+		assert "[[Lgroovy.MultiDimArraysTest;" == ff.class.name;
+    }
+
+}
+
diff --git a/groovy/src/test/groovy/MultilineChainExpressionTest.groovy b/groovy/src/test/groovy/MultilineChainExpressionTest.groovy
new file mode 100644
index 0000000..4e6ad5b
--- /dev/null
+++ b/groovy/src/test/groovy/MultilineChainExpressionTest.groovy
@@ -0,0 +1,18 @@
+package groovy
+
+class MultilineChainExpressionTest extends GroovyTestCase {
+   void testMultiLineChain() {
+       // the code below should be compileable
+       assert (
+	       System
+	           .out
+	           .class 
+ 	       == 
+	       PrintStream
+	           .class
+       )
+       assert System
+              .err
+              .class == PrintStream.class
+   }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/MultilineStringTest.groovy b/groovy/src/test/groovy/MultilineStringTest.groovy
new file mode 100644
index 0000000..692cc5d
--- /dev/null
+++ b/groovy/src/test/groovy/MultilineStringTest.groovy
@@ -0,0 +1,17 @@
+package groovy
+
+class MultilineStringTest extends GroovyTestCase {
+
+    void testMultilineString() {
+        def s = """abcd
+efg
+
+        hijk
+        
+"""
+        println(s)
+        assert s != null
+        def idx = s.indexOf("i")
+        assert idx > 0
+    }
+}
diff --git a/groovy/src/test/groovy/MultiplyDivideEqualsTest.groovy b/groovy/src/test/groovy/MultiplyDivideEqualsTest.groovy
new file mode 100644
index 0000000..bc1b603
--- /dev/null
+++ b/groovy/src/test/groovy/MultiplyDivideEqualsTest.groovy
@@ -0,0 +1,82 @@
+package groovy
+
+class MultiplyDivideEqualsTest extends GroovyTestCase {
+
+    void testIntegerMultiplyEquals() {
+        def x = 2
+        def y = 3
+        x *= y
+        
+        assert x == 6
+        
+        y *= 4
+        
+        assert y == 12
+    }
+
+    void testCharacterMultiplyEquals() {
+        Character x = 2
+        Character y = 3
+        x *= y
+        
+        assert x == 6
+        
+        y *= 4
+        
+        assert y == 12
+    }
+    
+    void testNumberMultiplyEquals() {
+        def x = 1.2
+        def y = 2
+        x *= y
+        
+        assert x == 2.4
+    }
+    
+    void testStringMultiplyEquals() {
+        def x = "bbc"
+        def y = 2
+        x *= y
+        
+        assert x == "bbcbbc"
+
+        x = "Guillaume"
+        y = 0
+        x *= y
+        assert x == ""
+    }
+    
+    
+    void testIntegerDivideEquals() {
+        def x = 18
+        def y = 6
+        x /= y
+        
+        assert x == 3.0
+        
+        y /= 3
+        
+        assert y == 2.0
+    }
+    
+    void testCharacterDivideEquals() {
+        Character x = 18
+        Character y = 6
+        x /= y
+        
+        assert x == 3.0
+        
+        y /= 3
+        
+        assert y == 2.0
+    }
+    
+    void testNumberDivideEquals() {
+        def x = 10.4
+        def y = 2
+        x /= y
+        
+        assert x == 5.2
+    }
+}
diff --git a/groovy/src/test/groovy/NamedParameterTest.groovy b/groovy/src/test/groovy/NamedParameterTest.groovy
new file mode 100644
index 0000000..355dc15
--- /dev/null
+++ b/groovy/src/test/groovy/NamedParameterTest.groovy
@@ -0,0 +1,15 @@
+package groovy
+
+class NamedParameterTest extends GroovyTestCase {
+
+    void testPassingNamedParametersToMethod() {
+        someMethod(name:"gromit", eating:"nice cheese", times:2)
+    }
+    
+    protected void someMethod(args) {
+        assert args.name == "gromit"
+        assert args.eating == "nice cheese"
+        assert args.times == 2
+        assert args.size() == 3
+    }
+}
diff --git a/groovy/src/test/groovy/NestedClassTest.groovy b/groovy/src/test/groovy/NestedClassTest.groovy
new file mode 100644
index 0000000..7c337b2
--- /dev/null
+++ b/groovy/src/test/groovy/NestedClassTest.groovy
@@ -0,0 +1,85 @@
+package groovy
+
+class NestedClassTest extends GroovyTestCase {
+
+    void testStaticInnerStaticMethod () {
+        def script = new GroovyClassLoader(getClass().getClassLoader()).parseClass ("""
+        package groovy
+
+        JavaClass.StaticInner.it
+        """).newInstance()
+        assertEquals 30, script.run()
+    }
+
+    void testStaticInnerInstanceMethod () {
+        def script = new GroovyClassLoader(getClass().getClassLoader()).parseClass ("""
+        package groovy
+
+        new JavaClass.StaticInner ().result
+        """).newInstance()
+        assertEquals 239, script.run()
+    }
+
+    void testParam () {
+        def script = new GroovyClassLoader(getClass().getClassLoader()).parseClass ("""
+        package groovy
+
+        def method (JavaClass.StaticInner obj) { obj.result }
+
+        method new JavaClass.StaticInner ()
+        """).newInstance()
+
+        assertEquals 239, script.run()
+    }
+
+    void testTypeDecl () {
+        def script = new GroovyClassLoader(getClass().getClassLoader()).parseClass ("""
+        package groovy
+
+        JavaClass.StaticInner method () { 239 }
+
+        method ()
+        """).newInstance()
+
+        shouldFail (org.codehaus.groovy.runtime.typehandling.GroovyCastException) {
+          assertEquals 239, script.run()
+        }
+    }
+
+    void testFieldDecl () {
+        def script = new GroovyClassLoader(getClass().getClassLoader()).parseClass ("""
+        package groovy
+
+        JavaClass.StaticInner field = 239
+
+        field
+        """).newInstance()
+
+        shouldFail (org.codehaus.groovy.runtime.typehandling.GroovyCastException) {
+          assertEquals 239, script.run()
+        }
+    }
+
+    void testInstanceof () {
+        def script = new GroovyClassLoader(getClass().getClassLoader()).parseClass ("""
+        package groovy
+
+        JavaClass.CONST instanceof JavaClass.StaticInner
+        """).newInstance()
+
+        assertTrue script.run ()
+    }
+
+    void testExtends () {
+        def script = new GroovyClassLoader(getClass().getClassLoader()).parseClass ("""
+        package groovy
+
+        class U extends JavaClass.StaticInner.Inner2 {}
+
+        new U ()
+
+        """).newInstance()
+
+        assert script.run () instanceof JavaClass.StaticInner.Inner2
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/NewExpressionTest.groovy b/groovy/src/test/groovy/NewExpressionTest.groovy
new file mode 100644
index 0000000..fe38b41
--- /dev/null
+++ b/groovy/src/test/groovy/NewExpressionTest.groovy
@@ -0,0 +1,56 @@
+package groovy
+
+import org.codehaus.groovy.runtime.DummyBean
+
+class NewExpressionTest extends GroovyTestCase {
+
+    void testNewInstance() {
+        def cheese = new String( "hey you hosers" )
+        
+        assert cheese != null
+        
+        println(cheese)
+    }
+
+    void testNewBeanNoArgs() {
+        def bean = new DummyBean()
+        assert bean.name == "James"
+        assert bean.i == 123
+    }
+
+    void testNewBean1Args() {
+        def bean = new DummyBean("Bob")
+        assert bean.name == "Bob"
+        assert bean.i == 123
+    }
+
+    void testNewBean2Args() {
+        def bean = new DummyBean("Bob", 1707)
+        assert bean.name == "Bob"
+        assert bean.i == 1707
+    }
+
+    void testNewInstanceWithFullyQualifiedName() {
+        def bean = new org.codehaus.groovy.runtime.DummyBean("Bob", 1707)
+        assert bean.name == "Bob"
+        assert bean.i == 1707
+    }
+
+    void testNewInstanceWithFullyQualifiedNameNotImported() {
+        def bean = new java.io.File("Foo")
+
+        println "Created $bean"
+
+        assert bean != null
+    }
+    
+    void testNewOnMultipleLines() {
+        def bean = 
+          new
+            File
+            ("Foo")
+
+        assert bean != null
+    }
+
+}
diff --git a/groovy/src/test/groovy/NoPackageTest.groovy b/groovy/src/test/groovy/NoPackageTest.groovy
new file mode 100644
index 0000000..eeb0a34
--- /dev/null
+++ b/groovy/src/test/groovy/NoPackageTest.groovy
@@ -0,0 +1,7 @@
+
+class NoPackageTest extends GroovyTestCase {
+
+    void testClassDef() {
+        assert getClass().name == "NoPackageTest"
+    }
+}
diff --git a/groovy/src/test/groovy/NullPropertyTest.groovy b/groovy/src/test/groovy/NullPropertyTest.groovy
new file mode 100644
index 0000000..d07e73a
--- /dev/null
+++ b/groovy/src/test/groovy/NullPropertyTest.groovy
@@ -0,0 +1,14 @@
+package groovy
+
+/**
+ * @author Jeremy Rayner 
+ */
+class NullPropertyTest extends GroovyTestCase { 
+    def wensleydale = null
+
+    void testNullProperty() { 
+        assert wensleydale == null 
+    } 
+} 
+
+
diff --git a/groovy/src/test/groovy/NumberMathTest.groovy b/groovy/src/test/groovy/NumberMathTest.groovy
new file mode 100644
index 0000000..1e861a3
--- /dev/null
+++ b/groovy/src/test/groovy/NumberMathTest.groovy
@@ -0,0 +1,140 @@
+package groovy
+
+/** 
+ * Basic NumberMath test.
+ * @see org.codehaus.groovy.runtime.NumberMath
+ */
+class NumberMathTest extends GroovyTestCase {
+
+    void testPromotions() {
+    	def C = '1'.toCharacter();
+    	def B = new Byte("1");
+    	def I = new Integer(1);
+    	def L = new Long(1);
+    	def F = new Float("1.0");
+    	def D = new Double("1.0");
+    	def BI = new BigInteger("1");
+    	def BD = new BigDecimal("1.0");
+    	
+    	//+, -, and * all promote the same way, so sample the matrix
+    	assert C+B instanceof Integer;
+    	assert C-BD instanceof BigDecimal;
+    	assert B+F instanceof Double;
+    	assert B+I instanceof Integer;
+    	
+    	assert I+I instanceof Integer;
+    	assert I-F instanceof Double;
+    	assert I*D instanceof Double;
+    	assert I+BI instanceof BigInteger;
+    	assert I-BD instanceof BigDecimal;
+    	
+    	assert F*L instanceof Double;
+    	assert D+L instanceof Double;
+    	assert BI-L instanceof BigInteger;
+    	assert BD*L instanceof BigDecimal;
+    	
+    	assert F+F instanceof Double;
+    	assert F-BI instanceof Double;
+    	assert F*BD instanceof Double;
+    	
+    	assert F+D instanceof Double;
+    	assert BI-D instanceof Double;
+    	assert BD*D instanceof Double;
+    	
+    	assert BI+BI instanceof BigInteger;
+    	assert BD-BI instanceof BigDecimal;
+    	assert BD*BD instanceof BigDecimal;
+    	
+    	//Division (/) promotes differently so change the expected results:
+    	assert I/I instanceof BigDecimal;
+    	assert I/F instanceof Double;
+    	assert I/D instanceof Double;
+    	assert I/BI instanceof BigDecimal;
+    	assert I/BD instanceof BigDecimal;
+    	
+    	assert F/L instanceof Double;
+    	assert D/L instanceof Double;
+    	assert BI/L instanceof BigDecimal;
+    	assert BD/L instanceof BigDecimal;
+    	
+    	assert F/F instanceof Double;
+    	assert F/BI instanceof Double;
+    	assert F/BD instanceof Double;
+    	
+    	assert F/D instanceof Double;
+    	assert BI/D instanceof Double;
+    	assert BD/D instanceof Double;
+    	
+    	assert BI/BI instanceof BigDecimal;
+    	assert BD/BI instanceof BigDecimal;
+    	assert BD/BD instanceof BigDecimal;
+    }
+    
+    void testOperations() {
+    	def I1 = new Integer(1);
+    	def I2 = new Integer(2);
+    	def I3 = new Integer(3);
+    	def L1 = new Long(1);
+    	def L2 = new Long(2);
+    	def L3 = new Long(3);
+    	def F1 = new Float("1.0");
+    	def F2 = new Float("2.0");
+    	def D1 = new Double("1.0");
+    	def D2 = new Double("2.0");
+    	def BI1 = new BigInteger("1");
+    	def BI2 = new BigInteger("2");
+    	def BD1 = new BigDecimal("1.0");
+    	def BD2 = new BigDecimal("2.0");
+    	def BD20 = new BigDecimal("2.00");
+
+    	
+    	assert I1/I2 instanceof BigDecimal;
+    	assert I1/I2 == new BigDecimal("0.5");
+
+    	assert I1.intdiv(I2) instanceof Integer;
+    	assert I1.intdiv(I2) == 0;
+
+    	assert I3.intdiv(I2) instanceof Integer;
+    	assert I3.intdiv(I2) == 1;
+    	
+    	assert L1.intdiv(I2) instanceof Long;
+    	assert L1.intdiv(I2) == 0;
+
+    	assert L3.intdiv(L2) instanceof Long;
+    	assert L3.intdiv(L2) == 1;
+    	
+    	assert BI1.intdiv(BI2) instanceof BigInteger;
+    	assert BI1.intdiv(BI2) == 0;
+    	
+    	assert I1/I3 instanceof BigDecimal;
+    	assert I1/I3 == new BigDecimal("0.3333333333");
+    	
+    	assert I2/I3 instanceof BigDecimal;
+    	assert I2/I3 == new BigDecimal("0.6666666667");
+    	    	
+    	assert I1/BD2 instanceof BigDecimal;
+    	
+    	//Test keeping max scale of (L, R or 10)
+    	def BBD1 = new BigDecimal("0.12345678901234567");
+    	assert BD1 + BBD1 == new BigDecimal("1.12345678901234567");
+
+    	def BBD2 = new BigDecimal(".000000000000000008");
+    	assert BBD1 + BBD2 == new BigDecimal("0.123456789012345678");
+	}
+	
+	void testUnsupportedIntDivision() {
+	   	try {
+    		1.0 .intdiv(3);
+    	} catch (UnsupportedOperationException uoe) {
+    		return
+    	}
+    	fail("Should catch an UnsupportedOperationException")
+    	
+	   	try {
+    		1.0G .intdiv(3);
+    	} catch (UnsupportedOperationException uoe) {
+    		return
+    	}
+    	fail("Should catch an UnsupportedOperationException")
+	} 
+}
diff --git a/groovy/src/test/groovy/OptionalReturnTest.groovy b/groovy/src/test/groovy/OptionalReturnTest.groovy
new file mode 100644
index 0000000..8d78ea1
--- /dev/null
+++ b/groovy/src/test/groovy/OptionalReturnTest.groovy
@@ -0,0 +1,105 @@
+package groovy
+
+class OptionalReturnTest extends GroovyTestCase {
+
+	def y
+	
+    void testSingleExpression() {
+        def value = foo()
+		
+        assert value == 'fooReturn'
+    }
+
+    void testLastExpressionIsSimple() {
+        def value = bar()
+        
+        assert value == 'barReturn'
+    }
+
+    void testLastExpressionIsBooleanExpression() {
+        def value = foo2()
+        
+        assert value
+
+        value = foo3()
+        
+        assert value == false
+    }
+
+    void testLastExpressionIsAssignment() {
+        def value = assign()
+        
+        assert value == 'assignReturn'
+        
+        value = assignField()
+        
+        assert value == 'assignFieldReturn'
+    }
+
+    void testLastExpressionIsMethodCall() {
+        def value = methodCall()
+        
+        assert value == 'fooReturn'
+    }
+
+    void testEmptyExpression() {
+        def value = nullReturn()
+        
+        assert value == null
+    }
+
+    //  now this is not a compile time error in jsr-03
+    void testVoidMethod() {
+        def value = voidMethod()
+
+        assert value == null
+    }
+
+    void testNonAssignmentLastExpressions() {
+        def value = lastIsAssert()
+        
+        assert value == null
+    }
+
+    def foo() {
+        'fooReturn'
+    }	
+	
+    def bar() {
+        def x = 'barReturn'
+        x
+    }
+	
+    def foo2() {
+        def x = 'cheese'
+        x == 'cheese'
+    }
+	
+    def foo3() {
+        def x = 'cheese'
+        x == 'edam'
+    }
+	
+    def assign() {
+        def x = 'assignReturn'
+    }
+	
+    def assignField() {
+        y = 'assignFieldReturn'
+    }
+    
+    def nullReturn() {
+    }
+
+    def lastIsAssert() {
+        assert 1 == 1
+    }
+
+    def methodCall() {
+        foo()
+    }
+    
+    void voidMethod() {
+        foo()
+    }
+}
diff --git a/groovy/src/test/groovy/OuterUser.java b/groovy/src/test/groovy/OuterUser.java
new file mode 100644
index 0000000..ba00a26
--- /dev/null
+++ b/groovy/src/test/groovy/OuterUser.java
@@ -0,0 +1,70 @@
+package groovy;
+
+/**
+ * <p>Sample class used for testing that groovy can call inner classes constructors.</p>
+ *
+ * @author Guillaume Laforge
+ * @cvs.revision $Revision$
+ */
+public class OuterUser {
+    private String name;
+    private Integer age;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public Integer getAge() {
+        return age;
+    }
+
+    public void setAge(Integer age) {
+        this.age = age;
+    }
+
+    public static class InnerAddress {
+        private String city;
+        private Integer zipcode;
+
+        public String getCity() {
+            return city;
+        }
+
+        public void setCity(String city) {
+            this.city = city;
+        }
+
+        public Integer getZipcode() {
+            return zipcode;
+        }
+
+        public void setZipcode(Integer zipcode) {
+            this.zipcode = zipcode;
+        }
+
+        public static class Street {
+            private String name;
+            private int number;
+
+            public String getName() {
+                return name;
+            }
+
+            public void setName(String name) {
+                this.name = name;
+            }
+
+            public int getNumber() {
+                return number;
+            }
+
+            public void setNumber(int number) {
+                this.number = number;
+            }
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/OverloadInvokeMethodTest.groovy b/groovy/src/test/groovy/OverloadInvokeMethodTest.groovy
new file mode 100644
index 0000000..29ac068
--- /dev/null
+++ b/groovy/src/test/groovy/OverloadInvokeMethodTest.groovy
@@ -0,0 +1,35 @@
+package groovy
+
+/**
+ * @version $Revision: 1.4 $
+ */
+class OverloadInvokeMethodTest extends GroovyTestCase {
+    
+    void testBug() {
+        def value = foo(123)
+        assert value == 246
+    }
+
+    /**
+     * Lets overload the invokeMethod() mechanism to provide an alias
+     * to an existing method
+     */
+    def invokeMethod(String name, Object args) {
+        try {
+            return metaClass.invokeMethod(this, name, args)
+        }
+        catch (MissingMethodException e) {
+            if (name == 'foo') {
+                return metaClass.invokeMethod(this, 'bar', args)
+            }
+            else {
+                throw e
+            }
+        }
+    }
+    
+    def bar(param) {
+        return param * 2
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/OverridePropertyGetterTest.groovy b/groovy/src/test/groovy/OverridePropertyGetterTest.groovy
new file mode 100644
index 0000000..8e0d100
--- /dev/null
+++ b/groovy/src/test/groovy/OverridePropertyGetterTest.groovy
@@ -0,0 +1,27 @@
+package groovy
+
+/**
+ * test to ensure that overriding getter doesn't throw a NPE on access
+ * 
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+class OverridePropertyGetterTest extends GroovyTestCase { 
+    def cheese
+       
+    void testSimpleMethodParameterAccess() { 
+        def o = new OverridePropertyGetterTest()
+        def p = new OverridePropertyGetterTest()
+        try {          
+            //@todo
+            //p.cheese = o.cheese
+        } catch (Exception e) {
+            fail(e.getMessage())
+        }
+    }
+    
+    public String getCheese() {
+        return cheese
+    }
+} 
diff --git a/groovy/src/test/groovy/PlusEqualsTest.groovy b/groovy/src/test/groovy/PlusEqualsTest.groovy
new file mode 100644
index 0000000..38a8f7f
--- /dev/null
+++ b/groovy/src/test/groovy/PlusEqualsTest.groovy
@@ -0,0 +1,53 @@
+package groovy
+
+class PlusEqualsTest extends GroovyTestCase {
+
+    void testIntegerPlusEquals() {
+        def x = 1
+        def y = 2
+        x += y
+        
+        assert x == 3
+        
+        y += 10
+        
+        assert y == 12
+    }
+
+    void testCharacterPlusEquals() {
+        Character x = 1
+        Character y = 2
+        x += y
+        
+        assert x == 3
+        
+        y += 10
+        
+        assert y == 12
+    }
+    
+    void testNumberPlusEquals() {
+        def x = 1.2
+        def y = 2
+        x += y
+        
+        assert x == 3.2
+        
+        y += 10.1
+        
+        assert y == 12.1
+    }
+    
+    void testStringPlusEquals() {
+        def x = "bbc"
+        def y = 2
+        x += y
+        
+        assert x == "bbc2"
+        
+        def foo = "nice cheese"
+        foo += " gromit"
+        
+        assert foo == "nice cheese gromit"
+    }
+}
diff --git a/groovy/src/test/groovy/PostfixTest.groovy b/groovy/src/test/groovy/PostfixTest.groovy
new file mode 100644
index 0000000..2c51777
--- /dev/null
+++ b/groovy/src/test/groovy/PostfixTest.groovy
@@ -0,0 +1,47 @@
+package groovy
+
+class PostfixTest extends GroovyTestCase {
+
+    void testIntegerPostfix() {
+        def x = 1
+        
+        def y = x++
+        
+        assert y == 1
+        assert x == 2
+        
+        assert x++ == 2
+        assert x == 3
+    }
+    
+    void testDoublePostfix() {
+        def x = 1.2
+        def y = x++
+
+        assert y == 1.2
+        assert x++ == 2.2
+        assert x == 3.2
+    }
+
+     void testStringPostfix() {
+        def x = "bbc"
+        x++
+        
+        assert x == "bbd"
+    }
+    
+    
+    void testArrayPostfix() {
+        int[] i = [1]
+        
+        def y = i[0]++
+        
+        assert y == 1
+        assert i[0]++ == 2
+        assert i[0] == 3
+    }
+    
+    void testConstantPostFix() {
+        assert 1 == 1++
+    }    
+}
diff --git a/groovy/src/test/groovy/PrefixTest.groovy b/groovy/src/test/groovy/PrefixTest.groovy
new file mode 100644
index 0000000..1c304d6
--- /dev/null
+++ b/groovy/src/test/groovy/PrefixTest.groovy
@@ -0,0 +1,52 @@
+package groovy
+
+class PrefixTest extends GroovyTestCase {
+
+    void testIntegerPrefix() {
+        def x = 1
+        
+        def y = ++x
+        
+        assert y == 2
+        assert x == 2
+        
+        assert ++x == 3
+    }
+    
+    void testDoublePrefix() {
+        def x = 1.2
+        def y = ++x
+        
+        assert y == 2.2
+        assert x == 2.2
+        assert ++x == 3.2
+        assert x == 3.2
+    }
+
+    void testStringPrefix() {
+        def x = "bbc"
+        ++x
+        
+        assert x == "bbd"
+        
+        --x
+        --x
+        
+        assert x == "bbb"
+    }
+    
+    void testArrayPrefix() {
+        int[] i = [1]
+        
+        ++i[0]
+        assert i[0] == 2
+        
+        --i[0]
+        --i[0]
+        assert i[0] == 0
+    }
+    
+    void testConstantPostFix() {
+        assert 2 == ++1
+    }  
+}
diff --git a/groovy/src/test/groovy/PrimitiveArraysTest.groovy b/groovy/src/test/groovy/PrimitiveArraysTest.groovy
new file mode 100644
index 0000000..918622a
--- /dev/null
+++ b/groovy/src/test/groovy/PrimitiveArraysTest.groovy
@@ -0,0 +1,123 @@
+package groovy
+
+class PrimitiveArraysTest extends GroovyTestCase {
+
+    def c1Field = [] as char[]
+    char[] c2Field = [] as char[]
+    
+    def i1Field = [] as int[]
+    int[] i2Field = [] as int[]
+
+    def d1Field = [] as double[]
+    double[] d2Field = [] as double[]
+
+    def f1Field = [] as float[]
+    float[] f2Field = [] as float[]
+    
+    def l1Field = [] as long[]
+    long[] l2Field = [] as long[]    
+
+    def b1Field = [] as byte[]
+    byte[] b2Field = [] as byte[]
+    
+    def s1Field = [] as short[]
+    short[] s2Field = [] as short[]
+    
+    void testChar() {
+        assert c1Field.class == c2Field.class
+        def ca = ['l','l'] as char[]
+        char[] cb = ['l','l']
+        assert ca.class == cb.class
+        assert c1Field.class == ca.class
+        assert ca.class.name == "[C"
+        
+        ca.each{ assert it=='l' }
+        cb.each{ assert it=='l' }
+    }
+
+
+    void testInt() {
+        assert i1Field.class == i2Field.class
+        def ia = [1,1] as int[]
+        int[] ib = [1,1]
+        assert ia.class == ib.class
+        assert i1Field.class == ia.class
+        assert ia.class.name == "[I"
+        
+        ia.each{ assert it==1 }
+        ib.each{ assert it==1 }
+    }
+
+
+    void testLong() {
+        assert l1Field.class == l2Field.class
+        def la = [1,1] as long[]
+        long[] lb = [1,1]
+        assert la.class == lb.class
+        assert l1Field.class == la.class
+        assert la.class.name == "[J"
+        
+        la.each{ assert it==1l }
+        lb.each{ assert it==1l }
+    }
+
+
+    void testShort() {
+        assert s1Field.class == s2Field.class
+        def sa = [1,1] as short[]
+        short[] sb = [1,1]
+        assert sa.class == sb.class
+        assert s1Field.class == sa.class
+        assert sa.class.name == "[S"
+        
+        sa.each{ assert it==1 }
+        sb.each{ assert it==1 }
+    }
+
+    void testByte() {
+        assert b1Field.class == b2Field.class
+        def ba = [1,1] as byte[]
+        byte[] bb = [1,1]
+        assert ba.class == bb.class
+        assert b1Field.class == ba.class
+        assert ba.class.name == "[B"
+        
+        ba.each{ assert it==1 }
+        bb.each{ assert it==1 }
+    }
+    
+    
+    void testDouble() {
+        assert d1Field.class == d2Field.class
+        def da = [1,1] as double[]
+        double[] db = [1,1]
+        assert da.class == db.class
+        assert d1Field.class == da.class
+        assert da.class.name == "[D"
+        
+        da.each{ assert it==1.0d }
+        db.each{ assert it==1.0d }
+    }
+
+    void testFloat() {
+        assert f1Field.class == f2Field.class
+        def fa = [1,1] as float[]
+        float[] fb = [1,1]
+        assert fa.class == fb.class
+        assert f1Field.class == fa.class
+        assert fa.class.name == "[F"
+        
+        fa.each{ assert it==1.0f }
+        fb.each{ assert it==1.0f }
+    }
+    
+    void testBoolean() {
+      def ba = new boolean[1][2][3]
+      assert ba[0].length == 2
+      assert ba[0][0].length == 3
+      ba = [true,true] as boolean[]
+      ba.each { assert it==true }
+      assert ba.class.name == "[Z"
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/PrimitiveDefaultValueTest.groovy b/groovy/src/test/groovy/PrimitiveDefaultValueTest.groovy
new file mode 100644
index 0000000..10e3d55
--- /dev/null
+++ b/groovy/src/test/groovy/PrimitiveDefaultValueTest.groovy
@@ -0,0 +1,91 @@
+package groovy
+
+/**
+ * @TODO: GROOVY-1037
+ *
+ *    $Revision 1.0
+ *    Test for non-initialized fields or variables of the primitive types.
+ *
+ * @author Pilho Kim
+ */
+
+class PrimitiveDefaultValueTest extends GroovyTestCase {
+
+    private int x
+    private long y
+    private double z
+    private byte b
+    private short s
+    private float f
+    private boolean flag
+    private char c
+
+    void testThisPrimitiveDefaultValues() {
+        this.x == 0
+        this.y == 0L
+        this.z == 0.0
+        this.b == (byte) 0
+        this.s == (short) 0
+        this.f == 0.0F
+        this.flag == false
+        this.c == (char) 0
+    }
+
+    void testPrimitiveDefaultValues() {
+        def a = new ClassForPrimitiveDefaultValue()
+        a.x == 0
+        a.y == 0L
+        a.z == 0.0
+        a.b == (byte) 0
+        a.s == (short) 0
+        a.f == 0.0F
+        a.flag == false
+        a.c == (char) 0
+    }
+
+    void testDefaultPrimitiveValuesForAttributes() {
+        def a = new ClassForPrimitiveDefaultValue()
+        a.@x == 0
+        a.@y == 0L
+        a.@z == 0.0
+        a.@b == (byte) 0
+        a.@s == (short) 0
+        a.@f == 0.0F
+        a.@flag == false
+        a.@c == (char) 0
+    }
+
+    void testDefaultPrimitiveValuesForProperties() {
+        def a = new ClassForPrimitiveDefaultValue()
+        a.x1 == 0
+        a.y1 == 0L
+        a.z1 == 0.0
+        a.b1 == (byte) 0
+        a.s1 == (short) 0
+        a.f1 == 0.0F
+        a.flag1 == false
+        a.c1 == (char) 0
+    }
+}
+
+class ClassForPrimitiveDefaultValue {
+    int x
+    long y
+    double z
+    byte b
+    short s
+    float f
+    boolean flag
+    char c
+
+    int x1
+    long y1
+    double z1
+    byte b1
+    short s1
+    float f1
+    boolean flag1
+    char c1
+}
+
+
diff --git a/groovy/src/test/groovy/PrimitiveTypeFieldTest.groovy b/groovy/src/test/groovy/PrimitiveTypeFieldTest.groovy
new file mode 100644
index 0000000..731c6fe
--- /dev/null
+++ b/groovy/src/test/groovy/PrimitiveTypeFieldTest.groovy
@@ -0,0 +1,59 @@
+package groovy
+
+class PrimitiveTypeFieldTest extends GroovyTestCase {
+    private long longField
+    private static short shortField
+
+    void setValue() {
+        longField = 1
+    }
+
+    def getValue() {
+        def x = longField
+        return x
+    }
+
+    void testPrimitiveField() {
+        setValue()
+
+        def value = getValue()
+        assert value == 1
+
+        assert longField == 1
+    }
+
+    void testIntParamBug() {
+        assert bugMethod(123) == 246
+        assert bugMethod2(123) == 246
+
+        // @todo GROOVY-133
+        def closure = {int x-> x * 2 }
+        assert closure.call(123) == 246
+
+    }
+
+    int bugMethod(int x) {
+        x * 2
+    }
+
+    def bugMethod2(int x) {
+        x * 2
+    }
+    void testStaticPrimitiveField() {
+        shortField = (Short) 123
+
+        assert shortField == 123
+    }
+
+    void testIntLocalVariable() {
+        int x = 123
+        def y = x + 1
+        assert y == 124
+    }
+
+    void testLongLocalVariable() {
+        long x = 123
+        def y = x + 1
+        assert y == 124
+    }
+}
diff --git a/groovy/src/test/groovy/PrimitiveTypesTest.groovy b/groovy/src/test/groovy/PrimitiveTypesTest.groovy
new file mode 100644
index 0000000..cd14027
--- /dev/null
+++ b/groovy/src/test/groovy/PrimitiveTypesTest.groovy
@@ -0,0 +1,67 @@
+package groovy
+
+class PrimitiveTypesTest extends GroovyTestCase {
+
+	int getInt() {
+		return 1;
+	}
+	
+	short getShort() {
+		return 1;
+	}
+	
+	boolean getBoolean() {
+		return true;
+	}
+	
+	double getDouble() {
+		return 1.0;
+	}
+	
+	float getFloat() {
+		return 1.0;
+	}
+	
+	byte getByte() {
+		return 1;
+	}
+	
+	long getLong() {
+		return 1;
+	}
+
+	char getChar() {
+		return 'a';
+	}
+	
+	int getNextInt(int i) {
+		return i + 1
+	}
+	
+	short getNextShort(short i) {
+		return i + 1
+	}
+	
+	void testPrimitiveTypes() {
+		assert 1 == getInt()
+		assert 1 == getShort()
+		assert 1 == getByte()
+		assert 1 == getLong()
+		assert getBoolean()
+		assert getDouble() > 0.99
+		assert getFloat() > 0.99
+		assert 'a' == getChar()
+	}
+
+	void testPrimitiveParameters() {		
+		assert getNextInt(1) == 2
+		assert 3 == getNextInt(2)
+		
+		assert getNextShort((Short) 1) == 2
+		assert 3 == getNextShort((Short) 2)
+	}
+		
+	static void main(args) {
+		new PrimitiveTypesTest().testPrimitiveTypes()
+	}
+}
diff --git a/groovy/src/test/groovy/PrintTest.groovy b/groovy/src/test/groovy/PrintTest.groovy
new file mode 100644
index 0000000..f30c5b2
--- /dev/null
+++ b/groovy/src/test/groovy/PrintTest.groovy
@@ -0,0 +1,73 @@
+package groovy
+
+import java.text.NumberFormat
+
+class PrintTest extends GroovyTestCase {
+
+    void testToString() {
+        assertToString("hello", 'hello')
+
+        assertToString([], "[]")
+        assertToString([1, 2, "hello"], '[1, 2, hello]')
+
+        // TODO: change toString on Map to produce same as inspect method
+        assertToString([1:20, 2:40, 3:'cheese'], '{1=20, 2=40, 3=cheese}')
+        assertToString([:], "{}")
+
+        // TODO: change toString on Map to produce same as inspect method
+        assertToString([['bob':'drools', 'james':'geronimo']], '[{bob=drools, james=geronimo}]')
+        // TODO: change toString on Map to produce same as inspect method
+        assertToString([5, ["bob", "james"], ["bob":"drools", "james":"geronimo"], "cheese"], '[5, [bob, james], {bob=drools, james=geronimo}, cheese]')
+    }
+
+    void testInspect() {
+        assertInspect("hello", '"hello"')
+        
+        assertInspect([], "[]")
+        assertInspect([1, 2, "hello"], '[1, 2, "hello"]')
+        
+        assertInspect([1:20, 2:40, 3:'cheese'], '[1:20, 2:40, 3:"cheese"]')
+        assertInspect([:], "[:]")
+
+       assertInspect([['bob':'drools', 'james':'geronimo']], '[["bob":"drools", "james":"geronimo"]]')
+       assertInspect([5, ["bob", "james"], ["bob":"drools", "james":"geronimo"], "cheese"], '[5, ["bob", "james"], ["bob":"drools", "james":"geronimo"], "cheese"]')
+    }
+
+    void testCPlusPlusStylePrinting() {
+        def endl = "\n"
+        System.out << "Hello world!" << endl
+    }
+
+    void testSprintf() {
+        if (System.properties.'java.version'[2] >= '5') {
+            // would be nice to use JDK 1.6 DecimalFormatSymbols
+            def decimalSymbol = NumberFormat.instance.format(1.5) - '1' - '5'
+            assert sprintf('%5.2f', 12 * 3.5) == "42${decimalSymbol}00"
+            assert sprintf('%d + %d = %d' , [1, 2, 1+2] as Integer[]) == '1 + 2 = 3'
+            assert sprintf('%d + %d = %d' , [2, 3, 2+3] as int[]) == '2 + 3 = 5'
+            assert sprintf('%d + %d = %d' , [3, 4, 3+4] as long[]) == '3 + 4 = 7'
+            assert sprintf('%d + %d = %d' , [4, 5, 4+5] as byte[]) == '4 + 5 = 9'
+            assert sprintf('%d + %d = %d' , [5, 6, 5+6] as short[]) == '5 + 6 = 11'
+            def floatExpr = sprintf('%5.2f + %5.2f = %5.2f' , [3, 4, 3+4] as float[])
+            assertEquals " 3${decimalSymbol}00 +  4${decimalSymbol}00 =  7${decimalSymbol}00", floatExpr
+            def doubleExpr = sprintf('%5.2g + %5.2g = %5.2g' , [3, 4, 3+4] as double[])
+            // TODO: work out why decimalSymbol is not used here (at least for FR and RU)
+            assertEquals "  3.0 +   4.0 =   7.0", doubleExpr
+            assert sprintf('hi %s' , 'there') == 'hi there'
+            assert sprintf('%c' , 0x41) == 'A'
+            assert sprintf('%x' , 0x41) == '41'
+            assert sprintf('%o' , 0x41) == '101'
+            assert sprintf('%h' , 0x41) == '41'
+            assert sprintf('%b %b' , [true, false] as boolean[]) == 'true false'
+        }
+    }
+    
+    void testSprintfExceptionPropagation() {
+	   if (System.properties.'java.version'[2] >= '5') {
+	       
+	       shouldFail(java.util.IllegalFormatConversionException){
+		       sprintf('%2.4f', {3})
+	       }
+	   }
+    }
+}
diff --git a/groovy/src/test/groovy/PrivateVariableAccessFromAnotherInstanceTest.groovy b/groovy/src/test/groovy/PrivateVariableAccessFromAnotherInstanceTest.groovy
new file mode 100644
index 0000000..760876d
--- /dev/null
+++ b/groovy/src/test/groovy/PrivateVariableAccessFromAnotherInstanceTest.groovy
@@ -0,0 +1,36 @@
+package groovy
+
+/**
+ * test to ensure that private instance variables are visible to 
+ * other instance variables of the same class
+ * 
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+class PrivateVariableAccessFromAnotherInstanceTest extends GroovyTestCase implements Cloneable { 
+    def foo
+    private def bar
+              
+    public PrivateVariableAccessFromAnotherInstanceTest() {
+        super()
+        foo = "foo"
+        bar = "bar"
+    }
+              
+    public Object clone() {
+        def result = new PrivateVariableAccessFromAnotherInstanceTest()
+        result.foo = foo
+        result.bar = bar
+        return result
+    }
+    
+    void testClone() {
+        def fred = new PrivateVariableAccessFromAnotherInstanceTest()
+        //@todo fails due to private access to 'bar'
+        //barney = fred.clone()
+
+        // TODO identity comparison
+        //assert !(barney === fred)
+    }
+} 
diff --git a/groovy/src/test/groovy/ProcessTest.groovy b/groovy/src/test/groovy/ProcessTest.groovy
new file mode 100644
index 0000000..cfc1e45
--- /dev/null
+++ b/groovy/src/test/groovy/ProcessTest.groovy
@@ -0,0 +1,95 @@
+package groovy
+
+/**
+ * check that groovy Process methods do their job.
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+class ProcessTest extends GroovyTestCase {
+    def myProcess
+    
+    void setUp() {
+        myProcess = new MockProcess()
+    }
+    
+    void testProcessAppendBytes() {
+        def myBytes = "mooky".getBytes()
+                  
+        myProcess << myBytes
+                  
+        def result = myProcess.outputStream.toByteArray()
+        assert result != null
+        assert Arrays.equals(myBytes,result)
+    }
+    void testProcessAppendTwoByteArrays() {
+        def myBytes1 = "foo".getBytes()
+        def myBytes2 = "bar".getBytes()
+                  
+        myProcess << myBytes1 << myBytes2
+                  
+        def result = myProcess.outputStream.toByteArray()
+        assert result != null
+        assert result.size() == myBytes1.size() + myBytes2.size()          
+    }
+    
+    void testProcessAppend() {
+        myProcess << "mooky"
+        assert "mooky" == myProcess.outputStream.toString()
+    }
+    
+    void testProcessInputStream() {
+        assert myProcess.in instanceof InputStream
+        assert myProcess.in != null
+    }
+    
+    void testProcessText() {
+        assert "" == myProcess.text
+    }
+    
+    void testProcessErrorStream() {
+        assert myProcess.err instanceof InputStream
+        assert myProcess.err != null
+    }
+    
+    void testProcessOutputStream() {
+        assert myProcess.out instanceof OutputStream
+        assert myProcess.out != null
+    }
+    
+    // @todo - ps.waitForOrKill(secs) creates it's own thread, leave this out of test suite for now...
+    
+    void tearDown() {
+        myProcess.destroy()
+    }
+}
+
+/**
+ * simple Process, used purely for test cases
+ */
+class MockProcess extends Process {
+    private def e
+    private def i
+    private def o
+    
+    public MockProcess() {
+        e = new AnotherMockInputStream()
+        i = new AnotherMockInputStream()
+        o = new ByteArrayOutputStream()
+    }
+    void destroy() {}
+    int exitValue() { return 0 }
+    InputStream getErrorStream() { return e }
+    public InputStream getInputStream() { return i }
+    public OutputStream getOutputStream() { return o }
+    int waitFor() { return 0 }
+}
+
+/**
+ * only needed for workaround in groovy, 
+ *     new ByteArrayInputStream(myByteArray) doesn't work at mo... (28-Sep-2004)
+ */
+class AnotherMockInputStream extends InputStream {
+    int read() { return -1 }
+}
diff --git a/groovy/src/test/groovy/Property2Test.groovy b/groovy/src/test/groovy/Property2Test.groovy
new file mode 100644
index 0000000..f356127
--- /dev/null
+++ b/groovy/src/test/groovy/Property2Test.groovy
@@ -0,0 +1,89 @@
+package groovy
+
+/** 
+ * was: Tests the use of new def methods in Groovy: eachProperty(), eachPropertyName(), and
+ * allProperties().
+ * New Method: getMetaPropertyValues
+ * Method name has changed: getProperties
+ * Remove: eachProperty(), eachPropertyName() use properties.each {key,value -> } instead
+ *
+ * @author john stump
+ * @author dierk koenig
+ * @version $Revision$
+ */
+class Property2Test extends GroovyTestCase {
+
+    void testEachPropertyName() {
+        def foo = new Foo()
+		
+		// these are the properties that should be there
+		def props = ['name', 'count', 'location', 'blah']
+		foo.properties.each { name, value ->
+			//println "looking for ${prop} in ${props}"
+
+			// todo: GROOVY-996
+                                    // We should see protected properties, but not  private ones.
+			assert name != "invisible"
+
+			// remove this one from the list
+			props = props - [name]
+		}
+
+		// make sure there are none left over
+		//println "count left in props list is ${props.count()}"
+		assert props.count() == 0
+    }
+
+    void testMetaPropertyValuesFromObject() {
+        def foo = new Foo()
+		def metaProps = foo.metaPropertyValues
+		assert metaProps[0] instanceof PropertyValue
+		assertNotNull metaProps[0].name
+		assertNotNull metaProps[0].value
+		assertNotNull metaProps[0].type
+    }
+
+	void testEachProperty() {
+        def foo = new Foo()
+
+		// these are the properties and their values that should be there
+		def props = ['name':'James', 'count':1, 'location':'London', 'blah':9]
+		foo.properties.each { name, value ->
+			//println "looking for ${prop.name} in ${props}"
+			
+			// todo: GROOVY-996
+                                    // We should see protected properties, but not  private ones.
+			assert name != "invisible"
+			
+			def pvalue = props[name]
+			if(pvalue != null)
+				assert pvalue == value
+			
+			// remove this one from the map
+			props.remove(name)
+		}
+		
+		// make sure there are none left over
+		//println "count left in props map is ${props.size()}"
+		assert props.size() == 0
+	}
+	
+	// make sure allProperties() works with expando objects too
+    void testAllPropertiesExpando() {
+        def foo = new Expando()
+		
+		foo.name = 'John'
+		foo.location = 'Colorado'
+		foo.count = 23
+		foo.blah = true
+		
+		// these are the properties that should be there
+		def props = ['name', 'count', 'location', 'blah']
+		foo.properties.each { name, value -> props -= [name] }
+		
+		// there should be none left
+		//println props
+		assert props.size() == 0
+    }
+}
+
diff --git a/groovy/src/test/groovy/PropertyTest.groovy b/groovy/src/test/groovy/PropertyTest.groovy
new file mode 100644
index 0000000..0f234d1
--- /dev/null
+++ b/groovy/src/test/groovy/PropertyTest.groovy
@@ -0,0 +1,198 @@
+package groovy
+
+/** 
+ * Tests the use of properties in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class PropertyTest extends GroovyTestCase {
+
+    void testNormalPropertyGettersAndSetters() {
+        def foo = new Foo()
+        def value = foo.getMetaClass()
+
+        assert foo.name == "James"
+        assert foo.getName() == "James"
+        assert foo.location == "London"
+        assert foo.getLocation() == "London"
+        assert foo.blah == 9
+        assert foo.getBlah() == 9
+
+        foo.name = "Bob"
+        foo.location = "Atlanta"
+
+        assert foo.name == "Bob"
+        assert foo.getName() == "Bob"
+        assert foo.location == "Atlanta"
+        assert foo.getLocation() == "Atlanta"
+    }
+
+    // GROOVY-1809
+    void testClassWithPrivateFieldAndGetter() {
+        assert java.awt.Font.getName() == 'java.awt.Font'
+        assert java.awt.Font.name == 'java.awt.Font'
+    }
+
+    void testOverloadedGetter() {
+        def foo = new Foo()
+        assert foo.getCount() == 1
+        assert foo.count == 1
+        foo.count = 7
+        assert foo.count == 7
+        assert foo.getCount() == 7
+    }
+
+    void testNoSetterAvailableOnPrivateProperty() {
+        def foo = new Foo()
+
+        // methods should fail on non-existent method calls
+        //shouldFail { foo.blah = 4 }
+        shouldFail {foo.setBlah(4)}
+    }
+
+    void testCannotSeePrivateProperties() {
+        def foo = new Foo()
+
+        // def access fails on non-existent def
+        //shouldFail { def x = foo.invisible } //todo: correct handling of access rules
+
+        // methods should fail on non-existent method calls
+        shouldFail {foo.getQ()}
+    }
+
+    void testConstructorWithNamedProperties() {
+        def foo = new Foo(name: 'Gromit', location: 'Moon')
+
+        assert foo.name == 'Gromit'
+        assert foo.location == 'Moon'
+    }
+
+    void testToString() {
+        def foo = new Foo(name: 'Gromit', location: 'Moon')
+        assert foo.toString().endsWith('name: Gromit location: Moon')
+    }
+
+    void testArrayLengthProperty() {
+        // create two arrays, since all use the same MetaArrayLengthProperty object -
+        // make sure it can work for all types and sizes
+        def i = new Integer[5]
+        def s = new String[10]
+
+        // put something in it to make sure we're returning the *allocated* length, and
+        // not the *used* length
+        s[0] = "hello"
+
+        assert i.length == 5
+        assert s.length == 10
+
+        // this def does not mean there is a getLength() method
+        shouldFail {i.getLength()}
+
+        // verify we can't set this def, it's read-only
+        shouldFail {i.length = 6}
+    }
+
+    void testGstringAssignment() {
+        def foo = new Foo()
+        foo.body = "${foo.name}"
+        assert foo.body == "James"
+    }
+
+    void testFinalProperty() {
+        def shell = new GroovyShell();
+        assertScript """
+        class A {
+           final foo = 1
+        }
+        A.class.declaredMethods.each {
+          assert it.name!="setFoo"
+          
+        }
+        assert new A().foo==1
+      """
+        shouldFail {
+            shell.execute """
+          class A {
+            final foo = 1
+          }
+          new A().foo = 2
+        """
+        }
+    }
+
+    void testFinalPropertyWithInheritance() {
+        def child = new Child()
+        assert child.finalProperty == 1
+        child.finalProperty = 22
+        assert child.finalProperty == 1
+    }
+
+    void testBaseProperties() {
+        assert new Child().field == 'foobar'
+    }
+
+    // GROOVY-1736
+    void testGetSuperProperties() {
+        def c = new Child()
+        assert c.thing == 'bar thing'
+        assert c.superthing() == 'bar1foo thing'
+        assert c.x() == 'bar2foo x'
+        assert c.xprop == 'bar3foo x prop'
+        assert c.xpropViaMethod == 'bar4foo x prop'
+    }
+
+    void testSetSuperProperties() {
+        def c = new Child()
+        assert c.superField == 'bar'
+        c.setSuperField('baz1')
+        assert c.superField == 'baz1'
+        c.superField = 'baz2'
+        assert c.superField == 'baz2'
+
+        assert c.superthing() == 'bar1foo thing'
+        c.superThing = 'bar thing'
+        assert c.superthing() == 'bar1bar thing'
+    }
+}
+
+class Base {
+    protected String field = 'bar'
+
+    protected thing = 'foo thing'
+    def getXprop() {'foo x prop'}
+    def x() {'foo x'}
+    void setThing(value) {thing = value}
+    
+    //testing final property getter
+    final getFinalProperty() {1}
+}
+
+class Child extends Base {
+    protected String field = 'foo' + super.field
+    public getField() {field}
+    void setSuperField(value) {super.field = value}
+    public getSuperField() {super.field}
+
+    def thing = 'bar thing'
+    def superthing() {
+        'bar1' + super.thing
+    }
+    def x() {
+        'bar2' + super.x()
+    }
+    def getXprop() {
+        'bar3' + super.xprop
+    }
+    def getXpropViaMethod() {
+        'bar4' + super.getXprop()
+    }
+    def setSuperThing(value) {
+        super.thing = value
+    }
+    
+    // testing final property getter
+    // the following property should not add a new getter
+    // method, this would result in a verify error
+    def finalProperty = 32
+}
diff --git a/groovy/src/test/groovy/PropertyWithoutDotTest.groovy b/groovy/src/test/groovy/PropertyWithoutDotTest.groovy
new file mode 100644
index 0000000..c26b0d0
--- /dev/null
+++ b/groovy/src/test/groovy/PropertyWithoutDotTest.groovy
@@ -0,0 +1,13 @@
+package groovy
+
+class PropertyWithoutDotTest extends GroovyTestCase {
+    def getFoo() {
+        return "cheese"
+    }
+    
+    void testProperty() {
+        def value = foo
+        
+        assert value == "cheese"
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/RangeTest.groovy b/groovy/src/test/groovy/RangeTest.groovy
new file mode 100644
index 0000000..ba3bcc2
--- /dev/null
+++ b/groovy/src/test/groovy/RangeTest.groovy
@@ -0,0 +1,243 @@
+package groovy
+
+class RangeTest extends GroovyTestCase {
+	
+	void testRange() {
+	    def x = 0
+
+	    for ( i in 0..9 ) {
+	        x = x + i
+	    }
+
+	    assert x == 45
+	    
+	    x = 0
+
+	    for ( i in 0..<10 ) {
+	        x = x + i
+	    }
+
+	    assert x == 45
+	    
+	    x = 0
+
+	    for ( i in 0..'\u0009' ) {
+	        x = x + i
+	    }
+
+	    assert x == 45
+	}
+	
+	void testRangeEach() {
+	    def x = 0
+
+	    (0..9).each {
+	        x = x + it
+	    }
+
+	    assert x == 45
+	    
+	    x = 0
+
+	    (0..<10).each {
+	        x = x + it
+	    }
+
+	    assert x == 45
+	}
+
+	void testIntStep() {
+	    assertStep(0..9, 3, [0, 3, 6, 9])
+	    assertStep(0..<10, 3, [0, 3, 6, 9])
+	    
+	    assertStep(9..0, 3, [9, 6, 3, 0])
+	    assertStep(9..<0, 3, [9, 6, 3])
+	}
+	
+	void testObjectStep() {
+	    assertStep('a'..'f', 2, ['a', 'c', 'e'])
+	    assertStep('a'..<'e', 2, ['a', 'c'])
+	    
+	    assertStep('z'..'v', 2, ['z', 'x', 'v'])
+	    assertStep('z'..<'v', 2, ['z', 'x'])
+	}
+	
+	void testIterateIntRange() {
+	    assertIterate(0..9, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+	    assertIterate(1..<8, [1, 2, 3, 4, 5, 6, 7])
+	    assertIterate(7..1, [7, 6, 5, 4, 3, 2, 1])
+	    assertIterate(6..<1, [6, 5, 4, 3, 2])
+	}
+	
+	void testIterateObjectRange() {
+	    assertIterate('a'..'d', ['a', 'b', 'c', 'd'])
+	    assertIterate('a'..<'d', ['a', 'b', 'c'])
+	    assertIterate('z'..'x', ['z', 'y', 'x'])
+	    assertIterate('z'..<'x', ['z', 'y'])
+	}
+	
+	void testRangeContains() {
+	    def range = 0..10
+	    assert range.contains(0)
+	    assert range.contains(10)
+	    
+	    range = 0..<5
+	    assert range.contains(0)
+	    assert ! range.contains(5)
+	}
+	
+	void testBackwardsRangeContains() {
+	    def range = 10..0
+	    assert range.contains(0)
+	    assert range.contains(10)
+	    
+	    range = 5..<1
+	    assert range.contains(5)
+	    assert ! range.contains(1)
+	}
+	
+	void testObjectRangeContains() {
+	    def range = 'a'..'x'
+	    assert range.contains('a')
+	    assert range.contains('x')
+	    assert range.contains('z') == false
+	    
+	    range = 'b'..<'f'
+	    assert range.contains('b')
+	    assert ! range.contains('g')
+	    assert ! range.contains('f')
+	    assert ! range.contains('a')
+	}
+	
+	void testBackwardsObjectRangeContains() {
+	    def range = 'x'..'a'
+	    assert range.contains('a')
+	    assert range.contains('x')
+	    assert range.contains('z') == false
+	    
+	    range = 'f'..<'b'
+	    assert ! range.contains('g')
+	    assert range.contains('f')
+	    assert range.contains('c')
+	    assert ! range.contains('b')
+	}
+	
+	void testIntRangeToString() {
+	    assertToString(0..10, "0..10")
+	    assertToString([1, 4..10, 9], "[1, 4..10, 9]")
+	    
+	    assertToString(0..<11, "0..10")
+	    assertToString([1, 4..<11, 9], "[1, 4..10, 9]")
+	    
+	    
+	    assertToString(10..0, "10..0")
+	    assertToString([1, 10..4, 9], "[1, 10..4, 9]")
+	    
+	    assertToString(11..<0, "11..1")
+	    assertToString([1, 11..<4, 9], "[1, 11..5, 9]")
+	}
+	
+	void testObjectRangeToString() {
+	    assertToString('a'..'d', 'a..d', '"a".."d"')
+	    assertToString('a'..<'d', 'a..c', '"a".."c"')
+	    assertToString('z'..'x', 'z..x', '"z".."x"')
+	    assertToString('z'..<'x', 'z..y', '"z".."y"')
+	}
+	
+	void testRangeSize() {
+	    assertSize(1..10, 10)
+	    assertSize(11..<21, 10)
+	    assertSize(30..21, 10)
+	    assertSize(40..<30, 10)
+	}
+
+	void testBorderCases() {
+	    assertIterate(0..1, [0,1])
+	    assertIterate(0..0, [0])
+	    assertIterate(0..-1, [0,-1])
+	    assertIterate(0..<-1, [0])
+	}
+
+	void testEmptyRanges(){
+	    assertSize(0..<0, 0)
+	    assertSize(1..<1, 0)
+	    assertSize(-1..<-1, 0)
+	    assertSize('a'..<'a', 0)
+	    assertSize(0.0G..<0.0G, 0)
+	    (0..<0).each{assert false}
+	    (0..<0).step(1){assert false}
+	    for (i in 0..<0) assert false
+	    assertToString(0..<0, '0..<0', '0..<0')
+	    assertToString('a'..<'a', 'a..<a', '"a"..<"a"')
+	    assertToString(null..<null, 'null..<null', 'null..<null')
+	    assertStep(0..<0, 3, [])
+	    assertIterate(0..<0, [])
+	}
+	
+	void testStringRange() {
+	    def range = 'a'..'d'
+	    
+	    def list = []
+	    range.each { list << it }
+	    assert list == ['a', 'b', 'c', 'd']
+	    
+	    def s = range.size()
+	    assert s == 4
+	}
+	
+	void testBackwardsStringRange() {
+	    def range = 'd'..'a'
+	    
+	    def list = []
+	    range.each { list << it }
+	    assert list == ['d', 'c', 'b', 'a']
+	    
+	    def s = range.size()
+	    assert s == 4
+	}
+	
+	protected void assertIterate(range, expected) {
+	    def list = []
+	    for (it in range) {
+	        list << it
+	    }
+	    assert list == expected , "for loop on ${range}"
+	    
+		list = []
+	    range.each { list << it}
+	    assert list == expected , "each() on ${range}"
+	}
+	
+	protected void assertSize(range, expected) {
+	    def size = range.size()
+	    assert size == expected , range
+	}
+	
+	protected void assertToString(range, expected) {
+	    def text = range.toString()
+	    assert text == expected , "toString() for ${range}"
+	    text = range.inspect()
+	    assert text == expected , "inspect() for ${range}"
+	}
+	
+	protected void assertToString(range, expectedString, expectedInspect) {
+	    def text = range.toString()
+	    assert text == expectedString , "toString() for ${range}"
+	    text = range.inspect()
+	    assert text == expectedInspect , "inspect() for ${range}"
+	}
+	
+	protected void assertStep(range, stepValue, expected) {
+	    def list = []
+	    range.step(stepValue) {
+	        list << it
+	    }
+	    assert list == expected
+
+	    list = []
+	    for (it in range.step(stepValue)) {
+	        list << it
+	    }
+	    assert list == expected
+	}
+}
diff --git a/groovy/src/test/groovy/ReadLineTest.groovy b/groovy/src/test/groovy/ReadLineTest.groovy
new file mode 100644
index 0000000..0087321
--- /dev/null
+++ b/groovy/src/test/groovy/ReadLineTest.groovy
@@ -0,0 +1,70 @@
+// Do not remove this line: it is used in test below
+package groovy
+
+/**
+ * Test to ensure that readLine() method works on Reader/InputStream
+ * 
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @author Joachim Baumann
+ * @version $Revision$
+ */
+
+class ReadLineTest extends GroovyTestCase {
+    def file
+    void setUp() {
+        file = new File("src/test/groovy/ReadLineTest.groovy")
+    }
+    void testReadOneLineFromReader() {
+        def line
+        file.withReader() {line = it.readLine()}
+        assert line == "// Do not remove this line: it is used in test below"
+    }
+    
+    static testString = " ä\n ö\n\n ü\r\n 5\r\r 7\n\r 9"
+    static expectedLines = [ " ä", " ö", "", " ü", " 5", "", " 7", "", " 9" ]
+    static String[] expectedLinesSlow = [ " ä", " ö", " ü", " 5", " 7" ]
+    static int[] expectedChars = [' ', '9', -1];
+
+    void readFromReader(Reader reader) throws IOException {
+        expectedLines.each { expected ->
+            def line = reader.readLine()
+            assertEquals("Readline should return correct line", expected, line)
+        }
+        assertNull("Readline should return null", reader.readLine())
+    }
+
+    public void testBufferedReader() throws IOException {        
+        Reader reader = new BufferedReader(new StringReader(testString))
+        readFromReader(reader)
+    }
+
+    public void testReaderSupportingMark() throws IOException {        
+        Reader reader = new StringReader(testString)
+        readFromReader(reader)
+    }
+
+    /*
+     * In this case we cannot read more than one line separator
+     * Thus empty lines can be returned if line separation is \r\n.
+     */
+    public void testReaderSlow() throws IOException {        
+        Reader reader = new SlowStringReader(testString);
+        expectedLinesSlow.each { expected ->
+            String line = reader.readLine()
+            while(line != null && line.length() == 0) {
+                line = reader.readLine()
+            } 
+            assertEquals("Readline should return correct line", expected, line);    
+        }
+        assertEquals("Readline should return empty string", "", reader.readLine());
+
+        expectedChars.each { expected ->
+            assertEquals("Remaining characters incorrect", expected, reader.read())        
+        }
+        assertNull(reader.readLine());
+    }
+}
+class SlowStringReader extends StringReader {
+    public SlowStringReader(String s) { super(s); }
+    public boolean markSupported() { return false }
+}
diff --git a/groovy/src/test/groovy/RegExpGroupMatchTest.groovy b/groovy/src/test/groovy/RegExpGroupMatchTest.groovy
new file mode 100644
index 0000000..a3f3db0
--- /dev/null
+++ b/groovy/src/test/groovy/RegExpGroupMatchTest.groovy
@@ -0,0 +1,101 @@
+package groovy
+
+/**
+ * Test for fixing the Jira issue GROOVY-1000
+ *
+ *    Fix an infinite loop when getting after group matching in regular expression.
+ *    Graham Miller has given this idea.
+ *
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+
+import java.util.regex.Matcher
+import java.util.regex.Pattern
+
+class RegExpGroupMatchTest extends GroovyTestCase {
+
+    void testFirst() {
+        assert "cheesecheese" =~ "cheese"
+        assert "cheesecheese" =~ /cheese/
+        assert "cheese" == /cheese/   /*they are both string syntaxes*/
+    }
+
+    void testSecond() {
+        // Lets create a regex Pattern
+        def pattern = ~/foo/
+        assert pattern instanceof Pattern
+        assert pattern.matcher("foo").matches()
+    }
+
+    void testThird() {
+        // Let's create a Matcher
+        def matcher = "cheesecheese" =~ /cheese/
+        assert matcher instanceof Matcher
+        def answer = matcher.replaceAll("edam")
+        assert answer == "edamedam"
+    }
+
+    void testFourth() {
+        // Lets do some replacement
+        def cheese = ("cheesecheese" =~ /cheese/).replaceFirst("nice")
+        assert cheese == "nicecheese"
+    }
+
+    void testFifth() {
+        // Group demo
+        def matcher = "\$abc." =~ "\\\$(.*)\\."
+        matcher.matches();                   // must be invoked
+        assert matcher.group(1) == "abc"     // is one, not zero
+        // assert matcher[1] == "abc"     // This has worked only before jsr-03-release
+        println (matcher[0])
+        assert matcher[0] == ["\$abc.", "abc"]
+        assert matcher[0][1] == "abc"
+    }
+
+    void testSixth() {
+        // Group demo
+        // Avoid having to double all the backslash escaping characters.
+        def matcher = "\$abc." =~ /\$(.*)\./    // no need to double-escape!
+        assert "\\\$(.*)\\." == /\$(.*)\./
+        matcher.matches();                      // must be invoked
+        assert matcher.group(1) == "abc"        // is one, not zero
+        // assert matcher[1] == "abc"     // This has worked only before jsr-03-release
+        println (matcher[0])
+        assert matcher[0] == ["\$abc.", "abc"]
+        assert matcher[0][1] == "abc"
+    }
+
+    // Test no group match.
+    void testNoGroupMatcherAndGet() {
+        def p = /ab[d|f]/
+        def m = "abcabdabeabf" =~ p 
+
+        for (i in 0..<m.count) { 
+            println( "m.groupCount() = " + m.groupCount())
+            println( "  " + i + ": " + m[i] )   // m[i] is a String
+        }
+    }
+
+    // Test group matches.
+    void testGroupMatcherAndGet() {
+        def p = /(?:ab([c|d|e|f]))/
+        def m = "abcabdabeabf" =~ p 
+
+        for (i in 0..<m.count) { 
+            println( "m.groupCount() = " + m.groupCount())
+            println( "  " + i + ": " + m[i] )   // m[i] is a String
+        }
+    }
+
+    // Test group matches.
+    void testAnotherGroupMatcherAndGet() {
+        def m = "abcabdabeabfabxyzabx" =~ /(?:ab([d|x-z]+))/
+
+        m.count.times { 
+            println( "m.groupCount() = " + m.groupCount())
+            println( "  " + it + ": " + m[it] )   // m[it] is a String
+        }
+    }
+}
+
diff --git a/groovy/src/test/groovy/RegularExpressionsTest.groovy b/groovy/src/test/groovy/RegularExpressionsTest.groovy
new file mode 100644
index 0000000..ca7b19f
--- /dev/null
+++ b/groovy/src/test/groovy/RegularExpressionsTest.groovy
@@ -0,0 +1,122 @@
+package groovy
+
+/**
+ * Tests the regular expression syntax.
+ *
+ * @author Sam Pullara
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+ 
+import java.util.regex.Matcher
+import java.util.regex.Pattern
+
+class RegularExpressionsTest extends GroovyTestCase {
+
+     void testSubscript() {
+         def a = "cheesecheese"
+         def b = a =~ "e+"
+         def value = b[2]
+         assert value == "ee"
+
+         value = b[0, 2]
+
+         assert value == "eeee"
+
+         value = b[0, 1..2]
+
+         assert value == "eeeee"
+     }
+
+     void testFindRegex() {
+         assert "cheese" =~ "cheese"
+
+         def regex = "cheese"
+         def string = "cheese"
+         assert string =~ regex
+
+         def i = 0
+         def m = "cheesecheese" =~ "cheese"
+
+         assert m instanceof Matcher
+
+         while(m) { i = i + 1 }
+         assert i == 2
+
+         i = 0
+         m = "cheesecheese" =~ "e+"
+         while(m) { i = i + 1 }
+         assert i == 4
+
+         m.reset()
+         m.find()
+         m.find()
+         m.find()
+         assert m.group() == "ee"
+     }
+
+     void testMatchRegex() {
+         assert "cheese" ==~ "cheese"
+
+         assert !("cheesecheese" ==~ "cheese")
+
+     }
+
+     void testRegexEach() {
+         def i = 0
+         ("cheesecheese" =~ "cheese").each {value -> println(value); i = i + 1}
+         assert i == 2
+
+         i = 0
+         ("cheesecheese" =~ "ee+").each { println(it); i = i + 1}
+         assert i == 2
+     }
+
+     void testSimplePattern() {
+         def pattern = ~"foo"
+         assert pattern instanceof Pattern
+         assert pattern.matcher("foo").matches()
+         assert !pattern.matcher("bar").matches()
+     }
+
+     void testMultiLinePattern() {
+         def pattern = ~"""foo"""
+
+         assert pattern instanceof Pattern
+         assert pattern.matcher("foo").matches()
+         assert !pattern.matcher("bar").matches()
+     }
+
+     void testPatternInAssertion() {
+         assert "foofoofoo" =~ ~"foo"
+     }
+
+
+     void testMatcher() {
+         def matcher = "cheese-cheese" =~ "cheese"
+         def answer = matcher.replaceAll("edam")
+         assert answer == 'edam-edam'
+
+         def cheese = ("cheese cheese!" =~ "cheese").replaceFirst("nice")
+         assert cheese == "nice cheese!"
+     }
+
+    void testGetLastMatcher() {
+        assert "cheese" ==~ "cheese"
+        assert Matcher.getLastMatcher().matches()
+
+        switch("cheesefoo") {
+            case ~"cheesecheese":
+                assert false;
+            case ~"(cheese)(foo)":
+                def m = Matcher.getLastMatcher();
+                assert m.group(0) == "cheesefoo"
+                assert m.group(1) == "cheese"
+                assert m.group(2) == "foo"
+                assert m.groupCount() == 2
+                break;
+            default:
+                assert false
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/ReturnTest.groovy b/groovy/src/test/groovy/ReturnTest.groovy
new file mode 100644
index 0000000..9abc849
--- /dev/null
+++ b/groovy/src/test/groovy/ReturnTest.groovy
@@ -0,0 +1,70 @@
+package groovy
+
+/** 
+ * Tests the use of returns in Groovy
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class ReturnTest extends GroovyTestCase {
+    void testIntegerReturnValues() {
+        def value = foo(5)
+        assert value == 10
+    }
+
+    void testBooleanReturnValues() {
+        def value = bar(6)
+        assert value
+    }
+
+    def foo(x) {
+        return ( x * 2 )
+    }
+
+    def bar(x) {
+        return x > 5
+    }
+    
+    void testVoidReturn() {
+        explicitVoidReturn()
+        implicitVoidReturn()
+        explicitVoidReturnWithoutFinalReturn()
+        implicitVoidReturnWithoutFinalReturn()
+    }
+
+    void explicitVoidReturn() {
+        return
+    }
+
+    def implicitVoidReturn() {
+        return
+    }
+
+    void explicitVoidReturnWithoutFinalReturn() {
+        def x = 4;
+        if (x == 3) {
+            return;
+        } else {
+            try {
+                x = 3;
+                return;
+            } finally {
+                //do nothing
+            }
+        }
+    }
+
+    def implicitVoidReturnWithoutFinalReturn() {
+        def x = 4;
+        if (x == 3) {
+            return;
+        } else {
+            try {
+                x = 3;
+                return;
+            } finally {
+                //do nothing
+            }
+        }
+    } 
+}
diff --git a/groovy/src/test/groovy/SafeNavigationTest.groovy b/groovy/src/test/groovy/SafeNavigationTest.groovy
new file mode 100644
index 0000000..3cca530
--- /dev/null
+++ b/groovy/src/test/groovy/SafeNavigationTest.groovy
@@ -0,0 +1,52 @@
+package groovy
+
+class SafeNavigationTest extends GroovyTestCase {
+
+    void testNullNavigation() {
+        def x = null
+        def y = x?.bar
+
+        assert y == null
+    }
+
+    void testNormalPropertyNavigation() {
+        def x = ['a':456, 'foo':['bar':123, 'x':456], 'z':99]
+        
+        def y = x?.foo?.bar
+        
+        println("found y ${x?.foo?.bar}")
+        
+        assert y == 123
+    }
+
+    void testNullPropertyNavigation() {
+        def x = null
+        
+        def y = x?.foo?.bar
+        
+        assert y == null
+
+
+        def java.awt.Color color = null
+        def a = color?.alpha
+        assert a == null
+
+    }
+    
+    void testNormalMethodCall() {
+        def x = 1234
+        
+        def y = x?.toString()
+        
+        assert y == "1234"
+    }
+
+    void testNullMethodCall() {
+        def x = null
+        
+        def y = x?.toString()
+        
+        assert y == null
+    }
+
+}
diff --git a/groovy/src/test/groovy/SampleMain.groovy b/groovy/src/test/groovy/SampleMain.groovy
new file mode 100644
index 0000000..5a4f2c7
--- /dev/null
+++ b/groovy/src/test/groovy/SampleMain.groovy
@@ -0,0 +1,9 @@
+package groovy
+
+class SampleMain {
+    static void main(args) {
+        for (arg in args) {
+            println("Argument: " + arg)
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/SerializeTest.groovy b/groovy/src/test/groovy/SerializeTest.groovy
new file mode 100644
index 0000000..4cd8966
--- /dev/null
+++ b/groovy/src/test/groovy/SerializeTest.groovy
@@ -0,0 +1,38 @@
+package groovy
+
+class SerializeTest extends GroovyTestCase {
+
+    void testFoo() {
+        def foo = new Foo()
+        foo.name = "Gromit"
+        foo.location = "Moon"
+
+        def buffer = write(foo)
+        def object = read(buffer)
+
+        assert object != null
+        assert object.metaClass != null , "Should have a metaclass!"
+        assert object.name == "Gromit"
+        assert object.location == "Moon"
+        assert object.class.name == "groovy.Foo"
+        def fooClass = this.class.classLoader.systemClassLoader.loadClass('groovy.Foo')
+        assert foo.class == fooClass
+        assert object.class == fooClass
+    }
+    
+    def write(object) {
+        def buffer = new ByteArrayOutputStream()
+        def out = new ObjectOutputStream(buffer)
+        out << object
+        out.close()
+        return buffer.toByteArray()
+    }
+    
+    def read(buffer) {
+        def input = new ObjectInputStream(new ByteArrayInputStream(buffer))
+        def object = input.readObject()
+        input.close()
+        return object
+    }
+
+}
diff --git a/groovy/src/test/groovy/ShellTest.groovy b/groovy/src/test/groovy/ShellTest.groovy
new file mode 100644
index 0000000..3f727d7
--- /dev/null
+++ b/groovy/src/test/groovy/ShellTest.groovy
@@ -0,0 +1,35 @@
+package groovy
+
+class ShellTest extends GroovyTestCase {
+
+    void testReadAndWriteVariable() {
+        def shell = new GroovyShell()
+        
+        shell.foo = 1
+        
+        def value = shell.evaluate("""
+println('foo is currently ' + foo)
+foo = 2 
+println('foo is now ' + foo)                
+return foo
+""", "Dummy1.groovy")
+
+        
+        assert value == 2
+        assert shell.foo == 2 , "Value is now ${shell.foo}"
+	}
+    
+    void testDefineNewVariable() {
+        def shell = new GroovyShell()
+        
+        def value = shell.evaluate( """
+bar = 3 
+println('bar is now ' + bar)                
+return bar
+""", "Dummy2.groovy")
+
+        
+        assert value == 3
+        assert shell.bar == 3 , "Value is now ${shell.bar}"
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/SimpleGStringTemplateEngineTest.groovy b/groovy/src/test/groovy/SimpleGStringTemplateEngineTest.groovy
new file mode 100644
index 0000000..408aaac
--- /dev/null
+++ b/groovy/src/test/groovy/SimpleGStringTemplateEngineTest.groovy
@@ -0,0 +1,26 @@
+package groovy;
+
+import java.io.StringWriter;
+
+import groovy.text.Template;
+import groovy.text.GStringTemplateEngine;
+import junit.framework.TestCase;
+
+/**
+ * @author andy
+ * @since Jan 11, 2006 1:05:23 PM
+ */
+public class SimpleGStringTemplateEngineTest extends GroovyTestCase
+{
+  public void testRegressionCommentBug() throws Exception
+  {
+    final Template template = new GStringTemplateEngine().createTemplate(
+        "<% // This is a comment that will be filtered from output %>\n" +
+        "Hello World!"
+    );
+
+    final StringWriter sw = new StringWriter();
+    template.make().writeTo(sw);
+    assertEquals("\nHello World!", sw.toString());
+  }
+}
diff --git a/groovy/src/test/groovy/SimplePostfixTest.groovy b/groovy/src/test/groovy/SimplePostfixTest.groovy
new file mode 100644
index 0000000..4604dbf
--- /dev/null
+++ b/groovy/src/test/groovy/SimplePostfixTest.groovy
@@ -0,0 +1,13 @@
+package groovy
+
+class SimplePostfixTest extends GroovyTestCase {
+
+    void testPostfix() {
+        def x = 1
+        ++x
+        println(x)
+
+        assert x == 2
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/SingletonBugTest.groovy b/groovy/src/test/groovy/SingletonBugTest.groovy
new file mode 100644
index 0000000..05caca1
--- /dev/null
+++ b/groovy/src/test/groovy/SingletonBugTest.groovy
@@ -0,0 +1,99 @@
+package groovy
+// TODO: GROOVY-435
+
+class SingletonBugTest extends GroovyTestCase {
+
+    public void testPrivate() {
+        def x = SingletonBugPrivate.getInstance()
+        def y = SingletonBugPrivate.getInstance()
+        println "Get one private instance: $x"
+        println "Get another private instance: $y"
+        assert x == y
+
+         println(SingletonBugPrivateSecond.getInstanceSecond())
+         println(SingletonBugPrivateSecond.doTestSecond())
+        // shouldFail { println(SingletonBugPrivateSecond.getInstanceSecond()) }
+        // shouldFail { println(SingletonBugPrivateSecond.doTestSecond()) }
+    }
+
+    public void testProtected() {
+        def x = SingletonBugProtected.getInstance()
+        def y = SingletonBugProtected.getInstance()
+        println "Get one protected instance: $x"
+        println "Get another protected instance: $y"
+        assert x == y
+
+        println(SingletonBugProtectedSecond.getInstanceSecond())
+        println(SingletonBugProtectedSecond.doTestSecond())
+        x = SingletonBugProtectedSecond.getInstanceSecond()
+        y = SingletonBugProtectedSecond.doTestSecond()
+        assert x != y
+    }
+
+}
+
+
+class SingletonBugPrivate {
+
+    private static SingletonBugPrivate instance1 = null
+
+    private SingletonBugPrivate() {
+    }
+    
+    static SingletonBugPrivate getInstance() {
+        if (instance1 == null)
+            instance1 = new SingletonBugPrivate()
+        return instance1
+    }
+    
+    // private static SingletonBugPrivate getInstance2() {
+    //     if (instance1 == null)
+    //         instance1 = new SingletonBugPrivate()
+    //     return instance1
+    // }
+}
+
+
+class SingletonBugProtected {
+
+    private static SingletonBugProtected instance1 = null
+
+    protected SingletonBugProtected() {
+    }
+    
+    static SingletonBugProtected getInstance() {
+        if (instance1 == null)
+            instance1 = new SingletonBugProtected()
+        return instance1
+    }
+    
+    // private static SingletonBugProtected getInstance2() {
+    //     if (instance1 == null)
+    //         instance1 = new SingletonBugProtected()
+    //     return instance1
+    // }
+}
+
+
+class SingletonBugPrivateSecond extends SingletonBugPrivate {
+
+    static SingletonBugPrivate getInstanceSecond() {
+        return doTestSecond()
+    }
+
+    static SingletonBugPrivate doTestSecond() {
+        return new SingletonBugPrivate()
+    }
+}
+
+
+class SingletonBugProtectedSecond extends SingletonBugProtected {
+
+    static SingletonBugProtected getInstanceSecond() {
+        return doTestSecond()
+    }
+
+    static SingletonBugProtected doTestSecond() {
+        return new SingletonBugProtected()
+    }
+}
diff --git a/groovy/src/test/groovy/SliceTest.groovy b/groovy/src/test/groovy/SliceTest.groovy
new file mode 100644
index 0000000..6809307
--- /dev/null
+++ b/groovy/src/test/groovy/SliceTest.groovy
@@ -0,0 +1,16 @@
+package groovy
+
+class SliceTest extends GroovyTestCase {
+
+    void testListSlice() {
+        def list = [1, 2, 3, 4]
+        list[1,2] = 5
+
+        assert list == [1, 5, 4]
+
+        list[0,1] = [5, 6, 7, 8, 9]
+
+        assert list == [5, 6, 7, 8, 9, 4]
+
+    }
+}
diff --git a/groovy/src/test/groovy/SocketTest.groovy b/groovy/src/test/groovy/SocketTest.groovy
new file mode 100644
index 0000000..46d3844
--- /dev/null
+++ b/groovy/src/test/groovy/SocketTest.groovy
@@ -0,0 +1,78 @@
+package groovy
+/**
+ * check that groovy Socket methods do their job.
+ *
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+class SocketTest extends GroovyTestCase {
+    def mySocket
+    
+    void setUp() {
+        mySocket = new MockSocket()
+    }
+    
+    void testSocketAppendBytes() {
+        def myBytes = "mooky".getBytes()
+                  
+        mySocket << myBytes
+                  
+        def result = mySocket.outputStream.toByteArray()
+        assert result != null
+        assert Arrays.equals(myBytes,result)
+    }
+    void testSocketAppendTwoByteArrays() {
+        def myBytes1 = "foo".getBytes()
+        def myBytes2 = "bar".getBytes()
+                  
+        mySocket << myBytes1 << myBytes2
+                  
+        def result = mySocket.outputStream.toByteArray()
+        assert result != null
+        assert result.size() == myBytes1.size() + myBytes2.size()          
+    }
+    
+    void testSocketAppend() {
+        mySocket << "mooky"
+        assert "mooky" == mySocket.outputStream.toString()
+    }
+    
+    void testSocketWithStreamsClosure() {
+        mySocket.withStreams {i,o->
+            assert i instanceof InputStream
+            assert i != null
+                      
+            assert o instanceof OutputStream          
+            assert o != null          
+        }
+    }
+    
+    void tearDown() {
+        mySocket.close()
+    }
+}
+
+/**
+ * simple, unconnected Socket, used purely for test cases
+ */
+class MockSocket extends Socket {
+    private def i
+    private def o
+
+    public MockSocket() {
+        i = new MockInputStream()
+        o = new ByteArrayOutputStream()
+    }
+
+    public InputStream getInputStream() { return i }
+    public OutputStream getOutputStream() { return o }
+}
+
+/**
+ * only needed for workaround in groovy, 
+ *     new ByteArrayInputStream(myByteArray) doesn't work at mo... (28-Sep-2004)
+ */
+class MockInputStream extends InputStream {
+    int read() { return -1 }
+}
diff --git a/groovy/src/test/groovy/SomeClass.java b/groovy/src/test/groovy/SomeClass.java
new file mode 100644
index 0000000..72764bd
--- /dev/null
+++ b/groovy/src/test/groovy/SomeClass.java
@@ -0,0 +1,18 @@
+package groovy;
+
+/**
+ * Arbitrary holder for Java Methods to be called by Groovy TestCases.
+ *
+ * @author Dierk Koenig
+ */
+
+public class SomeClass {
+    // currently not supported to be called from Groovy
+    public String[][] anArrayOfStringArrays() {
+        return new String[][]{{"whatever"}};
+    }
+
+    public Object[] anArrayOfStringArraysWorkaround() {
+        return new Object[]{new String[]{"whatever", null}};
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/SortTest.groovy b/groovy/src/test/groovy/SortTest.groovy
new file mode 100644
index 0000000..151a0a9
--- /dev/null
+++ b/groovy/src/test/groovy/SortTest.groovy
@@ -0,0 +1,72 @@
+package groovy
+
+/** 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class SortTest extends GroovyTestCase {
+
+    // GROOVY-1956
+    void testSortWithNull() {
+        // normal case, should sort in place and return result
+        def x = [1, 2, 3, 1, 2, 3, null, 'a', null]
+        assert x.is(x.sort())
+        def y = x.sort()
+        assert (y == x && x == [null, null, 1, 1, 2, 2, 3, 3, 'a'])
+
+        // transitivity
+        x = [1, 2, 3, 1, 2, 3, null, 'a', null]
+        x.unique().sort()
+        y = [1, 2, 3, 1, 2, 3, null, 'a', null]
+        y.sort().unique()
+        assert (x == y && y == [null, 1, 2, 3, 'a'])
+    }
+
+    void testSortWithOrderBy() {
+        def list = getPeople()
+        def order = new OrderBy( { it.cheese } )
+        list.sort(order)
+        assert list[0].name == 'Joe'
+        assert list[-1].name == 'Chris'
+        assert list.name == ['Joe', 'Bob', 'James', 'Chris']
+    }
+    
+    void testSortWithClosure() {
+        def list = getPeople()
+        list.sort { it.cheese }
+        assert list.name == ['Joe', 'Bob', 'James', 'Chris']
+    }
+
+    void testSortClassHierarchy() {
+        def aFooList = [
+                new AFoo(5),
+                new AFoo(7),
+                new ABar(4),
+                new ABar(6)
+                ]
+        def sorted = aFooList.sort()
+        assert sorted.collect{ it.class } == [ABar, AFoo, ABar, AFoo]
+        assert sorted.collect{ it.key } == (4..7).toList()
+    }
+
+    def getPeople() {
+        def answer = []
+        answer << new Expando(name:'James', cheese:'Edam', location:'London')
+        answer << new Expando(name:'Bob', cheese:'Cheddar', location:'Atlanta')
+        answer << new Expando(name:'Chris', cheese:'Red Leicester', location:'London')
+        answer << new Expando(name:'Joe', cheese:'Brie', location:'London')
+        return answer
+    }
+
+}
+
+class AFoo implements Comparable {
+    int key
+    AFoo(int key) { this.key = key }
+    int compareTo(Object rhs) { key - rhs.key }
+    String toString() { this.class.name + ": " + key }
+}
+
+class ABar extends AFoo {
+    public ABar(int x) {super(x)}
+}
diff --git a/groovy/src/test/groovy/SpreadDotTest.groovy b/groovy/src/test/groovy/SpreadDotTest.groovy
new file mode 100644
index 0000000..bd66f00
--- /dev/null
+++ b/groovy/src/test/groovy/SpreadDotTest.groovy
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+ package groovy
+
+/**
+ * Test for the spread dot operator "*.".
+ *
+ * For an example,
+ *          list*.property
+ * means
+ *          list.collect { it?.property }
+ *
+ * @author  Pilho Kim
+ * @author  Paul King
+ */
+public class SpreadDotTest extends GroovyTestCase {
+    public void testSpreadDot() {
+        def m1 = ["a":1, "b":2]
+        def m2 = ["a":11, "b":22]
+        def m3 = ["a":111, "b":222]
+        def x = [m1,m2,m3]
+        println x*.a
+        println x*."a"
+        assert x == [m1, m2, m3]
+
+        def m4 = null
+        x << m4
+        println x*.a
+        println x*."a"
+        assert x == [m1, m2, m3, null]
+
+        def d = new SpreadDotDemo()
+        x << d
+        println x*."a"
+        assert x == [m1, m2, m3, null, d]
+
+        def y = new SpreadDotDemo2()
+        println y."a"
+        println y.a
+
+        x << y
+        println x*."a"
+        assert x == [m1, m2, m3, null, d, y]
+    }
+
+    public void testSpreadDot2() {
+        def a = new SpreadDotDemo()
+        def b = new SpreadDotDemo2()
+        def x = [a, b]
+
+        assert x*.fnB("1") == [a.fnB("1"), b.fnB("1")]
+        assert [a,b]*.fnB() == [a.fnB(), b.fnB()]
+    }
+
+    public void testSpreadDotArrays() {
+        def a = new SpreadDotDemo()
+        def b = new SpreadDotDemo2()
+        Object[] x = [a, b]
+
+        assert x*.fnB("1") == [a.fnB("1"), b.fnB("1")]
+        assert [a,b]*.fnB() == [a.fnB(), b.fnB()]
+
+        int[] nums = [3, 4, 5]
+        assert nums*.toString() == ['3', '4', '5']
+
+        boolean[] answers = [true, false, true]
+        assert answers*.toString() == ['true', 'false', 'true']
+
+        String[] pets = ['ant', 'bear', 'camel']
+        assert pets*.length() == nums
+    }
+
+    public void testSpreadDotOnArrays2 () {
+        def books = [Book1, Book2, Book3] as Object[]
+
+        books*.metaClass*.foo = { "Hello, ${delegate.class.name}" }
+
+        assertEquals "Hello, groovy.Book1", new Book1 ().foo ()
+        assertEquals "Hello, groovy.Book2", new Book2 ().foo ()
+        assertEquals "Hello, groovy.Book3", new Book3 ().foo ()
+    }
+}
+
+class SpreadDotDemo {
+    public java.util.Date getA() {
+        return new Date()
+    }
+    public String fnB() {
+        return "bb"
+    }
+    public String fnB(String m) {
+        return "BB$m"
+    }
+}
+
+class SpreadDotDemo2 {
+    public String getAttribute(String key) {
+        return "Attribute $key"
+    }
+    public String get(String key) {
+        return getAttribute("Get $key")
+    }
+    public String fnB() {
+        return "cc"
+    }
+    public String fnB(String m) {
+        return "CC$m"
+    }
+}
+
+
+class Book1 {}
+class Book2 {}
+class Book3 {}
+
diff --git a/groovy/src/test/groovy/SqlDateTest.groovy b/groovy/src/test/groovy/SqlDateTest.groovy
new file mode 100644
index 0000000..7eec873
--- /dev/null
+++ b/groovy/src/test/groovy/SqlDateTest.groovy
@@ -0,0 +1,61 @@
+package groovy;
+
+import org.codehaus.groovy.runtime.TimeCategory
+
+class SqlDateTest extends GroovyTestCase {
+
+    void testIncrement() {
+        def nowMillis = System.currentTimeMillis()
+        def sqlDate = new java.sql.Date(nowMillis)
+        def nowOffset = TimeCategory.getDaylightSavingsOffset(sqlDate).toMilliseconds()
+        sqlDate++
+        def incOffset = TimeCategory.getDaylightSavingsOffset(sqlDate).toMilliseconds()
+        assertTrue "incrementing a java.sql.Date returned an incorrect type: ${sqlDate.class}", sqlDate instanceof java.sql.Date
+
+        // difference adjusted for daylight savings
+        def diff = (sqlDate.getTime() + incOffset) - (nowMillis + nowOffset)
+        assertEquals "incrementing a java.sql.Date did not work properly", 1000 * 60 * 60 * 24, diff
+    }
+
+      void testDecrement() {
+        def nowMillis = System.currentTimeMillis()
+        def sqlDate = new java.sql.Date(nowMillis)
+        def nowOffset = TimeCategory.getDaylightSavingsOffset(sqlDate).toMilliseconds()
+        sqlDate--
+        def decOffset = TimeCategory.getDaylightSavingsOffset(sqlDate).toMilliseconds()
+        assertTrue "decrementing a java.sql.Date returned an incorrect type: ${sqlDate.class}", sqlDate instanceof java.sql.Date
+        
+        // difference adjusted for daylight savings
+        def diff = (nowMillis + nowOffset) - (sqlDate.getTime() + decOffset)
+
+        assertEquals "decrementing a java.sql.Date did not work properly", 1000 * 60 * 60 * 24, diff
+    }
+      
+    void testPlusOperator() {
+        def nowMillis = System.currentTimeMillis()
+        def sqlDate = new java.sql.Date(nowMillis)
+        def nowOffset = TimeCategory.getDaylightSavingsOffset(sqlDate).toMilliseconds()
+        sqlDate += 1
+        def incOffset = TimeCategory.getDaylightSavingsOffset(sqlDate).toMilliseconds()
+
+        assertTrue  "the plus operator applied to a java.sql.Date returned an incorrect type: ${sqlDate.class}", sqlDate instanceof java.sql.Date
+        
+        // difference adjusted for daylight savings
+        def diff = (sqlDate.getTime() + incOffset) - (nowMillis + nowOffset)
+        assertEquals "decrementing a java.sql.Date did not work properly", 1000 * 60 * 60 * 24, diff
+    }
+    
+    void testMinusOperator() {
+        def nowMillis = System.currentTimeMillis()
+        def sqlDate = new java.sql.Date(nowMillis)
+        def nowOffset = TimeCategory.getDaylightSavingsOffset(sqlDate).toMilliseconds()
+        sqlDate -= 1
+        def decOffset = TimeCategory.getDaylightSavingsOffset(sqlDate).toMilliseconds()
+
+        assertTrue  "the minus operator applied to a java.sql.Date returned an incorrect type: ${sqlDate.class}", sqlDate instanceof java.sql.Date
+        
+        // difference adjusted for daylight savings
+        def diff = (nowMillis + nowOffset) - (sqlDate.getTime() + decOffset)
+        assertEquals "decrementing a java.sql.Date did not work properly", 1000 * 60 * 60 * 24, diff
+    }
+}
diff --git a/groovy/src/test/groovy/StackTraceTest.groovy b/groovy/src/test/groovy/StackTraceTest.groovy
new file mode 100644
index 0000000..79f4d00
--- /dev/null
+++ b/groovy/src/test/groovy/StackTraceTest.groovy
@@ -0,0 +1,33 @@
+package groovy
+
+/**
+* This test case is added to ensure an exception thrown from inside
+* groovy does always contain a valid line number and file name for
+* the script method the exception is thrown from.
+*
+* See also GROOVY-726
+*/
+class StackTraceTest extends GroovyTestCase {
+
+
+	public void testTrace() {
+		def className = this.class.name
+		def assertDone = false	
+		try {
+		  throw new Exception("e")
+		} catch (Exception e) {
+		  assert e.message == "e"
+		  def trace = e.stackTrace
+		  trace.each {
+		    if (it.className == className && it.methodName == "testTrace") {
+		      assert it.lineNumber>0
+		      assert it.fileName != null
+		      assert it.fileName.length() > 0
+		      assertDone = true
+		    }
+		  }
+		}
+		assert assertDone==true
+	}
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/StaticAndDefaultKeywordsInPropertyNamesTest.groovy b/groovy/src/test/groovy/StaticAndDefaultKeywordsInPropertyNamesTest.groovy
new file mode 100644
index 0000000..34fa446
--- /dev/null
+++ b/groovy/src/test/groovy/StaticAndDefaultKeywordsInPropertyNamesTest.groovy
@@ -0,0 +1,25 @@
+package groovy

+

+class StaticAndDefaultKeywordsInPropertyNamesTest extends GroovyTestCase {

+

+    void testKeywords() {

+        def value = "returnValue"

+        StaticAndDefaultClass.metaClass.static.dynStaticMethod = {-> value }

+        assert value == StaticAndDefaultClass.dynStaticMethod()

+

+        StaticAndDefaultClass.metaClass.default = value

+        StaticAndDefaultClass.metaClass.goto = value

+        assert value == new StaticAndDefaultClass().default

+        assert value == new StaticAndDefaultClass().goto

+    }

+

+    void testKeywordsAsMapKeys() {

+        def map = [goto: 1, default: 2, static: 3]

+

+        assert 1 == map.goto

+        assert 2 == map.default

+        assert 3 == map.static

+    }

+}

+

+class StaticAndDefaultClass {}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/StaticImportTarget.groovy b/groovy/src/test/groovy/StaticImportTarget.groovy
new file mode 100644
index 0000000..b4b945b
--- /dev/null
+++ b/groovy/src/test/groovy/StaticImportTarget.groovy
@@ -0,0 +1,10 @@
+package groovy
+
+class StaticImportTarget {
+    def static x(String message, int times) {
+        return message * times
+    }
+    def y(String message, int times) {
+        return x(message, times)
+    }
+}
diff --git a/groovy/src/test/groovy/StaticImportTest.groovy b/groovy/src/test/groovy/StaticImportTest.groovy
new file mode 100644
index 0000000..41b28a6
--- /dev/null
+++ b/groovy/src/test/groovy/StaticImportTest.groovy
@@ -0,0 +1,93 @@
+package groovy
+
+import static java.lang.Boolean.FALSE as F
+import static java.text.DateFormat.MEDIUM as M
+import static java.text.DateFormat.MEDIUM
+import static java.awt.Color.*
+import static junit.framework.Assert.format
+import static junit.framework.Assert.assertEquals
+import static StaticImportTarget.x
+import static java.lang.Math.*
+import static java.util.Calendar.getInstance as now
+import static API.*
+
+class StaticImportTest extends GroovyTestCase {
+    void testFieldWithAliasInExpression() {
+        assert !F
+    }
+
+    void testMethodAndField() {
+        assert cos(2 * PI) == 1.0
+    }
+
+    static myStaticMethod() {
+        cos(2 * PI)
+    }
+
+    void testMethodAndFieldInStaticContext() {
+        assert myStaticMethod() == 1.0
+    }
+
+    void testMethodAndFieldInClosure() {
+        def closure = { cos(2 * PI) }
+        assert closure() == 1.0
+    }
+
+    void testFieldAsObjectExpression() {
+        assert PI.equals(Math.PI)
+    }
+
+    void testFieldAsArgumentList() {
+        assert ("" + PI.toString()).contains('3.14')
+    }
+
+    void testFieldAliasing() {
+        assert MEDIUM == M
+    }
+
+    void testMethodAliasing() {
+        // GROOVY-1809 making this not possible on one line?
+        def now = now().time
+        assert now.class == Date
+    }
+
+    void testWildCardAliasing() {
+        assert LIGHT_GRAY == java.awt.Color.LIGHT_GRAY
+    }
+
+    private format(a, b, c, ignored) { format(a, b, c) }
+
+    void testMethodDefCanUseStaticallyImportedMethodWithSameNameButDiffArgs() {
+        assert format("different", "abc", "aBc", 3) == 'different expected:<abc> but was:<aBc>'
+    }
+
+    void testAssertEqualsFromJUnit() {
+        double[] values = [3.9999, 4.0001, 0.00021, 0.00019]
+        assertEquals(values[0], values[1], values[2])
+        shouldFail(junit.framework.AssertionFailedError) {
+            assertEquals(values[0], values[1], values[3])
+        }
+    }
+
+    void testStaticImportFromGroovy() {
+        def nonstaticval = new StaticImportTarget().y("he", 3)
+        def staticval = x("he", 3)
+        assert nonstaticval == staticval
+    }
+
+    void testStaticImportWithVarArgs() {
+        assert noArrayMethod("one", 1) == 'noArrayMethod(one, 1)'
+        assert API.arrayMethod("two", 1, 2, 3) == 'arrayMethod(two, 1, 2, 3)'
+        assert arrayMethod("three", 1, 2, 3) == 'arrayMethod(three, 1, 2, 3)'
+    }
+}
+
+class API {
+    static noArrayMethod(String s, int value) {
+        "noArrayMethod(${s}, ${value})"
+    }
+
+    static arrayMethod(String s, int[] values) {
+        "arrayMethod(${s}, " + values.toList().join(", ") + ")"
+    }
+}
diff --git a/groovy/src/test/groovy/StaticMessageTest.groovy b/groovy/src/test/groovy/StaticMessageTest.groovy
new file mode 100755
index 0000000..bb4c9cd
--- /dev/null
+++ b/groovy/src/test/groovy/StaticMessageTest.groovy
@@ -0,0 +1,21 @@
+package groovy

+

+class StaticMessageTest extends GroovyTestCase {

+

+   void testStaticMissingMethodException() {

+      try {

+         Integer.foobarbaz()

+      } catch (MissingMethodException mme) {

+         assert mme.message ==~ '.*static.*'

+      }

+   }

+

+   void testInstanceMissingMethodException() {

+      try {

+         Integer x = 5;

+         x.foobarbaz()

+      } catch (MissingMethodException mme) {

+         assert ! (mme.message ==~ '.*static.*')

+      }

+   }

+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/StaticThisTest.groovy b/groovy/src/test/groovy/StaticThisTest.groovy
new file mode 100644
index 0000000..0c13d45
--- /dev/null
+++ b/groovy/src/test/groovy/StaticThisTest.groovy
@@ -0,0 +1,24 @@
+package groovy
+
+class StaticThisTest extends GroovyTestCase {
+
+    void testThisFail() {
+        staticMethod()
+    }
+
+    static def staticMethod() {
+        def foo = this
+
+        assert foo != null
+        assert foo.name.endsWith("StaticThisTest")
+
+        println("this: " + this)
+
+        def s = super
+
+        assert s != null
+        assert s.name.endsWith("GroovyTestCase")
+
+        println("super: " + super)
+    }
+}
diff --git a/groovy/src/test/groovy/StrangeBean.java b/groovy/src/test/groovy/StrangeBean.java
new file mode 100644
index 0000000..312510c
--- /dev/null
+++ b/groovy/src/test/groovy/StrangeBean.java
@@ -0,0 +1,39 @@
+package groovy;
+
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * This bean should encompass all legal, but bad, JavaBeans practices
+ */
+public class StrangeBean {
+    
+    Set strangeListeners;
+    
+    /** Creates a new instance of StrangeBean */
+    public StrangeBean() {
+        strangeListeners = new LinkedHashSet();
+    }
+    
+    public void addStrangeEventListener(StrangeEventListener listener) {
+        strangeListeners.add(listener);
+    }
+    
+    public void removeStrangeEventListener(StrangeEventListener listener) {
+        strangeListeners.remove(listener);
+    }
+    
+    public StrangeEventListener[] getStrangeEventListeners() {
+        return (StrangeEventListener[]) strangeListeners.toArray(new StrangeEventListener[strangeListeners.size()]);
+    }
+    
+    public void somethingStrangeHappened(String what, String where) {
+        Iterator iter = strangeListeners.iterator();
+        while (iter.hasNext()) {
+            StrangeEventListener listener = (StrangeEventListener) iter.next();
+            listener.somethingStrangeHappened(what, where);
+        }
+    }
+    
+}
diff --git a/groovy/src/test/groovy/StrangeBeanBeanInfo.java b/groovy/src/test/groovy/StrangeBeanBeanInfo.java
new file mode 100644
index 0000000..450bb0e
--- /dev/null
+++ b/groovy/src/test/groovy/StrangeBeanBeanInfo.java
@@ -0,0 +1,30 @@
+package groovy;
+
+import java.beans.EventSetDescriptor;
+import java.beans.SimpleBeanInfo;
+import java.lang.reflect.Method;
+
+public class StrangeBeanBeanInfo extends SimpleBeanInfo {
+
+    public EventSetDescriptor[] getEventSetDescriptors() {
+        try {
+            Method[] events = StrangeEventListener.class.getMethods();
+            Method addListener = StrangeBean.class.getMethod("addStrangeEventListener", new Class[] {StrangeEventListener.class});
+            Method removeListener = StrangeBean.class.getMethod("removeStrangeEventListener", new Class[] {StrangeEventListener.class});
+            Method getListeners = StrangeBean.class.getMethod("getStrangeEventListeners", new Class[0]);
+            
+            return new EventSetDescriptor[] {
+                new EventSetDescriptor( 
+                        "strangeEvent",
+                        StrangeEventListener.class, 
+                        events,
+                        addListener,
+                        removeListener,
+                        getListeners)
+            };
+        } catch (Exception ie) {
+            ie.printStackTrace(System.out);
+            return super.getEventSetDescriptors();
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/StrangeEventListener.java b/groovy/src/test/groovy/StrangeEventListener.java
new file mode 100644
index 0000000..31b87bd
--- /dev/null
+++ b/groovy/src/test/groovy/StrangeEventListener.java
@@ -0,0 +1,16 @@
+package groovy;
+
+import java.util.EventListener;
+import javax.swing.event.ChangeEvent;
+
+public interface StrangeEventListener extends EventListener {
+
+    /**
+     * Acorrding to section 6.4.1 of the JavaBeans spec this is legal, but not
+     * good practice.  We need to test what can be done not what should be done
+     */
+    void somethingStrangeHappened(String what, String where);
+    
+    void somethingChanged(ChangeEvent changeEvent);
+    
+}
diff --git a/groovy/src/test/groovy/StringBufferTest.groovy b/groovy/src/test/groovy/StringBufferTest.groovy
new file mode 100644
index 0000000..ce57b85
--- /dev/null
+++ b/groovy/src/test/groovy/StringBufferTest.groovy
@@ -0,0 +1,27 @@
+package groovy
+
+class StringBufferTest extends GroovyTestCase {
+    void testSize() {
+        def x = new StringBuffer()
+        assert x.size() == x.length()
+        x = new StringBuffer('some text')
+        assert x.size() == x.length()
+    }
+
+    void testPutAt(){
+        def buf = new StringBuffer('0123')
+        buf[1..2] = 'xx'
+        assert '0xx3' == buf.toString()  , 'replace with String'
+        buf = new StringBuffer('0123')
+        buf[1..2] = 99
+        assert '0993' == buf.toString()  , 'replace with obj.toString()'
+        buf = new StringBuffer('0123')
+        buf[0..<0] = 'xx'
+        assert 'xx0123' == buf.toString(), 'border case left'
+        buf = new StringBuffer('0123')
+        buf[4..4] = 'xx'
+        println buf.toString()
+        assert '0123xx' == buf.toString(), 'border case right'
+        // more weird Ranges already tested in ListTest
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/StringTest.groovy b/groovy/src/test/groovy/StringTest.groovy
new file mode 100644
index 0000000..0cc60b0
--- /dev/null
+++ b/groovy/src/test/groovy/StringTest.groovy
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy
+
+/**
+ * Various tests for Strings.
+ *
+ * @author Michael Baehr
+ */
+class StringTest extends GroovyTestCase {
+
+    void testString() {
+        def s = "abcd"
+        assert s.length() == 4
+        assert 4 == s.length()
+        
+        // test polymorphic size() method like collections
+        assert s.size() == 4
+        
+        s = s + "efg" + "hijk"
+        
+        assert s.size() == 11
+        assert "abcdef".size() == 6
+    }
+
+    void testStringPlusNull() {
+        def y = null
+        def x = "hello " + y
+        assert x == "hello null"
+    }
+    
+    void testNextPrevious() {
+    	def x = 'a'
+    	def y = x.next()
+    	assert y == 'b'
+    
+    	def z = 'z'.previous()
+    	assert z == 'y'
+    	
+    	z = 'z'
+    	def b = z.next()
+    	assert b != 'z'
+
+        assert b > z
+        assert z.charAt(0) == 'z'
+        assert b.charAt(0) == '{'
+    }
+    
+    void testApppendToString() {
+        def name = "Gromit"
+        def result = "hello " << name << "!"
+        
+        assert result.toString() == "hello Gromit!"
+    }
+    
+    void testApppendToStringBuffer() {
+        def buffer = new StringBuffer()
+        
+        def name = "Gromit"
+        buffer << "hello " << name << "!" 
+        
+        assert buffer.toString() == "hello Gromit!"
+    }
+
+    void testApppendAndSubscipt() {
+        def result =  'hello' << " Gromit!"
+        result[1..4] = 'i'
+        assert result.toString() == "hi Gromit!"
+    }
+
+    void assertLength(s, len) {
+        if (s.length() != len)  println "*** length != $len: $s"
+        assert s.length() == len
+    }
+    void assertContains(s, len, subs) {
+        assertLength(s, len)
+        if (s.indexOf(subs) < 0)  println "*** missing $subs: $s"
+        assert s.indexOf(subs) >= 0
+    }
+
+    void testSimpleStringLiterals() {
+        assertLength("\n", 1)
+        assertLength("\"", 1)
+        assertLength("\'", 1)
+        assertLength("\\", 1)
+        assertContains("\${0}", 4, "{0}")
+        assertContains("x\
+y", 2, "xy")
+
+        assertLength('\n', 1)
+        assertLength('\'', 1)
+        assertLength('\\', 1)
+        assertContains('${0}', 4, '{0}')
+        assertContains('x\
+y', 2, 'xy')
+    }
+
+    void testMultilineStringLiterals() {
+        assertContains(""""x""", 2, '"x');
+        assertContains("""""x""", 3, '""x');
+        assertContains("""x
+y""", 3, 'x\ny');
+        assertContains("""\n
+\n""", 3, '\n\n\n');
+
+        assertContains(''''x''', 2, "'x");
+        assertContains('''''x''', 3, "''x");
+        assertContains('''x
+y''', 3, 'x\ny');
+        assertContains('''\n
+\n''', 3, '\n\n\n');
+
+    }
+
+    void testRegexpStringLiterals() {
+        assert "foo" == /foo/
+        assert '\\$$' == /\$$/
+        // Backslash before newline or slash disappears (all others are preserved):
+        assert "/\\*" == /\/\*/
+        assert "\n" == /\
+/
+    }
+
+
+    void testBoolCoerce() {
+
+        // Explicit coercion
+        assertFalse((Boolean) "")
+        assertTrue((Boolean) "content")
+
+        // Implicit coercion in statements
+        String s = null
+        if (s) {
+            fail("null should have evaluated to false, but didn't")
+        }
+        s = ''
+        if (s) {
+            fail("'' should have evaluated to false, but didn't")
+        }
+        s = 'something'
+        if (s) {
+            // OK
+        } else {
+            fail("'something' should have evaluated to false, but didn't")
+        }
+        
+    }
+
+    void testSplit() {
+        def text = "hello there\nhow are you"
+        def splitted = text.split().toList()
+        assert splitted == ['hello', 'there', 'how', 'are', 'you']
+    }
+
+    void testSplitEmptyText() {
+        def text = ""
+        def splitted = text.split().toList()
+        assert splitted == []
+    }
+
+    void testSplitEqualsTokenize() {
+        def text = """
+        A text with different words and
+        numbers like 3453 and 3,345,454.97 and
+        special characters %&)( and also
+        everything mixed together 45!kw?
+      """
+
+        def splitted = text.split().toList()
+        def tokenized = text.tokenize()
+        assert splitted == tokenized
+    }
+
+}
diff --git a/groovy/src/test/groovy/SubscriptTest.groovy b/groovy/src/test/groovy/SubscriptTest.groovy
new file mode 100644
index 0000000..43f93cf
--- /dev/null
+++ b/groovy/src/test/groovy/SubscriptTest.groovy
@@ -0,0 +1,213 @@
+package groovy
+
+class SubscriptTest extends GroovyTestCase {
+
+    void testListRange() {
+        def list = ['a', 'b', 'c', 'd', 'e']
+
+        def sub = list[2..4]
+        assert sub == ['c', 'd', 'e']
+        
+        sub = list[2..<5]
+        assert sub == ['c', 'd', 'e']
+        
+        def value = list[-1]
+        assert value == 'e'
+        
+        sub = list[-4..-2]
+        assert sub == ['b', 'c', 'd']
+        
+        // backwards ranges
+        sub = list[-1..-3]
+        assert sub == ['e', 'd', 'c']
+        
+        sub = list[-3..-1]
+        assert sub == ['c', 'd', 'e']
+        
+        sub = list[3..1]
+        assert sub == ['d', 'c', 'b']
+        
+        sub = list[1..-3]
+        assert sub == ['b', 'c']
+    }
+    
+    void testObjectRangeRange() {
+        def list = 'a'..'e'
+        
+        def sub = list[2..4]
+        assert sub == ['c', 'd', 'e']
+        
+        def value = list[-1]
+        assert value == 'e'
+        
+        sub = list[-4..-2]
+        assert sub == ['b', 'c', 'd']
+        
+        // backwards ranges
+        sub = list[-1..-3]
+        assert sub == ['e', 'd', 'c']
+        
+        sub = list[3..1]
+        assert sub == ['d', 'c', 'b']
+    }
+    
+    void testStringArrayRange() {
+        String[] list = ['a', 'b', 'c', 'd', 'e']
+        
+        def sub = list[2..4]
+        assert sub == ['c', 'd', 'e']
+        
+        def value = list[-1]
+        assert value == 'e'
+        
+        sub = list[-4..-2]
+        assert sub == ['b', 'c', 'd']
+        
+        // backwards ranges
+        sub = list[-1..-3]
+        assert sub == ['e', 'd', 'c']
+        
+        sub = list[3..1]
+        assert sub == ['d', 'c', 'b']
+    }
+    
+    void testIntRangeRange() {
+        def list = 10..15
+        
+        def sub = list[2..4]
+        assert sub == [12, 13, 14]
+        
+        def value = list[-1]
+        assert value == 15
+        
+        sub = list[-4..-2]
+        assert sub == [12, 13, 14]
+        
+        // backwards ranges
+        sub = list[-1..-3]
+        assert sub == [15, 14, 13]
+        
+        sub = list[3..1]
+        assert sub == [13, 12, 11]
+    }
+    
+    void testIntArrayRange() {
+        Integer[] list = [ 10, 11, 12, 13, 14, 15 ]
+        
+        def sub = list[2..4]
+        assert sub == [12, 13, 14]
+        
+        def value = list[-1]
+        assert value == 15
+        
+        sub = list[-4..-2]
+        assert sub == [12, 13, 14]
+        
+        // backwards ranges
+        sub = list[-1..-3]
+        assert sub == [15, 14, 13]
+        
+        sub = list[3..1]
+        assert sub == [13, 12, 11]
+    }
+    
+    void testStringSubscript() {
+        def text = "nice cheese gromit!"
+        
+        def x = text[2]
+        
+        assert x == "c"
+        assert x.class == String
+        
+        def sub = text[5..10]
+        assert sub == 'cheese'
+        
+        sub = text[10..5]
+        assert sub == 'eseehc'
+        
+        sub = text[-2..-7]
+        assert sub == 'timorg'
+        
+        sub = text[1..-3]
+        assert sub == "ice cheese gromi"
+        
+    }
+
+    void testStringPutAtRange(){
+        def text = "0123"
+    }
+    
+    void testListSubscriptWithList() {
+        def list = ['a', 'b', 'c', 'd', 'e']
+        
+        def indices = [0, 2, 4]
+        def sub = list[indices]
+        assert sub == ['a', 'c', 'e']
+        
+        // verbose but valid
+        sub = list[[1, 3]]
+        assert sub == ['b', 'd']
+     
+        // syntax sugar
+        sub = list[2, 4]
+        assert sub == ['c', 'e']
+    }
+    
+    
+    void testListSubscriptWithListAndRange() {
+        def list = 100..200
+
+        def sub = list[1, 3, 20..25, 33]
+        assert sub == [101, 103, 120, 121, 122, 123, 124, 125, 133]
+
+        // now lets try it on an array
+        def array = list.toArray()
+
+        sub = array[1, 3, 20..25, 33]
+        assert sub == [101, 103, 120, 121, 122, 123, 124, 125, 133]
+    }
+
+    void testStringWithSubscriptList() {
+
+        def text = "nice cheese gromit!"
+        
+        def sub = text[1, 2, 3, 5..10]
+        
+        assert sub == "icecheese"
+    }
+    
+    void testSubMap() {
+        def map = ['a':123, 'b':456, 'c':789]
+        
+        def keys = ['b', 'a']
+        def sub = map.subMap(keys)
+        
+        assert sub.size() == 2
+        assert sub['a'] == 123
+        assert sub['b'] == 456
+        assert ! sub.containsKey('c')
+        
+        assert map.getClass() == sub.getClass()
+    }
+    
+    void testListWithinAListSyntax() {
+        def list = [1, 2, 3, 4..10, 5, 6]
+        
+        assert list.size() == 6
+        def sublist = list[3]
+        assert sublist == 4..10
+        assert sublist == [4, 5, 6, 7, 8, 9, 10]
+    }
+
+
+    void testBeanProperties() {
+        def foo = new Foo()
+
+        foo['name'] = 'Gromit'
+
+        assert foo.name == 'Gromit'
+
+        def value = foo['name']
+        assert value == 'Gromit'
+    }
+}
diff --git a/groovy/src/test/groovy/SwitchTest.groovy b/groovy/src/test/groovy/SwitchTest.groovy
new file mode 100644
index 0000000..6369ed6
--- /dev/null
+++ b/groovy/src/test/groovy/SwitchTest.groovy
@@ -0,0 +1,198 @@
+package groovy
+
+class SwitchTest extends GroovyTestCase {
+
+    void testSwitch() {
+        callSwitch("foo", "foo")
+        callSwitch("bar", "barfoo")
+        callSwitch("barbar", "barfoo")
+        callSwitch("dummy", "d*")
+        callSwitch("xyz", "xyzDefault")
+        callSwitch("zzz", "Default")
+        callSwitch(4, "List")
+        callSwitch(5, "List")
+        callSwitch(6, "List")
+        callSwitch("inList", "List")
+        callSwitch(1, "Integer")
+        callSwitch(1.2, "Number")
+        callSwitch(null, "null")
+        callSwitch([1, 2, 3], "ListInterface")
+    }
+
+    def callSwitch(x, expected) {
+        def result = ""
+        switch (x) {
+            case null:
+                result = "null"
+                break
+
+            case ~/d.*/:
+                result = "d*"
+                break
+
+            case "barbar":
+            case "bar":
+                result = result + "bar"
+
+            case "foo":
+                result = result + "foo"
+                break
+
+            case [4, 5, 6, 'inList']:
+                result = "List"
+                break
+
+            case Integer:
+                result = "Integer"
+                break
+
+            case Number:
+                result = "Number"
+                break
+
+            case List:
+                result = "ListInterface"
+                break
+
+            case "xyz":
+                result = result + "xyz"
+
+            default:
+                result = result + "Default"
+                // unnecessary just testing compiler
+                break
+        }
+        assert result == expected, "Expected $expected but found $result when calling switch with $x"
+    }
+
+    // test the continue in switch, which should jump to the the while start
+    void testSwitchScope() {
+        def i = 0
+        def j = 0
+        while (true) {
+            ++i;
+            switch (i) {
+                case 4:
+                    continue
+                case 5:
+                    break;
+                default:
+                    j += i;
+                    break;
+            }
+            if (i == 5) break;
+        }
+        assert j == 6
+    }
+
+    void testSwitchWithClosure() {
+        switch (0) {
+            case {true}: break
+            default: assert false
+        }
+        switch (0) {
+            case {false}: assert false
+        }
+        switch (0) {
+            case {it == 0}: break
+            default: assert false
+        }
+        switch (0) {
+            case {candidate -> candidate == 0}: break
+            default: assert false
+        }
+    }
+
+    /** older versions of groovy produced a ListExpression for a
+        fall through. the result was that it worked in some cases
+        and in other cases not. For example not for patterns */
+    void testFallthroughToOtherCaseWithNoCode() {
+        def a = ['FileName.java', 'AnotherFileName.groovy', 'foo']
+        def i = 0
+        a.each {
+            switch (it) {
+                case ~/.*java$/:
+                case ~/.*groovy$/:
+                    i++
+                    break
+                default:
+                    i += 10
+            }
+        }
+        assertEquals 12, i
+    }
+
+    void testFallthroughToOtherCaseWithCode() {
+        def a = ['FileName.java', 'AnotherFileName.groovy', 'foo']
+        def i = 0
+        a.each {
+            switch (it) {
+                case ~/.*java$/:
+                    i += 5
+                case ~/.*groovy$/:
+                    i++
+                    break
+                default:
+                    i += 10
+            }
+        }
+        assertEquals 17, i
+    }
+
+    void testFallthroughToDefaultWithNoCode() {
+        def a = ['FileName.java', 'AnotherFileName.groovy', 'foo']
+        def i = 0
+        a.each {
+            switch (it) {
+                case ~/.*java$/:
+                    i++
+                    break
+                case ~/.*groovy$/:
+                default:
+                    i += 10
+            }
+        }
+        assertEquals 21, i
+    }
+
+    void testFallthroughToDefaultWithCode() {
+        def a = ['FileName.java', 'AnotherFileName.groovy', 'foo']
+        def i = 0
+        a.each {
+            switch (it) {
+                case ~/.*java$/:
+                    i++
+                    break
+                case ~/.*groovy$/:
+                    i += 5
+                default:
+                    i += 10
+            }
+        }
+        assertEquals 26, i
+    }
+
+    void testSwitchNoStatementsAtEnd() {
+        def a = ['FileName.java', 'AnotherFileName.groovy', 'foo']
+        def i = 0
+        a.each {
+            switch (it) {
+                case ~/.*java$/:
+                    i++
+                    break
+                case ~/.*groovy$/: break
+            }
+        }
+        assertEquals 1, i
+        i = 0
+        a.each {
+            switch (it) {
+                case ~/.*java$/:
+                    i++
+                    break
+                default: break
+            }
+        }
+        assertEquals 1, i
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/SwitchWithDifferentTypesTest.groovy b/groovy/src/test/groovy/SwitchWithDifferentTypesTest.groovy
new file mode 100644
index 0000000..c0394b7
--- /dev/null
+++ b/groovy/src/test/groovy/SwitchWithDifferentTypesTest.groovy
@@ -0,0 +1,134 @@
+package groovy
+
+/** 
+ * A test case for switch statement with different types
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+class SwitchWithDifferentTypesTest extends GroovyTestCase {
+
+    void testSwitchWithIntValues() {
+        assertSwitch(1, 2, 3, 4)
+    }
+
+    void testSwitchWithDoubleValues() {
+        assertSwitch(1.5, 2.4, 3.2, 4.1)
+    }
+    
+    void testSwitchWithStringValues() {
+        assertSwitch("abc", "def", "xyz", "unknown")
+    }
+
+    void testSwitchWithMixedTypeValues() {
+        assertSwitch("abc", new Date(), 5.32, 23)
+    }
+
+    void testSwitchWithMixedNumberValues() {
+        def i = 1 as Integer
+        def bi = 1 as BigInteger
+        def bd1 = 1.0 as BigDecimal
+        def bd2 = 1.0000 as BigDecimal
+        assertSwitchMixed(i, bi, 2, 3)
+        assertSwitchMixed(i, bd1, 2, 3)
+        assertSwitchMixed(i, bd2, 2, 3)
+        assertSwitchMixed(bi, i, 2, 3)
+        assertSwitchMixed(bi, bd1, 2, 3)
+        assertSwitchMixed(bi, bd2, 2, 3)
+        assertSwitchMixed(bd1, i, 2, 3)
+        assertSwitchMixed(bd1, bi, 2, 3)
+        assertSwitchMixed(bd1, bd2, 2, 3)
+        assertSwitchMixed(bd2, i, 2, 3)
+        assertSwitchMixed(bd2, bi, 2, 3)
+        assertSwitchMixed(bd2, bd1, 2, 3)
+    }
+    
+    void assertSwitchMixed(switchValue, matchingValue, nonmatchingValue1, nonmatchingValue2) {
+      assertSwitchMatch1(switchValue, matchingValue, nonmatchingValue1, nonmatchingValue2)
+      assertSwitchMatch2(switchValue, nonmatchingValue1, matchingValue, nonmatchingValue2)
+      assertSwitchMatch3(switchValue, nonmatchingValue1, nonmatchingValue2, matchingValue)
+    }
+    
+    void assertSwitch(a, b, c, d) {
+        assertSwitchMatch1(a, a, b, c)
+        assertSwitchMatch2(b, a, b, c)
+        assertSwitchMatch3(c, a, b, c)
+        assertSwitchMatchDefault(d, a, b, c)
+    }
+    
+    void assertSwitchMatch1(value, case1Value, case2Value, case3Value) {
+        switch (value) {
+            case case1Value: 
+                // worked
+                break
+            case case2Value: 
+                failNotEquals(value, case2Value)
+                break
+            case case3Value: 
+                failNotEquals(value, case3Value)
+                break
+            default:
+                failNotDefault(value)
+                break
+        }
+    }
+
+    void assertSwitchMatch2(value, case1Value, case2Value, case3Value) {
+        switch (value) {
+            case case1Value: 
+                failNotEquals(value, case1Value)
+                break
+            case case2Value: 
+                // worked
+                break
+            case case3Value: 
+                failNotEquals(value, case3Value)
+                break
+            default:
+                failNotDefault(value)
+                break
+        }
+    }
+    
+    void assertSwitchMatch3(value, case1Value, case2Value, case3Value) {
+        switch (value) {
+            case case1Value: 
+                failNotEquals(value, case1Value)
+                break
+            case case2Value: 
+                failNotEquals(value, case2Value)
+                break
+            case case3Value: 
+                // worked
+                break
+            default:
+                failNotDefault(value)
+                break
+        }
+    }
+    
+    void assertSwitchMatchDefault(value, case1Value, case2Value, case3Value) {
+        switch (value) {
+            case case1Value: 
+                failNotEquals(value, case1Value)
+                break
+            case case2Value: 
+                failNotEquals(value, case2Value)
+                break
+            case case3Value: 
+                failNotEquals(value, case3Value)
+                break
+            default:
+                // worked
+                break
+        }
+    }
+
+    void failNotEquals(value, expectedCaseValue) {
+        fail("value: " + value + " is not equal to case value: " + expectedCaseValue)
+    }
+
+    void failNotDefault(value) {
+        fail("value: " + value + " should not match the default switch clause" )
+    }
+}
diff --git a/groovy/src/test/groovy/TestInterruptor.java b/groovy/src/test/groovy/TestInterruptor.java
new file mode 100644
index 0000000..1102423
--- /dev/null
+++ b/groovy/src/test/groovy/TestInterruptor.java
@@ -0,0 +1,18 @@
+package groovy;
+
+public class TestInterruptor implements Runnable {
+    private Thread caller;
+
+    public TestInterruptor(Thread caller) {
+        this.caller = caller;
+    }
+
+    public void run() {
+        try {
+            Thread.currentThread().sleep(100); // enforce yield, so we have something to interrupt
+        } catch (InterruptedException e) {
+            // ignore
+        }
+        caller.interrupt();
+    }
+}
diff --git a/groovy/src/test/groovy/TextPropertyTest.groovy b/groovy/src/test/groovy/TextPropertyTest.groovy
new file mode 100644
index 0000000..19a990e
--- /dev/null
+++ b/groovy/src/test/groovy/TextPropertyTest.groovy
@@ -0,0 +1,43 @@
+package groovy
+/**
+ * check that text def is available on...
+ *
+ * myFile.text,  myFile.text(charset),  
+ * myURL.text,  myURL.text(charset),
+ * myInputStream.text,  myInputStream.text(charset),
+ * myReader.text,
+ * myBufferedReader.text,
+ * myProcess.text
+ * 
+ * @author <a href="mailto:jeremy.rayner@bigfoot.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+
+class TextPropertyTest extends GroovyTestCase {
+    def myReader
+    def myInputStream
+    def myBigEndianEncodedInputStream
+    
+    void setUp() {
+        myReader = new StringReader("digestive")
+        myInputStream = new ByteArrayInputStream("chocolate chip".bytes)
+        myBigEndianEncodedInputStream = new ByteArrayInputStream("shortbread".getBytes("UTF-16BE"))
+    }
+    
+    void testBigEndianEncodedInputStreamText() {
+        assert "shortbread" == myBigEndianEncodedInputStream.getText("UTF-16BE")
+    }
+    
+    void testInputStreamText() {
+        assert "chocolate chip" == myInputStream.text
+    }
+    
+    void testReaderText() {
+        assert "digestive" == myReader.text
+    }
+    
+    void tearDown() {
+        myInputStream = null
+        myReader = null
+    }
+}
diff --git a/groovy/src/test/groovy/ThisAndSuperTest.groovy b/groovy/src/test/groovy/ThisAndSuperTest.groovy
new file mode 100644
index 0000000..4c028f6
--- /dev/null
+++ b/groovy/src/test/groovy/ThisAndSuperTest.groovy
@@ -0,0 +1,152 @@
+package groovy

+

+class ThisAndSuperTest extends GroovyTestCase{

+	void testOverwrittenSuperMethod(){

+		def helper = new TestForSuperHelper2()

+		assert helper.foo() == 2

+		assert helper.callFooInSuper() == 1

+	}

+		

+	void testClosureUsingSuperAndThis(){

+		def helper = new TestForSuperHelper2()

+		assert helper.aClosureUsingThis() == 2

+		assert helper.aClosureUsingSuper() == 1

+  		// accessing private method should not be changed

+  		// by a public method of the same name and signature!

+		assertEquals "bar", helper.closureUsingPrivateMethod() 

+		assert helper.bar() == "no bar"

+		

+		assert helper.aField == "I am a field"

+		helper.closureFieldAccessUsingImplicitThis(1)

+        assert helper.aField == 1

+ 		helper.closureFieldAccessUsingExplicitThis(2)

+ 		assert helper.aField == 2

+	}

+	

+	void testClosureDelegateAndThis(){

+		def map = [:]

+		def helper = new TestForSuperHelper2()

+		

+		helper.aField = "I am a field"

+		helper.closureFieldAccessUsingExplicitThis.delegate = map

+		helper.closureFieldAccessUsingExplicitThis(3)

+ 		assert helper.aField == 3

+ 		assert map.aField == null

+

+		helper.aField = "I am a field"

+		helper.closureFieldAccessUsingImplicitThis.delegate = map

+		helper.closureFieldAccessUsingImplicitThis(4)

+ 		assert helper.aField == 4

+ 		assert map.aField == null

+ 		

+ 		def closure = {this.foo = 1}

+		shouldFail {

+		  closure()

+		}

+		closure.delegate = map

+		shouldFail {

+		  closure()

+		}

+		assert map.foo == null

+		

+		closure = {foo = 1}

+		shouldFail {

+		  closure()

+		}

+		closure.delegate = map

+		closure()

+		assert map.foo == 1

+	}

+	

+	void testConstructorChain() {

+		def helper = new TestForSuperHelper4()

+		assert helper.x == 1

+		helper = new TestForSuperHelper4("foo")

+		assert helper.x == "Object"

+	}

+	

+	void testChainingForAsType() {

+		def helper = new TestForSuperHelper1()

+		def ret = helper as Object[]

+		assert ret instanceof Object[]

+		assert ret[0] == helper

+		

+		shouldFail(ClassCastException) {

+		  helper as Integer

+		}

+	}

+

+    void testSuperEach () {

+        def x = new TestForSuperEach()

+        x.each {

+            x.res << "I am it: ${it.class.name}"

+        }

+

+        assertEquals 3, x.res.size()

+        assertEquals "start each in subclass", x.res [0]

+        assertEquals "I am it: groovy.TestForSuperEach", x.res [1]

+        assertEquals "end of each in subclass", x.res [2]

+    }

+

+    void testDgm () {

+      assertEquals A.empty(), '123'

+    }

+}

+

+class A {

+   static {

+       A.metaClass.static.empty << { -> '123' }

+   }

+}

+

+

+class TestForSuperEach {

+  def res = []

+    

+  def each(Closure c) {

+      res << "start each in subclass"

+      super.each(c)

+      res << "end of each in subclass"

+  }

+}

+

+class TestForSuperHelper1 {

+  def foo(){1}

+  private bar(){"bar"}

+  def closureUsingPrivateMethod() {bar()}

+  def asType(Class c) {

+    if (c==Object[]) return [this] as Object[]

+    return super.asType(c)

+  }

+}

+

+class TestForSuperHelper2 extends TestForSuperHelper1{

+  def foo(){2}

+  def callFooInSuper(){super.foo()}

+  def aClosureUsingSuper = {super.foo()}

+  def aClosureUsingThis = {this.foo()}

+  def bar(){"no bar"}

+  public aField = "I am a field"

+  def closureFieldAccessUsingImplicitThis = {x-> aField=x}

+  def closureFieldAccessUsingExplicitThis = {x-> this.aField=x}

+}

+

+class TestForSuperHelper3 {

+  def x

+  TestForSuperHelper3(int i) {

+    this("1")

+    x=1

+  }

+  TestForSuperHelper3(Object j) {

+    x = "Object"

+  }

+}

+

+class TestForSuperHelper4 extends TestForSuperHelper3{

+  TestForSuperHelper4() {

+    super(1)

+  }

+  TestForSuperHelper4(Object j) {

+    super(j)

+  }

+}

diff --git a/groovy/src/test/groovy/ThrowTest.groovy b/groovy/src/test/groovy/ThrowTest.groovy
new file mode 100644
index 0000000..c371c73
--- /dev/null
+++ b/groovy/src/test/groovy/ThrowTest.groovy
@@ -0,0 +1,18 @@
+package groovy
+
+class ThrowTest extends GroovyTestCase {
+    
+    void testThrow() {
+        
+        try {
+	        throw new Exception("abcd")
+	        
+	        fail("Should have thrown an exception by now")
+        }
+        catch (Exception e) {
+            assert e.message == "abcd"
+            
+            println("Caught exception ${e}")
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/ToArrayBugTest.groovy b/groovy/src/test/groovy/ToArrayBugTest.groovy
new file mode 100644
index 0000000..59f9c66
--- /dev/null
+++ b/groovy/src/test/groovy/ToArrayBugTest.groovy
@@ -0,0 +1,29 @@
+package groovy
+
+class ToArrayBugTest extends GroovyTestCase {
+    
+    void testToArrayBug() {
+        
+        def array = getArray()
+
+        callArrayMethod(array)
+    }
+    
+    protected def getArray() {
+        def list = [1, 2, 3, 4]
+        def array = list.toArray()
+        
+        assert array != null
+        
+        return array
+    }
+    
+    protected def callArrayMethod(array) {
+        System.out.println("Called method with ${array}")
+        
+        def list = Arrays.asList(array)
+        
+        assert list.size() == 4
+        assert list == [1, 2, 3, 4]
+    }
+}
diff --git a/groovy/src/test/groovy/TripleQuotedStringTest.groovy b/groovy/src/test/groovy/TripleQuotedStringTest.groovy
new file mode 100644
index 0000000..1e2a934
--- /dev/null
+++ b/groovy/src/test/groovy/TripleQuotedStringTest.groovy
@@ -0,0 +1,22 @@
+package groovy
+
+class TripleQuotedStringTest extends GroovyTestCase {
+
+    void testTripleQuotedString() {
+        def s = """
+        Lots of 'text' with a variety of ""quoting "" and
+   a few lines
+    and some escaped \""" quoting and
+    an ending""".trim()
+
+        println(s)
+        assert s != null
+        def idx = s.indexOf("quoting and")
+        assert idx > 0
+    }
+
+    static void main( String[] args ) { 
+        def o = new TripleQuotedStringTest();
+        o.testTripleQuotedString();
+    }
+}
diff --git a/groovy/src/test/groovy/TryCatchTest.groovy b/groovy/src/test/groovy/TryCatchTest.groovy
new file mode 100644
index 0000000..09169ba
--- /dev/null
+++ b/groovy/src/test/groovy/TryCatchTest.groovy
@@ -0,0 +1,157 @@
+package groovy
+
+class TryCatchTest extends GroovyTestCase {
+
+    def exceptionCalled
+    def finallyCalled
+	
+    void testTryCatch() {
+        try {
+            failingMethod()
+        }
+        catch (AssertionError e) {
+            onException(e)
+        }
+        finally {
+            onFinally()
+        }
+        afterTryCatch()
+        assert exceptionCalled , "should have invoked the catch clause"
+        assert finallyCalled , "should have invoked the finally clause"
+        println("After try/catch")
+     }
+
+
+     void testTryFinally() {
+         Boolean touched = false;
+         
+         try {
+         }
+         finally {
+             touched = true;
+         }
+
+         assert touched , "finally not called with empty try"
+     }
+
+     void testWorkingMethod() {
+         try {
+	    	 workingMethod()
+	     }
+	     catch (AssertionError e) {
+		     onException(e)
+	     }
+	     finally {
+		     onFinally()
+	     }
+	     assert exceptionCalled == false , "should not invoked the catch clause"
+	     assert finallyCalled , "should have invoked the finally clause"
+	     println("After try/catch")
+    }
+    
+    void failingMethod() {
+        assert false , "Failing on purpose"
+	}
+	
+    void workingMethod() {
+        assert true , "Should never fail"
+    }
+    
+    void onException(e) {
+	    assert e != null
+	    exceptionCalled = true
+	}
+	
+    void onFinally() {
+        finallyCalled = true
+	}
+
+    void afterTryCatch() {
+        assert exceptionCalled , "should have invoked the catch clause"        
+        assert finallyCalled , "should have invoked the finally clause"
+        println("After try/catch")
+    }
+    
+    protected void setUp() {
+        exceptionCalled = false
+        finallyCalled = false
+    }
+    
+    void testTryWithReturnWithPrimitiveTypes() {
+      assert intTry() == 1
+      assert longTry() == 2
+      assert byteTry() == 3
+      assert shortTry() == 4
+      assert charTry() == "c"
+      assert floatTry() == 1.0
+      assert doubleTry() == 2.0
+    }
+    
+    int intTry(){
+      try {
+        return 1
+      } finally {}
+    }
+    
+    long longTry(){
+      try {
+        return 2
+      } finally {}
+    }
+    
+    byte byteTry(){
+      try {
+        return 3
+      } finally {}
+    }
+    
+    short shortTry(){
+      try {
+        return 4
+      } finally {}
+    }
+    
+    char charTry(){
+      try {
+        return 'c'
+      } finally {}
+    }
+    
+    float floatTry(){
+      try {
+        return 1.0
+      } finally {}
+    }
+    
+    double doubleTry(){
+      try {
+        return 2.0
+      } finally {}
+    }
+    
+    void testTryCatchWithUntyped(){
+      try {
+        throw new Exception();
+      } catch(e) {
+        assert true
+        return
+      }
+      assert false
+    }
+    
+    void testTryCatchInConstrcutor() {
+      // the super() call construction left an
+      // element on the stack, causing a inconsisitent
+      // stack height problem for the try-catch
+      // this ensures the stack is clean after the call
+      assertScript """
+        class A {
+          A() {
+            super()
+            try{}catch(e){}
+          }
+        }
+        assert null != new A()
+      """
+    }
+}
diff --git a/groovy/src/test/groovy/TypesafeMethodTest.groovy b/groovy/src/test/groovy/TypesafeMethodTest.groovy
new file mode 100644
index 0000000..e23c989
--- /dev/null
+++ b/groovy/src/test/groovy/TypesafeMethodTest.groovy
@@ -0,0 +1,14 @@
+package groovy
+
+class TypesafeMethodTest extends GroovyTestCase {
+
+    void testTypesafeMethod() {
+        def y = someMethod(1)
+
+        assert y == 2
+    }
+
+    Integer someMethod(Integer i) {
+        return i + 1
+    }
+}
diff --git a/groovy/src/test/groovy/UniqueOnCollectionTest.groovy b/groovy/src/test/groovy/UniqueOnCollectionTest.groovy
new file mode 100644
index 0000000..a4dcab2
--- /dev/null
+++ b/groovy/src/test/groovy/UniqueOnCollectionTest.groovy
@@ -0,0 +1,71 @@
+package groovy
+
+class UniqueOnCollectionTest extends GroovyTestCase {
+
+	void testUnique() {
+		def list = [-1, 0, 1, 1, 0, -1]
+        assert list.unique() == [-1, 0, 1]
+    }
+
+    void testUniqueOnListNoDupls() {
+    	assert [].unique() == []
+    	assert [1].unique() == [1]
+    	assert [1,2].unique() == [1,2]
+    	def a = [1,2]
+    	assert a.is(a.unique())
+    }
+
+    void testUniqueOnListOneDupl() {
+    	assert [1,1].unique() == [1]
+    	def a = [1,1]
+    	assert a.is(a.unique())
+    	assert [1,2,1].unique() == [1,2]
+    	assert [1,2,1,1].unique() == [1,2]
+    	assert [1,1,2].unique() == [1,2]
+    	assert [1,1,2,1].unique() == [1,2]
+    	assert [1,1,2,1,1].unique() == [1,2]
+    }
+
+    void testUniqueOnListTwoDupls() {
+    	assert [1,1,2,2].unique() == [1,2]
+    	def a = [1,1,2,2]
+    	assert a.is(a.unique())
+    	assert [1,2,1,2].unique() == [1,2]
+    	assert [1,2,1,1,2].unique() == [1,2]
+    	assert [1,1,2,2].unique() == [1,2]
+    	assert [1,1,2,1,2].unique() == [1,2]
+    	assert [1,1,2,2,1,1,2,2].unique() == [1,2]
+    }
+
+    void testUniqueOnOtherCollections() {
+    	def a = new HashSet([1,1])
+    	assert a.is(a.unique())
+    	assert 1 == a.size()
+    	a = new TreeSet([1,1])
+    	assert a.is(a.unique())
+    	assert 1 == a.size()
+    	a = new Vector([1,1])
+    	assert a.is(a.unique())
+    	assert 1 == a.size()
+    	a = new LinkedList([1,1])
+    	assert a.is(a.unique())
+    	assert 1 == a.size()
+    }
+
+    // GROOVY-1006
+    void testUniqueOnDifferentTypes() {
+    	def a = [1, 2, (short)1, 2L, 2.0]
+    	def b = a.unique()
+    	assert (b == a && a == [1, 2])
+    	a = [Math.PI, "foo", 1.0, 2L, (short)2, 2.0F]
+    	b = a.unique()
+    	assert (b == a && a == [Math.PI, "foo", 1.0, 2L])
+    }
+
+    // GROOVY-1956
+    void testUniqueOnListWithNulls() {
+    	def x = [1, 2, 3, 1, 2, 3, null, 'a', null]
+    	x.unique()
+    	assert x == [1, 2, 3, null, 'a']
+    }
+}
diff --git a/groovy/src/test/groovy/UniqueOnCollectionWithClosureTest.groovy b/groovy/src/test/groovy/UniqueOnCollectionWithClosureTest.groovy
new file mode 100644
index 0000000..d5634bf
--- /dev/null
+++ b/groovy/src/test/groovy/UniqueOnCollectionWithClosureTest.groovy
@@ -0,0 +1,22 @@
+package groovy
+
+/**
+ * @author Michael Baehr
+ */
+class UniqueOnCollectionWithClosureTest extends GroovyTestCase {
+
+    // GROOVY-1236
+    void testUniqueWithTwoParameterClosure() {
+        def list = [-1, 0, 1, 1, 0, -1]
+        def closure = {a,b -> Math.abs(a) <=> Math.abs(b)}
+        assert list.unique(closure) == [-1, 0]
+    }
+
+    // GROOVY-1236
+    void testUniqueWithOneParameterClosure() {
+        def list = [-1, 0, 1, 1, 0, -1]
+        def closure = {a -> Math.abs(a)}
+        assert list.unique(closure) == [-1, 0]
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/UniqueOnCollectionWithComparatorTest.groovy b/groovy/src/test/groovy/UniqueOnCollectionWithComparatorTest.groovy
new file mode 100644
index 0000000..d8ab0cf
--- /dev/null
+++ b/groovy/src/test/groovy/UniqueOnCollectionWithComparatorTest.groovy
@@ -0,0 +1,13 @@
+package groovy

+

+/** 

+ * @author Michael Baehr

+ */

+class UniqueOnCollectionWithComparatorTest extends GroovyTestCase {

+    

+    void testUniqueWithComparator() {

+        def list = [-1, 0, 1, 1, 0, -1]

+        def comparator = new ClosureComparator() {a,b -> Math.abs(a) <=> Math.abs(b)}

+        assert list.unique(comparator) == [-1, 0]

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/UnitTestAsScriptTest.groovy b/groovy/src/test/groovy/UnitTestAsScriptTest.groovy
new file mode 100644
index 0000000..bf0f93a
--- /dev/null
+++ b/groovy/src/test/groovy/UnitTestAsScriptTest.groovy
@@ -0,0 +1,4 @@
+a = 123
+a *= 2
+println "Running unit test with a = ${a}"
+assert a == 246
diff --git a/groovy/src/test/groovy/UnsafeNavigationTest.groovy b/groovy/src/test/groovy/UnsafeNavigationTest.groovy
new file mode 100644
index 0000000..ba122e5
--- /dev/null
+++ b/groovy/src/test/groovy/UnsafeNavigationTest.groovy
@@ -0,0 +1,16 @@
+package groovy
+
+class UnsafeNavigationTest extends GroovyTestCase {
+
+    void testUnsafePropertyNavigations() {
+        def x = null
+        
+        try {
+            def y = x.foo
+            fail("should fail")
+        }
+        catch (NullPointerException e) {
+            assert e != null
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/VArgsTest.groovy b/groovy/src/test/groovy/VArgsTest.groovy
new file mode 100644
index 0000000..016824b
--- /dev/null
+++ b/groovy/src/test/groovy/VArgsTest.groovy
@@ -0,0 +1,131 @@
+package groovy
+
+class VArgsTest extends GroovyTestCase {
+
+  def primitiveMethod(){0}
+  def primitiveMethod(int i) {1}  
+  def primitiveMethod(int i, int j) {2}
+  def primitiveMethod(int[] is) {10+is.length}
+  
+  void testPrimitiveMethod() {
+    assert primitiveMethod()==0
+    assert primitiveMethod(1)==1
+    assert primitiveMethod(1,1)==2
+    assert primitiveMethod(1,1,1)==13 
+    assert primitiveMethod([1,2,2,2] as int[])==14
+  }  
+
+  def doubleMethod(double[] id) {20+id.length}
+    
+  void testDoubleMethod() {
+    // with BigDecimal
+    assert doubleMethod()==20
+    assert doubleMethod(1.0G)==21
+    assert doubleMethod(1.0G,1.0G)==22
+    assert doubleMethod(1.0G,1.0G,1.0G)==23 
+    assert doubleMethod([1,2,2,2] as BigDecimal[])==24
+    
+    // with double
+    assert doubleMethod()==20
+    assert doubleMethod(1.0d)==21
+    assert doubleMethod(1.0d,1.0d)==22
+    assert doubleMethod(1.0d,1.0d,1.0d)==23 
+    assert doubleMethod([1,2,2,2] as double[])==24
+    
+  }
+  
+  def objectMethod(){0}
+  def objectMethod(Object i) {1}  
+  def objectMethod(Object i, Object j) {2}
+  def objectMethod(Object[] is) {10+is.length}
+  
+  void testObjectMethod() {
+    assert objectMethod()==0
+    assert objectMethod(1)==1
+    assert objectMethod(1,1)==2
+    assert objectMethod(1,1,1)==13
+    assert objectMethod([1,2,2,2] as Object[])==14
+  }
+  
+  def gstringMethod(GString[] gstrings){gstrings.length}
+  
+  void testGStringVargsMethod() {
+    def content = 1
+    def gstring ="$content"
+    assert gstringMethod() == 0
+    assert gstringMethod(gstring) == 1
+    assert gstringMethod(gstring,gstring,gstring) == 3
+    assert gstringMethod([gstring] as GString[]) == 1
+  }
+  
+  def stringMethod(String[] strings) {strings.length}
+  
+  void testStringMethod() {
+    def content = 1
+    def gstring ="$content"
+    assert stringMethod() == 0
+    assert stringMethod(gstring) == 1
+    assert stringMethod(gstring,gstring,gstring) == 3
+    assert stringMethod([gstring] as GString[]) == 1
+    assert stringMethod() == 0
+    assert stringMethod("a") == 1
+    assert stringMethod("a","a","a") == 3
+    assert stringMethod(["a"] as String[]) == 1
+  }
+  
+  //tests related to GROOVY-1807
+  
+  def overloadedMethod1(String s){1}
+  def overloadedMethod1(Object[] args){2}
+  
+  void testOverloadedMethod1() {
+    assert overloadedMethod1() == 2
+  }
+  
+  def overloadedMethod2(x,y){1}
+  def overloadedMethod2(x,Object... y){2}
+  
+  void testOverloadedMethod2() {
+    assert overloadedMethod2(null) == 2
+    assert overloadedMethod2("foo") == 2
+  }
+  
+  def normalVargsMethod(Object[] a){a.length}
+  
+  void testArrayCoercion() {
+    assert normalVargsMethod([1,2,3] as int[]) == 3
+  }
+  
+  
+  // GROOVY-2204
+  def m2204a(Map kwargs=[:], arg1, arg2, Object[] args) {
+    "arg1: $arg1, arg2: $arg2, args: $args, kwargs: $kwargs"
+  }
+
+  def m2204b(Map kwargs=[:], arg1, arg2="1", Object[] args) {
+    "arg1: $arg1, arg2: $arg2, args: $args, kwargs: $kwargs"
+  }
+
+  void test2204a() {
+     assert m2204a('hello', 'world') == 'arg1: hello, arg2: world, args: {}, kwargs: [:]'
+     assert m2204a('hello', 'world', 'from', 'list') == 'arg1: hello, arg2: world, args: {"from", "list"}, kwargs: [:]'
+     assert m2204a('hello', 'world', 'from', 'list', from: 'kwargs') == 'arg1: hello, arg2: world, args: {"from", "list"}, kwargs: ["from":"kwargs"]'
+     assert m2204a('hello', 'world', from: 'kwargs') == 'arg1: hello, arg2: world, args: {}, kwargs: ["from":"kwargs"]'
+     
+     assert m2204b('hello', 'world') == 'arg1: hello, arg2: 1, args: {"world"}, kwargs: [:]'
+     assert m2204b('hello', 'world', 'from', 'list') == 'arg1: hello, arg2: 1, args: {"world", "from", "list"}, kwargs: [:]'
+     assert m2204b('hello', 'world', 'from', 'list', from: 'kwargs') == 'arg1: hello, arg2: world, args: {"from", "list"}, kwargs: ["from":"kwargs"]'
+     assert m2204b('hello', 'world', from: 'kwargs') == 'arg1: hello, arg2: world, args: {}, kwargs: ["from":"kwargs"]'
+  }
+
+ 
+  // GROOVY-2351
+  
+  def m2351(Object... args)  {1}
+  def m2351(Integer... args) {2}
+ 
+  void test2351() {
+    assert m2351(1, 2, 3, 4, 5) == 2
+  }
+  
+}  
diff --git a/groovy/src/test/groovy/VarargsMethodTest.groovy b/groovy/src/test/groovy/VarargsMethodTest.groovy
new file mode 100644
index 0000000..6453c96
--- /dev/null
+++ b/groovy/src/test/groovy/VarargsMethodTest.groovy
@@ -0,0 +1,69 @@
+package groovy
+
+/** 
+ * VarargsMethodTest.groovy
+ *
+ *   1) Test to fix the Jira issues GROOVY-1023 and GROOVY-1026.
+ *   2) Test the feature that the length of arguments can be variable
+ *      when invoking methods with or without parameters.
+ *
+ * @author Dierk Koenig
+ * @author Pilho Kim
+ * @author Hein Meling
+ * @version $Revision$
+ */
+
+class VarargsMethodTest extends GroovyTestCase {  
+
+    void testVarargsOnly() {  
+        assertEquals 1, varargsOnlyMethod('')  
+        assertEquals 1, varargsOnlyMethod(1)  
+        assertEquals 2, varargsOnlyMethod('','')  
+        assertEquals 1, varargsOnlyMethod( ['',''] )  
+        assertEquals 2, varargsOnlyMethod( ['',''] as Object[])  
+        assertEquals 2, varargsOnlyMethod( *['',''] )  
+
+        // todo: GROOVY-1023
+        assertEquals 0, varargsOnlyMethod()
+
+        // todo: GROOVY-1026
+        assertEquals(-1, varargsOnlyMethod(null))
+        assertEquals(2, varargsOnlyMethod(null, null))
+     }  
+
+     Integer varargsOnlyMethod(Object[] args) {  
+         println("args = " + args)
+         // (1) todo: GROOVY-1023 (Java 5 feature)
+         //     If this method having varargs is invoked with no parameter,
+         //     then args is not null, but an array of length 0.
+         // (2) todo: GROOVY-1026 (Java 5 feature)
+         //     If this method having varargs is invoked with one parameter
+         //     null, then args is null, and so -1 is returned here.
+         if (args == null)
+               return -1
+         return args.size()  
+     }  
+  
+     void testVarargsLast() {  
+         assertEquals 0, varargsLastMethod('')  
+         assertEquals 0, varargsLastMethod(1)  
+         assertEquals 1, varargsLastMethod('','')  
+         assertEquals 2, varargsLastMethod('','','')  
+         assertEquals 1, varargsLastMethod('', ['',''] )  
+         assertEquals 2, varargsLastMethod('', ['',''] as Object[])  
+         assertEquals 2, varargsLastMethod('', *['',''] )  
+
+         // todo: GROOVY-1026
+         assertEquals(-1, varargsLastMethod('',null))
+         assertEquals(2, varargsLastMethod('',null, null))
+     }  
+  
+     Integer varargsLastMethod(Object first, Object[] args) {  
+         // (1) todo: GROOVY-1026 (Java 5 feature)
+         //     If this method having varargs is invoked with two parameters
+         //     1 and null, then args is null, and so -1 is returned here.
+         if (args == null)
+               return -1
+         return args.size()  
+     }  
+}  
diff --git a/groovy/src/test/groovy/VerbatimGStringTest.groovy b/groovy/src/test/groovy/VerbatimGStringTest.groovy
new file mode 100644
index 0000000..d5adca7
--- /dev/null
+++ b/groovy/src/test/groovy/VerbatimGStringTest.groovy
@@ -0,0 +1,60 @@
+package groovy
+
+class VerbatimGStringTest extends GroovyTestCase {
+
+    void testWithOneVariable() {
+        
+        def name = "Bob"
+        
+        def template = """
+hello ${name} how are you?
+"""
+
+        assert template instanceof GString
+
+        def count = template.getValueCount()
+        assert count == 1
+
+        def value = template.getValue(0)
+        assert value == "Bob"
+        assert template.getValue(0) == "Bob"
+
+        def string = template.toString().trim()
+        assert string == "hello Bob how are you?"
+    }
+    
+    void testWithVariableAtEnd() {
+        def name = "Bob"
+
+        def template = """
+hello ${name}
+"""
+
+        def string = template.toString().trim()
+        
+        assert string == "hello Bob"
+    }
+    
+    void testWithVariableAtBeginning() {
+        def name = "Bob"
+
+        def template = """
+${name} hey,
+hello
+"""
+        def string = template.toString().trim()
+        
+        assert fixEOLs(string) == "Bob hey,\nhello"
+    }
+
+    void testWithJustVariable() {
+        def name = "Bob"
+
+        def template = """
+${name}
+"""
+        def string = template.toString().trim()
+        
+        assert string == "Bob"
+    }
+}
diff --git a/groovy/src/test/groovy/WhileLoopTest.groovy b/groovy/src/test/groovy/WhileLoopTest.groovy
new file mode 100644
index 0000000..38f9e4b
--- /dev/null
+++ b/groovy/src/test/groovy/WhileLoopTest.groovy
@@ -0,0 +1,33 @@
+package groovy
+
+class WhileLoopTest extends GroovyTestCase {
+
+    void testVerySimpleWhile() {
+        def val = doWhileMethod(0, 5)
+        println(val)
+    }
+
+    void testMoreComplexWhile() {
+        def x = 0
+        def y = 5
+
+        while ( y > 0 ) {
+            x = x + 1
+            y = y - 1
+        }
+
+        assert x == 5
+    }
+
+    def doWhileMethod(x, m) {
+        while ( x < m ) {
+            x = increment(x)
+        }
+
+        return x
+    }
+
+    def increment(x) {
+        x + 1
+    }
+}
diff --git a/groovy/src/test/groovy/benchmarks/createLoop.groovy b/groovy/src/test/groovy/benchmarks/createLoop.groovy
new file mode 100644
index 0000000..b467d28
--- /dev/null
+++ b/groovy/src/test/groovy/benchmarks/createLoop.groovy
@@ -0,0 +1,7 @@
+c = {
+  for (i in 1..it){
+    x = new Object()
+  }
+}
+c.call(30000)
+
diff --git a/groovy/src/test/groovy/benchmarks/loop.groovy b/groovy/src/test/groovy/benchmarks/loop.groovy
new file mode 100644
index 0000000..ee30b72
--- /dev/null
+++ b/groovy/src/test/groovy/benchmarks/loop.groovy
@@ -0,0 +1,26 @@
+package groovy.benchmarks
+
+class Loop {
+  def array = new ArrayList()
+  def pos = 0
+
+  void push(obj){
+     array[pos] = obj
+     pos = pos + 1
+  }
+  Object pop(){
+     pos = pos - 1
+     return array[pos]
+  }
+
+  static void main(args){
+     def s = new Loop()
+     for (i in 1..1000000){
+       s.push(i)
+     }
+     for (i in 1..1000000){
+       s.pop()
+     }
+  }
+}
+
diff --git a/groovy/src/test/groovy/benchmarks/loop2.groovy b/groovy/src/test/groovy/benchmarks/loop2.groovy
new file mode 100644
index 0000000..8d38360
--- /dev/null
+++ b/groovy/src/test/groovy/benchmarks/loop2.groovy
@@ -0,0 +1,29 @@
+package groovy.benchmarks
+
+class Loop2 {
+  def array = new ArrayList()
+  def pos = 0
+
+  void push(obj){
+     array[pos] = obj
+     pos = pos + 1
+  }
+
+  Object pop(){
+     pos = pos - 1
+     return array[pos]
+  }
+
+  static void main(args){
+     println "Starting the Loop2 test"
+      
+     def s = new Loop2()
+     for (i in 1..1000000){
+       s.push(i)
+     }
+     for (i in 1..1000000){
+       s.pop()
+     }
+  }
+}
+
diff --git a/groovy/src/test/groovy/bugs/AmbigousListOrMethodTest.groovy b/groovy/src/test/groovy/bugs/AmbigousListOrMethodTest.groovy
new file mode 100644
index 0000000..ae73d24
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/AmbigousListOrMethodTest.groovy
@@ -0,0 +1,41 @@
+package groovy.bugs
+
+class AmbigousListOrMethodTest extends GroovyTestCase {
+
+    void testLocalVariableVersion() {
+        def foo = [3, 2, 3]
+
+        def val = foo [0]
+        println val
+        assert val == 3
+    }
+
+    void testUndefinedPropertyVersion() {
+        try {
+            def val = this.foo [0]
+            println val
+        }
+        catch (MissingPropertyException e) {
+            println "Worked! Caught missing property $e"
+        }
+    }
+
+    void testMethodCallVersion() {
+        def val = foo([0])
+        println val
+        assert val == 1
+    }
+
+
+    def foo(int val) {
+        println "Calling foo method with a int param of val"
+        println val
+        return null
+    }
+
+    def foo(List myList) {
+        println "Calling foo method with a list param of $myList"
+        return myList.size()
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ArrayMethodCallBug.groovy b/groovy/src/test/groovy/bugs/ArrayMethodCallBug.groovy
new file mode 100644
index 0000000..b8b31b6
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ArrayMethodCallBug.groovy
@@ -0,0 +1,13 @@
+package groovy.bugs
+
+class ArrayMethodCallBug extends TestSupport {
+
+    void testMethodCallingWithArrayBug() {
+        def array = getMockArguments()
+        
+        dummyMethod(array)
+    }
+    
+    protected void dummyMethod(array) {
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/AsBoolBug.groovy b/groovy/src/test/groovy/bugs/AsBoolBug.groovy
new file mode 100644
index 0000000..61dfa84
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/AsBoolBug.groovy
@@ -0,0 +1,71 @@
+package groovy.bugs
+
+/**
+ * Test to fix the Jira issues GROOVY-810 and GROOVY-811.
+ * Test of "string as Boolean" against the issue GROOVY-812.
+ *
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+
+public class AsBoolBug extends GroovyTestCase {
+
+    void testMapAsBool() {
+        def a = ["A":123]
+        println ("$a : ${a as Boolean}")
+        assert a as Boolean == true
+        a = [:]
+        println ("$a : ${a as Boolean}")
+        assert a as Boolean == false
+    }
+
+    void testListAsBool() {
+        def b = [123]
+        println ("$b : ${b as Boolean}")
+        assert b as Boolean == true
+        b = []
+        println ("$b : ${b as Boolean}")
+        assert b as Boolean == false
+    }
+
+    /**
+     * void testStringAsBool().
+     *
+     * <code>string as Boolean</code> is equivalent to
+     *     <code>string != null && string.length() > 0</code>.
+     */
+    // Unfortunately, it contradicts several other test cases, and
+    // it has already been decided to handle string-to-boolean conversions
+    // differently. Commented out temporarily on 10 May 2005.
+    // This is a test case against GROOVY-812
+    void testStringAsBool() {
+        def c = "false"
+        println ("$c : ${c as Boolean}")
+        assert c as Boolean == true
+        assert c as Boolean == (c != null && c.length() > 0)
+        boolean z = c
+        println ("$z")
+        assert z == true
+        if (c)
+           println "It is true!!"
+        else
+           println "It is false!!"
+
+        c = "123"
+        println ("$c : ${c as Boolean}")
+        assert c as Boolean == true
+        assert c as Boolean == (c != null && c.length() > 0)
+
+        c = "False"
+        println ("$c : ${c as Boolean}")
+        assert c as Boolean == true
+        assert c as Boolean == (c != null && c.length() > 0)
+        if (c)
+           println "It is true!!"
+        else
+           println "It is false!!"
+        z = c
+        println ("$z")
+        assert z
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/AssignmentInsideExpressionBug.groovy b/groovy/src/test/groovy/bugs/AssignmentInsideExpressionBug.groovy
new file mode 100644
index 0000000..a5735d4
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/AssignmentInsideExpressionBug.groovy
@@ -0,0 +1,30 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class AssignmentInsideExpressionBug extends GroovyTestCase {
+    
+    void testBug() {
+        def x
+        if ((x = someMethod()) != null) {
+            println x
+        }
+        def y
+        if ((y = getFoo()) > 5) {
+            println "y is greater than 5"
+        }
+        
+        def a = 123, b = 123
+        assert a == 123
+        assert b == 123
+    }
+
+    def someMethod() {
+        return "worked!"
+    }
+    
+    def getFoo() {
+        return 7
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/AttributeSetExpressionBug.groovy b/groovy/src/test/groovy/bugs/AttributeSetExpressionBug.groovy
new file mode 100644
index 0000000..0d84f51
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/AttributeSetExpressionBug.groovy
@@ -0,0 +1,28 @@
+/**
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+
+package groovy.bugs
+
+class AttributeSetExpressionBug extends GroovyTestCase {
+    void testAttributeSetAccess() {
+        def a = new HasStaticFieldSomeClass()
+        a.name = a.name * 3
+        assert a.@name == "gettter" * 3 
+        assert a.name == "gettter"
+
+        new HasStaticFieldSomeClass().@name = "changed bar"
+        assert( HasStaticFieldSomeClass.class.@name == "changed bar" )
+
+        HasStaticFieldSomeClass.class.@name = "changed static bar"
+        assert( HasStaticFieldSomeClass.class.@name == "changed static bar" )
+    }
+}
+
+class HasStaticFieldSomeClass {
+    static String name = "bar" 
+    static String getName() {
+        return "gettter"
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/AutoboxingOfComparisonsBug.groovy b/groovy/src/test/groovy/bugs/AutoboxingOfComparisonsBug.groovy
new file mode 100644
index 0000000..b68ebaf
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/AutoboxingOfComparisonsBug.groovy
@@ -0,0 +1,10 @@
+package groovy.bugs
+
+class AutoboxingOfComparisonsBug extends GroovyTestCase {
+    void testBug() {
+        def y = true
+        def x = y == true
+        def z = y != false
+        assert x && z
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/BadScriptNameBug.groovy b/groovy/src/test/groovy/bugs/BadScriptNameBug.groovy
new file mode 100644
index 0000000..c913737
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/BadScriptNameBug.groovy
@@ -0,0 +1,13 @@
+package groovy.bugs
+
+/**
+ * @author Sergey Udovenko 
+ * @version $Revision: 1.3 $
+ */
+class BadScriptNameBug extends GroovyTestCase {
+    
+    void testBug() {
+		GroovyClassLoader cl = new GroovyClassLoader(); 
+		cl.parseClass("println 'oops!'", "/script.groovy");
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/BenchmarkBug.groovy b/groovy/src/test/groovy/bugs/BenchmarkBug.groovy
new file mode 100644
index 0000000..135339f
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/BenchmarkBug.groovy
@@ -0,0 +1,32 @@
+package groovy.bugs
+
+/**
+ * A little performance test
+ * @version $Revision$
+ */
+class BenchmarkBug extends GroovyTestCase {
+    
+    void testPerformance() {
+        def start = System.currentTimeMillis()
+
+        def total = 0
+        def size = 10000
+        for (i in 0..size) {
+            total = total + callSomeMethod("hello", total)
+        }
+
+        def end = System.currentTimeMillis()
+
+        def time = end - start
+
+        println "Performed ${size} iterations in ${time / 1000} seconds which is ${time / size} ms per iteration"
+
+        // TODO: parser bug
+        // assert total == size * 10 + 10
+        assert total == 100010
+    }
+    
+    def callSomeMethod(text, total) {
+        return 10
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/BlockAsClosureBug.groovy b/groovy/src/test/groovy/bugs/BlockAsClosureBug.groovy
new file mode 100644
index 0000000..eb1dd60
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/BlockAsClosureBug.groovy
@@ -0,0 +1,51 @@
+package groovy.bugs
+
+/**
+ * @version $Revision: 1.4 $
+ */
+class BlockAsClosureBug extends GroovyTestCase {
+    
+   void testBug() {
+        def c = 0
+        
+        block: { 
+            c = 9 
+        } 
+
+        println(c) 
+        
+        assert c == 9
+    }
+    
+    void testStaticBug() {
+        staticMethod(null)		
+    }
+    
+    void testNonVoidMethod() {
+        foo()		
+    }
+    
+    static void staticMethod(args) {
+        def c = 0
+        
+        block: {
+            c = 9 
+        }
+
+        println(c) 
+        
+        assert c == 9
+    }
+    
+    def foo() {
+        def c = 0 
+        
+        block: { 
+            c = 9 
+        } 
+        println(c) 
+        
+        assert c == 9
+        return 5
+    }
+   }
diff --git a/groovy/src/test/groovy/bugs/BooleanBug.groovy b/groovy/src/test/groovy/bugs/BooleanBug.groovy
new file mode 100644
index 0000000..a1acd50
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/BooleanBug.groovy
@@ -0,0 +1,33 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class BooleanBug extends GroovyTestCase {
+    
+    void testBug() {
+        def x = new BooleanBean(name:'James', foo:true)
+        def y = new BooleanBean(name:'Bob', foo:false)
+
+        assert x.foo
+        assert ! y.foo
+        y.foo = true
+        assert y.foo
+    }
+    
+    void testBug2() {
+        BooleanBean bean = new BooleanBean(name:'Gromit', foo:false)
+        def value = isApplicableTo(bean)
+        assert value
+    }
+    
+    public boolean isApplicableTo(BooleanBean field) {
+        return !field.isFoo();
+    }
+
+}
+
+class BooleanBean {
+    String name
+    boolean foo
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ByteIndexBug.groovy b/groovy/src/test/groovy/bugs/ByteIndexBug.groovy
new file mode 100644
index 0000000..7922c82
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ByteIndexBug.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs
+
+/**
+ * @author Robert Fuller 
+ * @version $Revision$
+ */
+class ByteIndexBug extends GroovyTestCase {
+    // TODO: this tests a string with 128 nulls - is that what is intended?
+    void testBug() {
+        def sb = new StringBuffer("\"\"\"\n")
+        for (j in 0..127){ // 126 is okay.
+            sb.append('$').append("{x}")
+        }
+        sb.append("\n\"\"\"\n")
+        def b = new Binding(x:null)
+        new GroovyShell(b).evaluate(sb.toString(),"foo")
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/Bytecode2Bug.groovy b/groovy/src/test/groovy/bugs/Bytecode2Bug.groovy
new file mode 100644
index 0000000..b8ac503
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Bytecode2Bug.groovy
@@ -0,0 +1,55 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class Bytecode2Bug extends GroovyTestCase {
+
+    Integer count = 0
+    
+    void testBytecodeBug() {
+        getCollection().each { count += it }
+    }
+    
+    void testTedsBytecodeBug() {
+        //doTest(getCollection())
+        def a = [1, 2, 3, 4]
+        doTest(a)
+
+    }
+    
+    void doTest(args) {
+        def m = [:]
+        def i = 1
+        args.each { m.put(it, i++) }     
+        
+        assert m[1] == 1
+        assert m[2] == 2
+        assert m[3] == 3
+        assert m[4] == 4
+        
+        println("created: ${m}")
+        
+        assert i == 5
+    }
+    
+    
+    void testTedsBytecode2Bug() {
+        def m = [:]
+        def i = 1
+        getCollection().each { m.put(it, i++) }     
+        
+        assert m[1] == 1
+        assert m[2] == 2
+        assert m[3] == 3
+        assert m[4] == 4
+        
+        println("created: ${m}")
+        
+        assert i == 5
+    }
+    
+    def getCollection() {
+        [1, 2, 3, 4]
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/Bytecode3Bug.groovy b/groovy/src/test/groovy/bugs/Bytecode3Bug.groovy
new file mode 100644
index 0000000..01bb4c1
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Bytecode3Bug.groovy
@@ -0,0 +1,23 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class Bytecode3Bug extends GroovyTestCase {
+    
+    def count
+         
+    void testIncrementPropertyInclosure() {
+        def args = [1, 2, 3]
+        def m = [:]
+        count = 0
+        doLoop(args, m)
+        assert count == 3
+    }
+    
+    void doLoop(args, m) {
+        args.each { 
+            m.put(it, count++)
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/Bytecode4Bug.groovy b/groovy/src/test/groovy/bugs/Bytecode4Bug.groovy
new file mode 100644
index 0000000..089caaf
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Bytecode4Bug.groovy
@@ -0,0 +1,30 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class Bytecode4Bug extends GroovyTestCase {
+
+    def count = 0
+     
+    void testInject() {
+        def x = [1, 2, 3].inject(0) { c, s -> c += s }
+        assert x == 6
+    }
+     
+    void testUsingProperty() {
+        count = 0
+        getCollection().each { count += it }       
+        assert count == 10
+    }
+    
+    void testUsingIncrementingProperty() {
+        count = 0
+        getCollection().each { count++ }       
+        assert count == 4
+    }
+    
+    def getCollection() {
+        [1, 2, 3, 4]
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/Bytecode5Bug.groovy b/groovy/src/test/groovy/bugs/Bytecode5Bug.groovy
new file mode 100644
index 0000000..675eed8
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Bytecode5Bug.groovy
@@ -0,0 +1,17 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class Bytecode5Bug extends GroovyTestCase {
+
+    void testUsingLocalVar() {
+        def c = 0
+        getCollection().each { c += it }       
+        assert c == 10
+    }
+    
+    def getCollection() {
+        [1, 2, 3, 4]
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/Bytecode6Bug.groovy b/groovy/src/test/groovy/bugs/Bytecode6Bug.groovy
new file mode 100644
index 0000000..e9259f5
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Bytecode6Bug.groovy
@@ -0,0 +1,25 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class Bytecode6Bug extends GroovyTestCase {
+
+    void testPostFixReturn() {
+        def i = 1
+        def closure = { i++ }
+        def value = closure()
+        
+        assert value == 1
+        assert i == 2
+    }
+    
+    void testPreFixReturn() {
+        def i = 1
+        def closure = { return ++i }
+        def value = closure()
+        
+        assert value == 2
+        assert i == 2
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/Bytecode7Bug.groovy b/groovy/src/test/groovy/bugs/Bytecode7Bug.groovy
new file mode 100644
index 0000000..cad9e5d
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Bytecode7Bug.groovy
@@ -0,0 +1,27 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class Bytecode7Bug extends GroovyTestCase {
+
+    void testDuplicateVariables() {
+        if (true) {
+            def a = 123
+        }
+        if (true) {
+            def a = 456
+        }
+    }
+
+    void testDuplicateVariablesInClosures() {
+        def coll = [1]
+
+        coll.each {
+            def a = 123
+        }
+        coll.each {
+            def a = 456
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/BytecodeBug.groovy b/groovy/src/test/groovy/bugs/BytecodeBug.groovy
new file mode 100644
index 0000000..f43ca19
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/BytecodeBug.groovy
@@ -0,0 +1,30 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class BytecodeBug extends GroovyTestCase {
+     
+    void testTedsBytecodeBug() {
+        //def a = ['tom','dick','harry']
+        def a = [1, 2, 3, 4]
+        doTest(a)
+    }
+    
+    void doTest(args) {
+        def m = [:]
+        def i = 1
+        args.each { 
+            talk(it)
+            m.put(it, i++)
+        }
+        assert i == 5
+        m.each {
+            println(it)
+        }
+    }
+    
+    def talk(a) {
+        println("hello "+a)
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/CallingClosuresWithClosuresBug.groovy b/groovy/src/test/groovy/bugs/CallingClosuresWithClosuresBug.groovy
new file mode 100644
index 0000000..99f25b2
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/CallingClosuresWithClosuresBug.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class CallingClosuresWithClosuresBug extends GroovyTestCase {
+
+    void testBug() {
+        def a = {1}
+        // old workaround
+        //def b = {a.call()}
+        def b = {a()}
+        
+        def value = b()
+        
+        assert value == 1
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/CastWhenUsingClosuresBug.groovy b/groovy/src/test/groovy/bugs/CastWhenUsingClosuresBug.groovy
new file mode 100644
index 0000000..107d982
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/CastWhenUsingClosuresBug.groovy
@@ -0,0 +1,14 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class CastWhenUsingClosuresBug extends GroovyTestCase {
+
+    void testBug() {
+        def a = 1
+
+        def list = [1]
+        list.each { a = it }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ChristofsPropertyBug.groovy b/groovy/src/test/groovy/bugs/ChristofsPropertyBug.groovy
new file mode 100644
index 0000000..05791e6
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ChristofsPropertyBug.groovy
@@ -0,0 +1,17 @@
+package groovy.bugs
+
+/**
+ * @version $Revision: 1.4 $
+ */
+class ChristofsPropertyBug extends GroovyTestCase {
+     
+    def mixedCaseProperty
+
+    void testChristofsPropertyBug() {
+    	this.mixedCaseProperty = "test"
+    	shouldFail({this.mixedcaseproperty = "test"})
+    }
+    
+    def getMixedCaseProperty()    { mixedCaseProperty }
+    void setMixedCaseProperty(val) { this.mixedCaseProperty = val }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ClassGeneratorFixesTest.groovy b/groovy/src/test/groovy/bugs/ClassGeneratorFixesTest.groovy
new file mode 100644
index 0000000..15ebe55
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ClassGeneratorFixesTest.groovy
@@ -0,0 +1,78 @@
+package groovy.bugs
+
+
+class ClassGeneratorFixesTest extends GroovyTestCase {
+    def count = 0;
+
+    def pf(int p) {
+        int i = p
+        boolean b = true
+    }
+
+    void testPrimitvesInFunc() { // groovy-373, 453, 385, 451, 199
+        pf(10)
+    }
+
+    void testPlusEqual() { // 372
+        count += 1
+        assert count == 1
+
+        def foo =
+            {i->
+                return {j->
+                    i += j
+                    i
+                }
+            }
+        def x = foo(1)
+        x(5)
+        foo(3)
+        println x(2.3)
+    }
+
+    void testIfAndSwitchInClosure (){ // 321, 324, 412
+
+        def a = 1
+        1.times {
+            if (a ==1) {
+                a = 2
+            }
+        }
+
+        def noneYet=true;
+        ["a","b","c","d"].each { c ->
+          if (noneYet) {
+            noneYet=false;
+          } else {
+            print(" > ");
+          }
+          print( c );
+        }
+
+        a = 1
+        switch (a) {
+        case 1:
+            a = 2;
+        case 2:
+            break;
+        default:
+            break;
+        }
+    }
+
+    void returnVoid() {
+        return
+    }
+
+    void testReturnVoid() { // groovy-405, 387
+        returnVoid()
+    }
+    
+    void testBooleanValue() { // groovy-385
+            /** @todo
+            boolean value
+            */
+        }
+
+}
+
diff --git a/groovy/src/test/groovy/bugs/ClassInNamedParamsBug.groovy b/groovy/src/test/groovy/bugs/ClassInNamedParamsBug.groovy
new file mode 100644
index 0000000..9854e29
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ClassInNamedParamsBug.groovy
@@ -0,0 +1,20 @@
+package groovy.bugs
+
+class ClassInNamedParamsBug extends GroovyTestCase {
+    
+    void testBug() {
+        def foo = method(class:'cheese', name:'cheddar')
+        
+        assert foo.name == "cheddar"
+        assert foo.class == "cheese"
+        
+        foo = method(name:'cheddar', class:'cheese')
+        
+        assert foo.name == "cheddar"
+        assert foo.class == "cheese"
+    }
+    
+    def method(Map data) {
+        data
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ClassInScriptBug.java b/groovy/src/test/groovy/bugs/ClassInScriptBug.java
new file mode 100644
index 0000000..4ad7b68
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ClassInScriptBug.java
@@ -0,0 +1,61 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.bugs;
+
+import org.codehaus.groovy.classgen.TestSupport;
+
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ClassInScriptBug extends TestSupport {
+
+    public void testBug() throws Exception {
+        assertScript("class X {}\nx = new X()\nprintln(x)");
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/ClosureInClosureBug.groovy b/groovy/src/test/groovy/bugs/ClosureInClosureBug.groovy
new file mode 100644
index 0000000..1cbd865
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ClosureInClosureBug.groovy
@@ -0,0 +1,33 @@
+package groovy.bugs
+
+/**
+ * Bug illustrating the nested closures variable scope visibility issue.
+ * l.each is ClosureInClosureBug$1 and it.each is ClosureInClosureBug$2
+ * The variable text is not visible from ClosureInClosureBug$2.
+ * Indeed, a closure can only see the variable defined outside this closure (one level up)
+ * but cannot see what's in the second level.
+ *
+ * In order to make the test work, do not forget to uncomment the line "println(text)"
+ *
+ * @authour Guillaume Laforge
+ */
+class ClosureInClosureBug extends GroovyTestCase {
+
+    void testInvisibleVariable() {
+        def text = "test "
+
+        def l = [1..11, 2..12, 3..13, 4..14]
+
+        l.each {
+            //println(text)
+            it.each{
+                println(text)
+            }
+        }
+    }
+
+    static void main(args) {
+        def bug = new ClosureInClosureBug()
+        bug.testInvisibleVariable()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ClosureParameterPassingBug.groovy b/groovy/src/test/groovy/bugs/ClosureParameterPassingBug.groovy
new file mode 100644
index 0000000..ca72750
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ClosureParameterPassingBug.groovy
@@ -0,0 +1,43 @@
+package groovy.bugs
+
+import org.codehaus.groovy.classgen.TestSupport
+
+/**
+ * @author John Wilson
+ * @version $Revision$
+ */
+class ClosureParameterPassingBug extends TestSupport {
+    
+    void testBugInMethod() {
+        def c = { x ->
+            def y = 123
+            def c1 = {
+                println y
+                println x
+                println x[0]
+            }
+
+            c1()
+        }
+
+        c([1])
+    }
+
+    void testBug() {
+        assertScript """
+def c = { x ->
+    def y = 123
+    def c1 = { 
+        assert x != null , "Could not find a value for x"
+        assert y == 123 , "Could not find a value for y"
+        println x[0]
+    }
+
+    c1()
+} 
+
+c([1]) 
+"""
+    }
+   
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ClosureTypedVariableBug.groovy b/groovy/src/test/groovy/bugs/ClosureTypedVariableBug.groovy
new file mode 100644
index 0000000..7be2221
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ClosureTypedVariableBug.groovy
@@ -0,0 +1,54 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class ClosureTypedVariableBug extends GroovyTestCase {
+    
+    void testBug2() {
+        def count = makeClosure(0)
+        assert count == 1
+        
+        count = makeClosure2(0)
+        assert count == 1
+    }
+
+
+    def makeClosure(Number count) {
+        def closure = { count = it }
+        closure(1)
+        return count
+    }
+
+    def makeClosure2(Number c) {
+        def count = c
+        def closure = { count = it }
+        closure(1)
+        return count
+    }
+
+    void testBug() {
+        Integer count = 0
+        def closure = { count = it }
+        closure(1)
+        assert count == 1
+    }
+    
+    void testBug3() {
+        def closure = getElementClosure("p")
+        def answer = closure("b")
+        def value = answer("c")
+        println "returned : ${value}"
+    }
+    
+    Closure getElementClosure(tag) {
+        return { body ->
+            if (true) {
+                return {"${body}"}
+            }
+            else {
+                body = null
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ClosureVariableBug.groovy b/groovy/src/test/groovy/bugs/ClosureVariableBug.groovy
new file mode 100644
index 0000000..a08d318
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ClosureVariableBug.groovy
@@ -0,0 +1,43 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class ClosureVariableBug extends GroovyTestCase {
+    
+    void testClosurePassingBug() {
+        def count = 0
+        def closure = { assert count == it }
+        closure(0)
+        
+        count = 1
+        closure(1)
+    }
+    
+    void testPassingClosureAsNamedParameter() {
+        def x = 123
+        
+        def foo = new Expando(a:{x}, b:456)
+    
+        assert foo.a != null
+        
+        println "Foo has a = ${foo.a}"
+        
+        def value = foo.a()
+        assert value == 123
+    }
+    
+    void testBug() {
+        def value = callClosure([1, 2])
+        assert value == 2
+    }
+    
+    protected Integer callClosure(collection) {
+        Integer x
+        /** @todo
+        Integer x = 0
+        */
+        collection.each { x = it }
+        return x
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ClosureWithBitwiseDefaultParamTest.groovy b/groovy/src/test/groovy/bugs/ClosureWithBitwiseDefaultParamTest.groovy
new file mode 100644
index 0000000..21bc8ce
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ClosureWithBitwiseDefaultParamTest.groovy
@@ -0,0 +1,15 @@
+package groovy.bugs
+
+class ClosureWithBitwiseDefaultParamTest extends GroovyTestCase {
+    void testAmbiguousStuff() {
+        def c = { x, y = 1 | 2, z = 0->
+            println x
+            println y
+            println z
+        }
+
+        // now lets invoke c
+        // TODO when closures support default parameters
+        //c.call()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ClosureWithStaticVariablesBug.groovy b/groovy/src/test/groovy/bugs/ClosureWithStaticVariablesBug.groovy
new file mode 100644
index 0000000..5da8b5a
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ClosureWithStaticVariablesBug.groovy
@@ -0,0 +1,31 @@
+package groovy.bugs
+
+/**
+ * @version $Revision: 1.5 $
+ */
+class ClosureWithStaticVariablesBug extends TestSupport {
+    
+    static def y = [:]
+    
+    void testBug() {
+        def c = { x ->
+            return {
+                def foo = Cheese.z
+                println foo
+                assert foo.size() == 0
+
+                println y
+                assert y.size() == 0
+
+                return 6
+            }
+        }
+        def c2 = c(5)
+        def answer = c2()
+        assert answer == 6
+    }
+}
+
+class Cheese {
+    public static z = [:]
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ClosuresInScriptBug.java b/groovy/src/test/groovy/bugs/ClosuresInScriptBug.java
new file mode 100644
index 0000000..d1c86b5
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ClosuresInScriptBug.java
@@ -0,0 +1,61 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.bugs;
+
+import org.codehaus.groovy.classgen.TestSupport;
+
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ClosuresInScriptBug extends TestSupport {
+
+    public void testBug() throws Exception {
+        assertScript("a = 1\n [2].each { a = it }\n assert a == 2");
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/ConstructorBug.groovy b/groovy/src/test/groovy/bugs/ConstructorBug.groovy
new file mode 100644
index 0000000..f71c548
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ConstructorBug.groovy
@@ -0,0 +1,38 @@
+package groovy.bugs
+
+import org.codehaus.groovy.runtime.InvokerHelper
+
+/**
+ * @author Jason Thomas
+ * @version $Revision$
+ */
+class ConstructorBug extends GroovyTestCase {
+    
+    void testBug() {
+        def type = new GroovyClassLoader().parseClass(new File("src/test/groovy/bugs/TestBase.groovy"))
+        assert type != null
+
+        println "created type: ${type}"
+        
+        type = new GroovyClassLoader().parseClass(new File("src/test/groovy/bugs/TestDerived.groovy"))
+        assert type != null
+
+        println "created type: ${type} of type: ${type.class}"
+
+        def mytest = InvokerHelper.invokeConstructorOf(type, ["Hello"] as Object[])
+        assert mytest.foo == "Hello"
+        /** @todo fix bug
+        */
+        
+        /*
+        def test = type.newInstance()
+        assert test.foo == null
+        */
+        
+//foo = new type('hello')
+        /*
+        */
+        mytest = new TestDerived("Hello")
+        assert mytest.foo == "Hello"
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ConstructorParameterBug.groovy b/groovy/src/test/groovy/bugs/ConstructorParameterBug.groovy
new file mode 100644
index 0000000..f4fcf6c
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ConstructorParameterBug.groovy
@@ -0,0 +1,17 @@
+package groovy.bugs
+
+class ConstructorParameterBug extends GroovyTestCase {
+
+    void testMethodWithNativeArray() {
+        int[] value = [2*2]
+        println "${value} of type ${value.class}"
+        /** @todo fixme!
+    	blah2(value)
+    	*/
+    }
+
+    def blah2(int[] wobble) {
+       println(wobble)
+    }
+
+}
diff --git a/groovy/src/test/groovy/bugs/ConstructorThisCallBug.groovy b/groovy/src/test/groovy/bugs/ConstructorThisCallBug.groovy
new file mode 100644
index 0000000..e58a4c5
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ConstructorThisCallBug.groovy
@@ -0,0 +1,62 @@
+/**
+ * ConstructorThisCallBug.groovy
+ *
+ *     Test Script for the Jira issue: GROOVY-994.
+ *
+ * @author    Pilho Kim
+ * @date      2005.08.05.06.21
+ */
+
+package groovy.bugs
+
+public class ConstructorThisCallBug extends GroovyTestCase {
+    public void testCallA() {
+        println "Testing for a class without call()"
+        def a1 = new ConstructorCallA("foo") 
+        def a2 = new ConstructorCallA(9) 
+        def a3 = new ConstructorCallA() 
+    }
+
+    void testCallB() {
+        println "Testing for a class with call()"
+        def b1 = new ConstructorCallB('bar') 
+        def b2 = new ConstructorCallB(9) 
+        def b3 = new ConstructorCallB() 
+    }
+}
+
+public class ConstructorCallA { 
+    public ConstructorCallA() {
+        this(19)               // call another constructor
+        println "(1) no argument consructor"
+    } 
+
+    public ConstructorCallA(String a) {
+        println "(2) String value a = $a"
+    } 
+
+    public ConstructorCallA(int a) {
+        this("" + (a*a))       // call another constructor
+        println "(3) int value a = $a"
+    } 
+} 
+
+public class ConstructorCallB { 
+    public ConstructorCallB() {
+        println '1: no argument consructor'
+        this(19)              // call the method call()
+    } 
+
+    public ConstructorCallB(String b) {
+        println """2: String value b = $b"""
+    } 
+
+    public ConstructorCallB(int b) {
+        println """3: int value b = $b"""
+        this('' + (b + b))     // call the method call()
+    } 
+
+    void call(Object o) {
+        println "Hello, $o"
+    } 
+} 
diff --git a/groovy/src/test/groovy/bugs/CustomMetaClassTest.groovy b/groovy/src/test/groovy/bugs/CustomMetaClassTest.groovy
new file mode 100644
index 0000000..d12acf9
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/CustomMetaClassTest.groovy
@@ -0,0 +1,114 @@
+package groovy.bugs
+
+import org.codehaus.groovy.runtime.InvokerHelper
+
+class CustomMetaClassTest extends GroovyTestCase{
+    void testReplaceMetaClass() {
+        /*
+         * Constructing first instance before meta class replacment
+         * is made.
+         */
+        def firstInstance = "first"
+        assertEquals "first", firstInstance.toString()
+
+        def invoker = InvokerHelper.instance
+        def stored = invoker.metaRegistry.getMetaClass(String.class)
+        assert stored instanceof MetaClassImpl
+        def myMetaClass = new MyDelegatingMetaClass(String.class)
+        invoker.metaRegistry.removeMetaClass String.class
+        invoker.metaRegistry.setMetaClass(String.class, myMetaClass)
+
+        /*
+         * Constructing second instance after meta class replacment
+         * is made.
+         */
+        def secondInstance = "second"
+
+        /*
+         * Since we are replacing a meta class at the class level
+         * we are changing the behavior of the first and second
+         * instance of the string.
+         */
+        assertEquals "changed first", firstInstance.toString()
+        assertEquals "changed second", secondInstance.toString()
+
+        invoker.metaRegistry.removeMetaClass String.class
+        stored = invoker.metaRegistry.getMetaClass(String.class)
+        assert stored instanceof MetaClassImpl
+    }
+
+  void testNormalCreated () {
+      assertEquals groovy.runtime.metaclass.groovy.bugs.CustomMetaClassTestMetaClass, metaClass.class
+      assertEquals  MetaClassImpl, metaClass.delegate.class
+  }
+
+  void testEmcCreated () {
+    GroovySystem.metaClassRegistry.removeMetaClass metaClass.theClass
+    ExpandoMetaClass.enableGlobally()
+        metaClass = GroovySystem.metaClassRegistry.getMetaClass(CustomMetaClassTest)
+        assertTrue metaClass instanceof groovy.runtime.metaclass.groovy.bugs.CustomMetaClassTestMetaClass
+        assertEquals  ExpandoMetaClass, metaClass.delegate.class
+    ExpandoMetaClass.disableGlobally()
+
+    GroovySystem.metaClassRegistry.removeMetaClass metaClass.theClass
+    metaClass = null
+    assert getMetaClass() instanceof groovy.runtime.metaclass.groovy.bugs.CustomMetaClassTestMetaClass
+    assert MetaClassImpl == getMetaClass().delegate.class
+  }
+
+  void testStaticMetaClass () {
+      // Custom metaclass created
+      assertEquals  groovy.runtime.metaclass.groovy.bugs.CustomMetaClassTestMetaClass, metaClass.class
+      // delegated to MCImpl
+      assertEquals  MetaClassImpl, metaClass.delegate.class
+
+      MetaClass expandoMetaClass = CustomMetaClassTest.metaClass
+
+      // It still to be custom
+      assertEquals groovy.runtime.metaclass.groovy.bugs.CustomMetaClassTestMetaClass, expandoMetaClass.class
+      // But delegated to EMC
+      assertEquals  ExpandoMetaClass, expandoMetaClass.delegate.class
+
+      // But object still to hold reference to old one
+      assertEquals  MetaClassImpl, metaClass.delegate.class
+      // let give it chance to reinitialize
+      metaClass = null
+      // Now it should be OK
+      assertEquals  ExpandoMetaClass, expandoMetaClass.delegate.class
+
+      expandoMetaClass.toString = {
+          -> "I am modified"
+      }
+
+      assertEquals "I am modified", toString()
+
+      assertEquals "I am modified", metaClass.invokeMethod(this, "toString", null)
+
+      expandoMetaClass.static.toString = {
+          -> "I am modified static"
+      }
+
+      assertEquals "I am modified static", getClass().toString()
+      
+      GroovySystem.metaClassRegistry.removeMetaClass metaClass.theClass
+      metaClass = null
+      assert getMetaClass() instanceof groovy.runtime.metaclass.groovy.bugs.CustomMetaClassTestMetaClass
+      assert MetaClassImpl == getMetaClass().delegate.class
+  }
+
+}
+
+class MyDelegatingMetaClass extends groovy.lang.DelegatingMetaClass
+{
+    MyDelegatingMetaClass(final Class a_class)
+    {
+        super(a_class);
+        initialize()
+    }
+
+    public Object invokeMethod(Object a_object, String a_methodName, Object[] a_arguments)
+    {
+        return "changed ${super.invokeMethod(a_object, a_methodName, a_arguments)}"
+    }
+}
+
diff --git a/groovy/src/test/groovy/bugs/DefVariableBug.groovy b/groovy/src/test/groovy/bugs/DefVariableBug.groovy
new file mode 100644
index 0000000..3b7545e
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/DefVariableBug.groovy
@@ -0,0 +1,21 @@
+package groovy.bugs
+
+/**
+ * @version $Revision: 1.3 $
+ */
+class DefVariableBug extends GroovyTestCase {
+    
+    void testBug() {
+
+     /* cpoirier - "def" can be refered to as a variable name,
+        but cannot be declared as one (due to ambiguities)
+
+        def = 123
+        
+        assert def == 123
+     */
+        
+        def foo = new Expando(a:123, def:456)
+        assert foo.def == 456
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/DoubleSizeParametersBug.groovy b/groovy/src/test/groovy/bugs/DoubleSizeParametersBug.groovy
new file mode 100644
index 0000000..e41a994
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/DoubleSizeParametersBug.groovy
@@ -0,0 +1,68 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.bugs;
+
+
+/**
+ * @author <a href="mailto:jstrachan@protique.com">James Strachan</a>
+ * @version $Revision$
+ */
+class DoubleSizeParametersBug extends TestSupport {
+
+    void testBug() {
+        assertScript( """
+def foo(double x, y) {
+   println "x: "+x
+   println "y: "+y
+}
+
+foo(10.0d, 0)
+""" );
+    }
+}
+
+
diff --git a/groovy/src/test/groovy/bugs/ForAndSqlBug.groovy b/groovy/src/test/groovy/bugs/ForAndSqlBug.groovy
new file mode 100644
index 0000000..752c715
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ForAndSqlBug.groovy
@@ -0,0 +1,40 @@
+package groovy.bugs
+
+import groovy.sql.TestHelper
+
+/**
+ * @author Jonathan Carlson
+ * @version $Revision$
+ */
+class ForAndSqlBug extends GroovyTestCase {
+    
+    void testBugInNormalMethod() {
+        def sql = TestHelper.makeSql()
+        
+        def li = ["a", "b"]
+        for (x in li) {
+            sql.eachRow("SELECT count(*) FROM FOOD") { e ->
+            	println " ${x}"
+
+	            assert x != null
+            }
+        }
+    }
+
+    void testBugInsideScript() {
+        assertScript( """
+import groovy.sql.TestHelper
+def sql = TestHelper.makeSql()
+
+def li = ["a", "b"]
+for (x in li) {
+    sql.eachRow("SELECT count(*) FROM FOOD") { e ->
+    	println " \${x}"
+    	
+    	assert x != null
+    }
+}
+""")        
+	}
+
+}
diff --git a/groovy/src/test/groovy/bugs/ForLoopBug.groovy b/groovy/src/test/groovy/bugs/ForLoopBug.groovy
new file mode 100644
index 0000000..ccf6645
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ForLoopBug.groovy
@@ -0,0 +1,66 @@
+package groovy.bugs
+
+/**
+ * @author John Wilson
+ * @version $Revision$
+ */
+class ForLoopBug extends GroovyTestCase {
+    
+    void testBug() {
+        assertScript( """
+def list = []
+def a = 1
+def b = 5
+for (c in a..b) {
+    list << c
+}
+assert list == [1, 2, 3, 4, 5]
+""")
+    }
+    
+    void testSeansBug() {
+        assertScript( """
+for (i in 1..10) {
+    println i
+}
+""")        
+    }
+
+    void testNormalMethod() {
+        def list = []
+        def a = 1
+        def b = 5
+        for (c in a..b) {
+            list << c
+        }
+        assert list == [1, 2, 3, 4, 5]
+    }
+    
+     void testBytecodeGenBug() {
+        def a = 1
+        def b = 5
+
+        def lastIndex
+        for (i in a..b) {
+            println i
+            lastIndex = i
+        }
+        a = lastIndex
+        
+		assert a == 5
+    }
+
+
+    void testVisibility() {
+        assertScript( """
+
+def array = [ true, true, true ];
+for( boolean i in array ) {
+   1.times {
+       assert i == true;
+   }
+}
+""")
+    }
+
+}
diff --git a/groovy/src/test/groovy/bugs/FullyQualifiedClassBug.groovy b/groovy/src/test/groovy/bugs/FullyQualifiedClassBug.groovy
new file mode 100644
index 0000000..5af8f9f
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/FullyQualifiedClassBug.groovy
@@ -0,0 +1,12 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class FullyQualifiedClassBug extends GroovyTestCase {
+
+    void testBug() {
+        java.lang.System.err.println("Hello world")
+    }
+    
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/FullyQualifiedMethodReturnTypeBug.groovy b/groovy/src/test/groovy/bugs/FullyQualifiedMethodReturnTypeBug.groovy
new file mode 100644
index 0000000..fd78741
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/FullyQualifiedMethodReturnTypeBug.groovy
@@ -0,0 +1,17 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class FullyQualifiedMethodReturnTypeBug extends GroovyTestCase {
+
+    void testBug() {
+        def s = foo()
+        assert s.length() == 3
+    }
+
+    java.lang.String foo() {
+        return "hey"
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/FullyQualifiedVariableTypeBug.groovy b/groovy/src/test/groovy/bugs/FullyQualifiedVariableTypeBug.groovy
new file mode 100644
index 0000000..a919176
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/FullyQualifiedVariableTypeBug.groovy
@@ -0,0 +1,13 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class FullyQualifiedVariableTypeBug extends GroovyTestCase {
+
+    void testBug() {
+        java.lang.String s = "hey"
+        assert s.length() == 3
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/GetterBug.groovy b/groovy/src/test/groovy/bugs/GetterBug.groovy
new file mode 100644
index 0000000..74224e0
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/GetterBug.groovy
@@ -0,0 +1,66 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class GetterBug extends GroovyTestCase {
+     
+    String foo
+    def bar
+
+    String getFoo() {
+    	if (foo == null) { 
+    		foo = "James"
+    	}
+    	return foo
+    }
+    
+    void setFoo(String foo) {
+    	this.foo = foo
+   	}
+    
+    void testTypedGetterAndSetter() {
+    	println "Running test"
+    	
+    	def value = getFoo()
+    	
+    	println "Value is ${value}"
+    	
+    	assert value == "James"
+    	
+    	setFoo("Bob")
+    	
+    	value = getFoo()
+    	
+    	assert value == "Bob"
+    }
+    
+    def getBar() {
+    	if (this.bar == null) {
+    		this.bar = "James"
+    	}
+    	bar
+    }
+    
+    void setBar(bar) {
+    	this.bar = bar
+    }
+    
+    
+    void testUntypedGetterAndSetter() {
+    	println "Running test"
+    	
+    	def value = getBar()
+    	
+    	println "Value is ${value}"
+    	
+    	assert value == "James"
+    	
+    	setBar("Bob")
+    	
+    	value = getBar()
+    	
+    	assert value == "Bob"
+    }
+    
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/Groovy1018_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy1018_Bug.groovy
new file mode 100644
index 0000000..6e0489a
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy1018_Bug.groovy
@@ -0,0 +1,33 @@
+package groovy.bugs
+
+/**
+ * Test to fix the Jira issues GROOVY-1018 and GROOVY-732.
+ * Access to a static field member by a class name:
+ *      ClassName.fieldName or ClassName.@fieldName.
+ *
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+
+class Groovy1018_Bug extends GroovyTestCase { 
+
+    public static Object Class = "bar" 
+
+    // todo: GROOVY-1018
+    void testGetPublicStaticField() {
+        def a = new Groovy1018_Bug()
+        println( a.Class )
+        println( a.@Class )
+        println( Groovy1018_Bug.Class )
+        println( Groovy1018_Bug.@Class )
+        assert a.Class == "bar" && a.@Class == "bar"
+        assert Groovy1018_Bug.Class == "bar" && Groovy1018_Bug.@Class == "bar"
+    }
+
+    // todo: GROOVY-732
+    void testSetPublicStaticField() {
+        Groovy1018_Bug.Class = 'bar-'
+        assert Groovy1018_Bug.Class == "bar-" && Groovy1018_Bug.@Class == "bar-"
+    }
+
+} 
diff --git a/groovy/src/test/groovy/bugs/Groovy1059_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy1059_Bug.groovy
new file mode 100644
index 0000000..5608d02
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy1059_Bug.groovy
@@ -0,0 +1,53 @@
+package groovy.bugs
+
+/**
+ * TODO: GROOVY-1059
+ *
+ *    Accessible to a closure attribute of an abject with the operator ".@".
+ *    For examples, all of the expressions
+ *
+ *            object.@closure()
+ *            object.@closure.call()
+ *            object.@closure.doCall()
+ *            (object.@closure)()
+ *
+ *    have the same meaning.
+ *
+ * @author  John Wilson
+ * @author  Pilho Kim
+ */
+
+class Groovy1059_Bug extends GroovyTestCase {
+
+    void testClosureAsAttribute() {
+        def x = new Groovy1059Foo()
+
+        println( x.say() )
+        println( (x.@say)() )
+        println( x.@say() )  // TODO: Groovy-1059 should work
+        println( x.@say.call() )
+        println( x.@say.doCall() )
+        println( x.@say2() )
+
+        assert "I am a Method" == x.say()
+        assert "I am a Method" == x.@say2()
+        assert "I am a Closure" == (x.@say)()
+        assert "I am a Closure" == x.@say()
+        assert x.@say() == (x.@say)()
+        assert x.@say() == x.@say.call()
+        assert x.@say() == x.@say.doCall()
+        assert x.@say() != x.say()
+        assert x.@say2() == x.say()
+    }
+
+}
+
+class Groovy1059Foo {
+
+    def public say = { it -> return "I am a Closure" }
+    def public say2 = this.&say
+
+    public Object say() {
+       return "I am a Method"
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy1081_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy1081_Bug.groovy
new file mode 100644
index 0000000..ba8f40d
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy1081_Bug.groovy
@@ -0,0 +1,20 @@
+package groovy.bugs
+
+/**
+ *  Verifies that DefaultGroovyMethods.transformLine(Reader, Writer, Closure)
+ *  actually writes its output.
+ */
+
+class Groovy1081_Bug extends GroovyTestCase {
+ 
+    void testShort() {
+     	def reader = new StringReader('abc')
+		def writer = new StringWriter()
+
+		reader.transformLine(writer) { it }
+		
+		// Implementation was creating a BufferedWriter, but not flushing it
+		assertTrue(writer.toString().startsWith('abc'))
+    }
+    
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy1407_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy1407_Bug.groovy
new file mode 100644
index 0000000..0425f3c
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy1407_Bug.groovy
@@ -0,0 +1,25 @@
+package groovy.bugs

+

+class Groovy1407_Bug extends GroovyTestCase {

+   void testGPathOnMultiKeyMap(){

+      // each key is a two-element String list

+      // each value is a two-element integer list

+      def map = [['a','b']:[2,34],['c','d']:[2,16],['e','f']:[3,97],['g','h']:[4,48]]

+      def expected = [["a", "b"],["c", "d"],["e", "f"],["g", "h"]]

+      // previous returned value was [a, b, g, h, e, f, c, d]

+      // i.e, expanded

+      def actual = map.entrySet().key

+      assert expected == actual

+   }

+

+   void testGPathOnMultiValueMap(){

+      // each key is a two-element String list

+      // each value is a two-element integer list

+      def map = [['a','b']:[2,34],['c','d']:[2,16],['e','f']:[3,97],['g','h']:[4,48]]

+      def expected = [[2, 34],[2, 16],[3, 97],[4, 48]]

+      // previous returned value was [2, 34, 4, 48, 3, 97, 2, 16]

+      // i.e, expanded

+      def actual = map.entrySet().value

+      assert expected == actual

+   }

+}

diff --git a/groovy/src/test/groovy/bugs/Groovy1567_Bug.java b/groovy/src/test/groovy/bugs/Groovy1567_Bug.java
new file mode 100644
index 0000000..0129311
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy1567_Bug.java
@@ -0,0 +1,30 @@
+package groovy.bugs;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyShell;
+import groovy.util.GroovyScriptEngine;
+import groovy.util.ResourceException;
+import groovy.util.ScriptException;
+import java.io.File;
+import java.io.IOException;
+import junit.framework.TestCase;
+
+public class Groovy1567_Bug extends TestCase {
+   public void testGroovyScriptEngineVsGroovyShell() throws IOException, ResourceException, ScriptException {
+      // @TODO refactor this path	   
+      File currentDir = new File("./src/test/groovy/bugs");	   
+      String file = "bug1567_script.groovy";
+	   
+      Binding binding = new Binding();
+      GroovyShell shell = new GroovyShell(binding);
+      String[] test = null;
+      Object result = shell.run( new File(currentDir,file), test );
+
+      String[] roots = new String[] { currentDir.getAbsolutePath() };
+      GroovyScriptEngine gse = new GroovyScriptEngine(roots);
+      binding = new Binding();
+      // a MME was ensued here stating no 't.start()' was available
+      // in the script
+      gse.run( file, binding );
+   }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy1593.groovy b/groovy/src/test/groovy/bugs/Groovy1593.groovy
new file mode 100644
index 0000000..1e110f5
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy1593.groovy
@@ -0,0 +1,14 @@
+package groovy.bugs

+

+class Groovy1593 extends GroovyTestCase {

+   void testPropertyAccessInSubClassOfHashMap() {

+      def subclass = new SubClassOfHashMap()

+      // any of the following caused a MPE previously

+      assertNull subclass.property

+      subclass.property = "value"

+      assert "value" == subclass.property

+   }

+

+}

+

+class SubClassOfHashMap extends HashMap {}

diff --git a/groovy/src/test/groovy/bugs/Groovy1617_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy1617_Bug.groovy
new file mode 100644
index 0000000..5c24d67
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy1617_Bug.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs

+

+class Groovy1617_Bug extends GroovyTestCase {

+   void testCoerceStringIntoStringArray() {

+      def expected = ["G","r","o","o","v","y"] as String[]

+      def actual = "Groovy" as String[]

+      assert expected == actual

+   }

+

+   void testCoerceGStringIntoStringArray() {

+      def expected = ["G","r","o","o","v","y"] as String[]

+      def a = "Gro"

+      def b = "ovy"

+      // previously returned ["Groovy"]

+      def actual = "$a$b" as String[]

+      assert expected == actual

+   }

+}

diff --git a/groovy/src/test/groovy/bugs/Groovy1706_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy1706_Bug.groovy
new file mode 100644
index 0000000..3440e12
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy1706_Bug.groovy
@@ -0,0 +1,23 @@
+package groovy.bugs

+

+class Groovy1706_Bug extends GroovyTestCase {

+   void testStaticMethodIsCalledFromSubclass() {

+      // disclaimer: static methods shouldn't be

+      // called on instances

+      Groovy1706A a = new Groovy1706A()

+      Groovy1706B b = new Groovy1706B()

+      assert "A" == a.doit()

+      assert "B" == b.doit()

+   }

+

+   void testStaticMethodIsCalledInCorrectInstance() {

+      // disclaimer: static methods shouldn't be

+      // called on instances

+      Groovy1706A i = new Groovy1706B()

+      assert "B" == i.doit()

+      // in Java the answer would be "A"

+   }

+}

+

+class Groovy1706A { static doit() { "A" } }

+class Groovy1706B extends Groovy1706A { static doit() { "B" } }

diff --git a/groovy/src/test/groovy/bugs/Groovy2339Bug.groovy b/groovy/src/test/groovy/bugs/Groovy2339Bug.groovy
new file mode 100644
index 0000000..db3a91e
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy2339Bug.groovy
@@ -0,0 +1,17 @@
+package groovy.bugs

+

+class Groovy2339Bug extends GroovyTestCase {

+

+    void testBug () {

+        List list = ['groovy', 'java']

+        Map map = [a: 1, b: 2]

+        

+		shouldFail (MissingMethodException) {

+	        list.each {

+	            map.keySet().each {Date d ->

+	                println d

+	            }

+	        }

+        }

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/Groovy2348Bug.groovy b/groovy/src/test/groovy/bugs/Groovy2348Bug.groovy
new file mode 100644
index 0000000..15f395c
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy2348Bug.groovy
@@ -0,0 +1,24 @@
+package groovy.bugs
+
+class Groovy2348Bug extends GroovyTestCase{
+  void test () {
+     assertEquals( ['1.0', '2.0'], Foo.test(['1.0-vers', '2.0-subvers']))
+  }
+}
+
+class Foo {
+
+    private static test(tokens) {
+        tokens.collect {
+            trimTag(it) 
+        }
+    }
+
+    private static trimTag(pluginVersion) {
+        int i = pluginVersion.indexOf('-')
+        if(i > 0) {
+            pluginVersion = pluginVersion[0..i-1]
+        }
+        pluginVersion
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy2350Bug.groovy b/groovy/src/test/groovy/bugs/Groovy2350Bug.groovy
new file mode 100644
index 0000000..31865f1
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy2350Bug.groovy
@@ -0,0 +1,46 @@
+package groovy.bugs
+
+class Groovy2350Bug extends GroovyTestCase{
+
+     void testNoArg () {
+         shouldFail (org.codehaus.groovy.runtime.metaclass.MethodSelectionException) {
+             def a = new DefaultNoArgCtor()
+             println a
+         }
+
+         assertEquals "NULL", new DefaultNoArgCtor2().value
+     }
+
+     void testNoDefCtor () {
+         def a = new NoDefaultCtor("first")
+         assertEquals "toS: first", a.toString()
+
+         def b = new NoDefaultCtor()
+         assertEquals "toS: null", b.toString()
+     }
+}
+
+class NoDefaultCtor {
+    def field
+
+    NoDefaultCtor(param) { field= param }
+
+    String toString() {
+      return "toS: ${field}"
+    }
+}
+
+
+class DefaultNoArgCtor {
+  DefaultNoArgCtor(String s) {}
+
+  DefaultNoArgCtor(int s) {}
+}
+
+class DefaultNoArgCtor2 {
+  String value
+
+  DefaultNoArgCtor2(String s) {
+      value = s ? s : "NULL"
+  }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy2351Bug.groovy b/groovy/src/test/groovy/bugs/Groovy2351Bug.groovy
new file mode 100644
index 0000000..feabb7a
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy2351Bug.groovy
@@ -0,0 +1,16 @@
+package groovy.bugs
+
+class Groovy2351Bug extends GroovyTestCase {
+   void testVarArgs () {
+
+       def a = new VarArgs()
+       assertEquals( "method with Integer", a.method(1, 2, 3, 4, 5))
+       assertEquals( "method with Objects", a.method("", 2, "22", 4, 5))
+   }
+}
+
+class VarArgs {
+    def method(Object... args) { "method with Objects" }
+
+    def method(Integer... args) { "method with Integer" }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy239_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy239_Bug.groovy
new file mode 100644
index 0000000..246a06d
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy239_Bug.groovy
@@ -0,0 +1,42 @@
+package groovy.bugs
+
+/**
+ * @author John Wilson
+ * @version $Revision$
+ */
+class Groovy239_Bug extends GroovyTestCase {
+    
+    void testBug() {
+        def a = makeClosure()
+        def b = makeClosure()
+        def c = makeClosure()
+
+        a() {
+            println("A")
+            b() {
+                println("B")
+                c() {
+                    println("C")
+                }
+            }
+        }
+    }
+
+    def makeClosure() {
+        return { it() }
+    }
+
+    void testBug2() {
+        def a = { it() }
+        def b = { it() }
+        def c = { it() }
+
+        a() {
+            b() {
+                c() {
+                }
+            }
+        }
+    }
+   
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/Groovy249_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy249_Bug.groovy
new file mode 100644
index 0000000..3fe96bb
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy249_Bug.groovy
@@ -0,0 +1,65 @@
+package groovy.bugs
+
+import groovy.xml.MarkupBuilder
+
+/**
+ * @author Merrick Schincariol 
+ * @version $Revision$
+ */
+class Groovy249_Bug extends GroovyTestCase {
+
+    void testBug() {
+		def t = new Bean249()
+		t.b = "hello"
+		println t.b
+		println "test: ${t.b}"
+		
+		def xml = new MarkupBuilder()
+		def root = xml.foo {
+			bar {
+				// works
+				baz("test")
+				// fails
+				baz(t.b)
+				// fails
+				baz("${t.b}")
+			}
+		} 
+	}
+	
+/** @todo don't know why this fails
+
+    void testBugInScript() {
+    	assertScript <<<EOF
+			import groovy.xml.MarkupBuilder;
+			
+			class Bean {
+				String b
+			};
+			
+			def t = new Bean()
+			t.b = "hello"
+			println t.b
+			println "test: ${t.b}"
+			
+			def xml = new MarkupBuilder()
+			root = xml.foo {
+				bar {
+					// works
+					baz("test")
+					// fails
+					baz(t.b)
+					// fails
+					baz("${t.b}")
+				}
+			} 
+
+EOF    	
+	}
+*/
+   
+}
+
+class Bean249 {
+	String b
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy252_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy252_Bug.groovy
new file mode 100644
index 0000000..af87240
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy252_Bug.groovy
@@ -0,0 +1,38 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class Groovy252_Bug extends GroovyTestCase {
+    
+    def count = 0
+    
+    void testBug() {
+        def value = f()
+        assert value == null
+        
+        value = g()
+        assert value == null
+        
+        value = h()
+        assert value == null
+    }
+    
+    
+    def f() {
+         if (count++ == 5)
+            return null
+         else
+            return null
+    } 
+    
+    def g() {
+         ++count
+         return null
+    } 
+    
+    def h() {
+         ++count
+         return null
+    } 
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy278_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy278_Bug.groovy
new file mode 100644
index 0000000..2b698be
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy278_Bug.groovy
@@ -0,0 +1,21 @@
+package groovy.bugs
+
+/**
+ * @author John Wilson
+ * @version $Revision$
+ */
+class Groovy278_Bug extends GroovyTestCase {
+    
+    void testBug() {
+        def value = new MyRange()
+        println value
+        assert value != null
+    }
+}
+
+
+class MyRange extends IntRange {
+    MyRange() {
+        super(1, 2)
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/Groovy303_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy303_Bug.groovy
new file mode 100644
index 0000000..9f696f0
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy303_Bug.groovy
@@ -0,0 +1,47 @@
+package groovy.bugs
+
+import java.awt.*
+import java.awt.event.*
+import javax.swing.*
+
+
+/**
+ * @author Bing Ran
+ * @author Andy Dwelly
+ * @version $Revision$
+ */
+class Groovy303_Bug extends GroovyTestCase {
+    
+    void testBug() {
+        try {
+            def scholastic = new Scholastic()
+               scholastic.createUI()
+           }
+           catch (HeadlessException e) {
+               // called from a non-UI environment
+           }
+    }
+}
+
+
+class Scholastic implements ActionListener {
+
+    void createUI() {
+       println('createUI called')
+       def frame = new JFrame("Hello World")
+       def contents = frame.getContentPane()
+       def pane = new JPanel()
+       pane.setLayout(new BorderLayout())
+       def button = new JButton("A button")
+       button.addActionListener(this)
+       pane.add(button, BorderLayout.CENTER)
+       contents.add(pane)
+       frame.setSize(100, 100)
+       //frame.setVisible(true)
+       button.doClick()
+    }
+
+    public void actionPerformed(ActionEvent event) {
+       println "hello"
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy308_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy308_Bug.groovy
new file mode 100644
index 0000000..8ebaaee
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy308_Bug.groovy
@@ -0,0 +1,26 @@
+package groovy.bugs
+
+import java.io.*
+
+/**
+ * @version $Revision$
+ */
+class Groovy308_Bug extends GroovyTestCase {
+    
+    void testBug() {
+    	def out = new StringWriter()
+    	out << "hello " << "world!"
+    	
+    	def value = out.toString()
+    	assert value == "hello world!"
+    	
+    	out = new ByteArrayOutputStream()
+    	out << "hello " << "world!"
+
+		value = new String(out.toByteArray())
+		assert value == "hello world!"
+    	    	
+    	System.out << "hello" << " world!"
+    }
+}
+
diff --git a/groovy/src/test/groovy/bugs/Groovy325_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy325_Bug.groovy
new file mode 100644
index 0000000..b3a1454
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy325_Bug.groovy
@@ -0,0 +1,12 @@
+package groovy.bugs
+
+class Groovy325_Bug extends GroovyTestCase {
+  static boolean staticMethod() {
+    return true
+  }
+
+  void testCallStaticMethodFromClosure() {
+    def c = { staticMethod() }
+    assert c()
+  }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/Groovy389_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy389_Bug.groovy
new file mode 100644
index 0000000..88275b2
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy389_Bug.groovy
@@ -0,0 +1,23 @@
+package groovy.bugs
+
+/**
+ *  Verifies that closures work inside case blocks.
+ *
+ */
+
+class Groovy389_Bug extends GroovyTestCase {
+ 
+    void testBug() {
+       def a = [10, 11, 12]
+       def b = 0
+       
+       switch( "list" ) {
+          case "list":
+             a.each { b = b + 1 }
+             break
+       }
+
+       assert b == 3
+    }
+
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy513_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy513_Bug.groovy
new file mode 100644
index 0000000..d67904e
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy513_Bug.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs

+

+/**

+ *  Verifies that comparisons to Integer.MIN_VALUE work

+ */

+

+class Groovy513_Bug extends GroovyTestCase {

+ 

+    void testMinMaxValueComparison() {

+    	assertTrue(8 < Integer.MAX_VALUE);

+    	assertTrue(8 > Integer.MIN_VALUE);

+    	assertTrue(8L < Long.MAX_VALUE);

+    	assertTrue(8L > Long.MIN_VALUE);

+    	assertTrue(8.0 < Double.MAX_VALUE);

+    	assertTrue(8.0 > Double.MIN_VALUE);

+    }

+    

+}

diff --git a/groovy/src/test/groovy/bugs/Groovy558_616_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy558_616_Bug.groovy
new file mode 100644
index 0000000..793440f
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy558_616_Bug.groovy
@@ -0,0 +1,45 @@
+package groovy.bugs
+
+import groovy.util.Dummy
+
+/**
+  * Fixes GROOVY-558 and GROOVY-616.
+  * A fully qualified class name ending with .class or not were not recognized properly.
+  *
+  * @author Jochen Theodorou
+  * @author Guillaume Laforge
+  */
+class Groovy558_616_Bug extends GroovyTestCase {
+
+    void testListClass() {
+        assert java.util.ArrayList.class == ArrayList.class
+        assert java.util.ArrayList.class == ArrayList
+        assert ArrayList != Class
+        def list = new ArrayList()
+        assert list.class == ArrayList
+    }
+
+    void testStringClass() {
+        assert java.lang.String.class == String.class
+        assert java.lang.String.class == String
+        assert String != Class
+        def st = ""
+        assert st.class == String
+    }
+
+    void testDummyClass() {
+        assert groovy.util.Dummy.class == Dummy.class
+        assert groovy.util.Dummy.class == Dummy
+        assert Dummy != Class
+        def dum = new Dummy()
+        assert dum.class == Dummy
+    }
+
+    void testFooClass() {
+        assert groovy.bugs.Groovy558_616_Bug.class == Groovy558_616_Bug
+        assert Groovy558_616_Bug != Class
+        def f = new Groovy558_616_Bug()
+        assert f.class == Groovy558_616_Bug
+    }
+}
+
diff --git a/groovy/src/test/groovy/bugs/Groovy593_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy593_Bug.groovy
new file mode 100644
index 0000000..4da678e
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy593_Bug.groovy
@@ -0,0 +1,58 @@
+package groovy.bugs
+
+import java.io.StringWriter
+import groovy.xml.MarkupBuilder
+
+/** 
+ * Tests that special XML chars are entitized by MarkupBuilder.
+ *
+ * @author <a href="mailto:scottstirling@rcn.com">Scott Stirling</a>
+ *
+ * @version $Revision: 1.4 $
+ *
+ *   Fix the cr lf handling of multiline stringon both of linux and Windows XP.
+ *   This test should success on Windows XP.
+ *
+ *   @author Pilho Kim
+ */
+class Groovy593_Bug extends GroovyTestCase {
+    
+    StringWriter writer = new StringWriter()
+    MarkupBuilder chars = new MarkupBuilder(writer)
+    XmlParser parser = new XmlParser()
+    String expectedXML = 
+"""<chars>
+  <ampersand a='&amp;'>&amp;</ampersand>
+  <quote attr='"'>"</quote>
+  <apostrophe attr='&apos;'>'</apostrophe>
+  <lessthan attr='value'>chars: &amp; &lt; &gt; '</lessthan>
+  <element attr='value 1 &amp; 2'>chars: &amp; &lt; &gt; " in middle</element>
+  <greaterthan>&gt;</greaterthan>
+</chars>"""
+
+    void testBug() {
+        // XML characters to test with
+        chars.chars {
+            ampersand(a: "&", "&")
+            quote(attr: "\"", "\"")
+            apostrophe(attr: "'", "'")
+            lessthan(attr: "value", "chars: & < > '") 
+            element(attr: "value 1 & 2", "chars: & < > \" in middle")
+            greaterthan(">")
+        }
+        //DEBUG
+        //println writer
+
+        // Test MarkupBuilder state with expectedXML
+  	    // Handling the cr lf characters, depending on operating system. 
+        def outputValue = writer.toString()
+        if (expectedXML.indexOf("\r\n") >= 0)  expectedXML = expectedXML.replaceAll("\r\n", "\n");
+        if (outputValue.indexOf("\r\n") >= 0)  outputValue = outputValue.replaceAll("\r\n", "\n");
+        assertEquals(expectedXML.replaceAll("\r\n", "\n"), outputValue)
+        
+        // parser will throw a SAXParseException if XML is not valid
+        parser.parseText(writer.toString())
+    }
+    
+}
+
diff --git a/groovy/src/test/groovy/bugs/Groovy662.groovy b/groovy/src/test/groovy/bugs/Groovy662.groovy
new file mode 100644
index 0000000..ceecbe7
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy662.groovy
@@ -0,0 +1,66 @@
+package groovy.bugs
+
+//  The order of the classes is crucial, the first must be the GroovyTestCase.  Its name doesn't
+//  matter it just has to be first.
+
+/**
+ * Test class and support to realize the GROOVY-662 test.  There is a difference between
+ * improper uses of properties between Groovy defined classes and Java defined classes.  There
+ * is no difference between correct uses so this is not a problem just an anti-regression test.
+ *
+ *  @author Russel Winder
+ *  @version $Revision$
+ */ 
+class Groovy662 extends GroovyTestCase {
+  private String expected = "Hello"
+  private usePropertyCorrectly ( def object ) { return object.@myProperty }
+  private usePropertyIncorrectly ( def object ) { return object.myProperty }
+  private useMethod ( def object ) { return object.getMyProperty ( ) }
+  private void doAssertions ( def object ) {
+    assertTrue ( useMethod ( object ) == expected )
+    assertTrue ( usePropertyCorrectly ( object ) == expected )
+  }
+  private String theTestScriptDefinitions = """
+String expected = "Hello"
+def usePropertyCorrectly ( def object ) { return object.@myProperty }
+def usePropertyIncorrectly ( def object ) { return object.myProperty }
+def useMethod ( def object ) { return object.getMyProperty ( ) }
+"""
+  private String theTestScriptAssertions = """
+assert useMethod ( object ) == expected
+assert usePropertyCorrectly ( object ) == expected
+"""
+  public void testJavaClass ( ) {
+    def object = new groovy.bugs.Groovy662_JavaClass ( ) 
+    doAssertions ( object )
+    assertTrue ( usePropertyIncorrectly ( object ) == null )
+  }
+  public void testGroovyClass ( ) {
+    def object = new Groovy662_GroovyClass ( ) 
+    doAssertions ( object )
+    assertTrue ( usePropertyIncorrectly ( object ) == expected )
+  }
+  public void testJavaClassAsScript ( ) { assertScript ( theTestScriptDefinitions + """
+def object = new groovy.bugs.Groovy662_JavaClass ( )
+""" + theTestScriptAssertions + """
+assert usePropertyIncorrectly ( object ) == null
+""") }
+  public void testGroovyClassAsScript ( ) {
+    assertScript ( theTestScriptDefinitions + """
+class Groovy662_GroovyClass extends HashMap {
+  String myProperty = "Hello"
+  public String getMyProperty ( ) { return myProperty }
+}
+def object = new Groovy662_GroovyClass ( )
+""" + theTestScriptAssertions + """
+assert usePropertyIncorrectly ( object ) == expected
+""")
+  }
+}
+
+class Groovy662_GroovyClass extends HashMap {
+  String myProperty = "Hello"
+  public String getMyProperty ( ) {
+    return myProperty
+  }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy662_JavaClass.java b/groovy/src/test/groovy/bugs/Groovy662_JavaClass.java
new file mode 100644
index 0000000..0dce3fc
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy662_JavaClass.java
@@ -0,0 +1,19 @@
+package groovy.bugs;
+
+import java.util.HashMap;
+
+/**
+ * Class to support the GROOVY-662 test.  There is a difference between improper uses of
+ * properties between Groovy defined classes and Java defined classes.  There is no difference
+ * between correct uses so this is not a problem just an anti-regression test.
+ *
+ * @author Russel Winder
+ * @version $Revision$
+ */
+public class Groovy662_JavaClass extends HashMap {
+    String myProperty = "Hello";
+
+    public String getMyProperty() {
+        return myProperty;
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy666_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy666_Bug.groovy
new file mode 100644
index 0000000..6980841
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy666_Bug.groovy
@@ -0,0 +1,11 @@
+package groovy.bugs
+
+/**
+ *  @author Russel Winder
+ *  @version $Revision$
+ */ 
+class Groovy666_Bug extends GroovyTestCase {
+  void testRunScript() {
+    (new GroovyShell ()).evaluate("x = 1")
+  }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy674_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy674_Bug.groovy
new file mode 100644
index 0000000..ca6b551
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy674_Bug.groovy
@@ -0,0 +1,55 @@
+package groovy.bugs
+
+/**
+ *  Test to ensure all the right exceptions are thrown for all the right/wrong combinations of
+ *  parentheses and no parameters for print and println.
+ *
+ *  @author Russel Winder
+ *  @version $Revision$
+ */
+class Groovy674_Bug extends GroovyTestCase {
+  void testTopLevelPrintParenthesesNoParameter ( ) {
+    try { ( new GroovyShell ( ) ).evaluate ( "print ( )" ) }
+    catch ( GroovyRuntimeException gre ) { return }
+    fail ( "Should have thrown GroovyRuntimeException" ) ;
+  }
+  void testTopLevelPrintlnParenthesesNoParameter ( ) {
+    assertScript ( "println ( )" )
+  }
+  void testClosurePrintParenthesesNoParameter ( ) {
+    try { ( new GroovyShell ( ) ).evaluate ( "[ 1 , 2 , 3 , 4 , 5 ].each { print ( ) }" ) }
+    catch ( GroovyRuntimeException gre ) { return }
+    fail ( "Should have thrown GroovyRuntimeException" ) ;
+  }
+  void testClosurePrintlnParenthesesNoParameter ( ) {
+    assertScript ( "[ 1 , 2 , 3 , 4 , 5 ].each { println ( ) }" )
+  }
+  void testTopLevelPrintNoParenthesesParameter ( ) { assertScript ( "print ( '' )" ) }
+  void testTopLevelPrintlnNoParenthesesParameter ( ) { assertScript ( "println ( '' )" ) }
+  void testClosurePrintNoParenthesesParameter ( ) { assertScript ( "[ 1 , 2 , 3 , 4 , 5 ].each { print ( '' ) }" ) }
+  void testClosurePrintlnNoParenthesesParameter ( ) { assertScript ( "[ 1 , 2 , 3 , 4 , 5 ].each { println ( '' ) }" ) }
+  void testTopLevelPrintParenthesesParameter ( ) { assertScript ( "print ''" ) }
+  void testTopLevelPrintlnParenthesesParameter ( ) { assertScript ( "println ''" ) }
+  void testClosurePrintParenthesesParameter ( ) { assertScript ( "[ 1 , 2 , 3 , 4 , 5 ].each { print '' }" ) }
+  void testClosurePrintlnParenthesesParameter ( ) { assertScript ( "[ 1 , 2 , 3 , 4 , 5 ].each { println '' }" ) }
+  void testTopLevelPrintProperty ( ) {
+    try { ( new GroovyShell ( ) ).evaluate ( "print" ) }
+    catch ( MissingPropertyException mpe ) { return ; }
+    fail ( "Should have thrown MissingPropertyException" ) ;
+  }
+  void testTopLevelPrintlnProperty  ( ) {
+    try { ( new GroovyShell ( ) ).evaluate ( "println" ) }
+    catch ( MissingPropertyException mpe ) { return ; }
+    fail ( "Should have thrown MissingPropertyException" ) ;
+  }
+  void testInClosurePrintProperty  ( ) {
+    try { ( new GroovyShell ( ) ).evaluate ( "[ 1 , 2 , 3 , 4 , 5 ].each { print }" ) }
+    catch ( MissingPropertyException mpe ) { return ; }
+    fail ( "Should have thrown MissingPropertyException" ) ;
+  }
+  void testInClosurePrintlnProperty  ( ) {
+    try { ( new GroovyShell ( ) ).evaluate ( "[ 1 , 2 , 3 , 4 , 5 ].each { println }" ) }
+    catch ( MissingPropertyException mpe ) { return ; }
+    fail ( "Should have thrown MissingPropertyException" ) ;
+  }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy675_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy675_Bug.groovy
new file mode 100644
index 0000000..5f2969e
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy675_Bug.groovy
@@ -0,0 +1,28 @@
+package groovy.bugs
+
+/**
+ *  @author Pilho Kim
+ *  @version $Revision$
+ */ 
+class Groovy675_Bug extends GroovyTestCase {
+    void testStringAndGString() {
+	assert "\\"!="\\\\" 
+	assert "\\\$"=="\\"+"\$" 
+	assert "\\"+"\\\\" == "\\"+"\\"+"\\" && "\\\\"+"\\" == "\\"+"\\"+"\\"
+	assert ("\\\\"+"\\").length() == 3
+	assert "\\3 \$1\$2" == "\\" + "3" + " " + "\$" + "1" + "\$" + "2"
+	assert "\\\\3 \\\$1\$2" == "\\" + "\\" + "3" + " " + "\\"+ "\$" + "1" + "\$" + "2"
+	assert "\\\\\\3 \\\\\$1\$2" == "\\" + "\\\\" + "3" + " " + "\\\\"+ "\$" + "1" + "\$" + "2"
+	assert "\\\\\\\\3 \\\\\\\$1\$2" == "\\\\" + "\\\\" + "3" + " " + "\\\\\\"+ "\$" + "1" + "\$" + "2"
+
+	assert "\\\\" == "\\" + "\\"
+	assert "\\\\".length() == 2
+
+	def z = 100 + 200
+	assert "\\\\ \\ ${z}" == "\\\\ \\ 300"
+	assert "\\\\ \\ ${z}" == "\\" + "\\" + " " + "\\" + " " + "300"
+	assert "Hello\\, \\World\\".charAt(4) == "o".charAt(0)
+	assert "Hello\\, \\World\\".charAt(5) == "\\".charAt(0)
+	assert "Hello\\, \\World\\".charAt(6) == ",".charAt(0)
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy770_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy770_Bug.groovy
new file mode 100644
index 0000000..76718a1
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy770_Bug.groovy
@@ -0,0 +1,47 @@
+/**
+ * @version $Revision$
+ */
+
+package groovy.bugs
+
+class Groovy770_Bug extends GroovyTestCase {
+     
+    void testBug() {
+        def a = new Pair(sym:"x")
+        def b = new Pair(sym:"y")
+        def c = new Pair(sym:"y")
+
+        def l1 = [a, b]
+        def l2 = [c]
+        println (l1)
+        println (l2)
+        println (l1 - l2)
+        assert l1 - l2 == l1
+
+
+        a = new CPair(sym:"x")
+        b = new CPair(sym:"y")
+        c = new CPair(sym:"y")
+        l1 = [a, b]
+        l2 = [c]
+        println (l1)
+        println (l2)
+        println (l1 - l2)
+        assert l1 - l2 == [a]
+    }
+}
+
+import java.util.*
+
+class Pair {
+  String sym
+}
+
+class CPair implements Comparable {
+  public String sym
+  int compareTo(Object o) {
+      return sym.compareTo(((CPair) o).sym);
+  }
+}
+
+
diff --git a/groovy/src/test/groovy/bugs/Groovy779_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy779_Bug.groovy
new file mode 100644
index 0000000..87deb4c
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy779_Bug.groovy
@@ -0,0 +1,104 @@
+package groovy.bugs
+
+class Groovy779_Bug extends GroovyTestCase {
+
+    def boolean exceptionCalled = false
+    def boolean finallyCalled = false
+
+    public static void main(String[] args) {
+        Groovy779_Bug app = new Groovy779_Bug()
+        app.testFieldProperty()
+        app.testBeanProperty()
+        app.testAutoboxingProperty()
+    }
+
+    public void testFieldProperty() {
+
+        try {
+            def p = new Groovy779OnePerson(nameID:"foo-", age:12.2)
+            assert p.age == 12
+            assert p.nameID == "foo-"
+            p = new Groovy779OnePerson(nameID:"foo-", age:"12")
+            println p.age
+            println p.nameID
+        }
+        catch (ClassCastException e) {
+            onException(e)
+        }
+        finally {
+            onFinally()
+        }
+        assert exceptionCalled , "should have invoked the catch clause"
+        assert finallyCalled , "should have invoked the finally clause"
+        // println("Success!")
+    }
+
+    public void testBeanProperty() {
+
+        try {
+            def p2 = new Groovy779AnotherPerson(nameID:1234, age:12.2)
+            assert p2.age == 12
+            assert p2.nameID == "1234"
+            p2 = new Groovy779AnotherPerson(nameID:111, age:"12")
+            println p2.age
+            println p2.nameID
+        }
+        catch (ClassCastException e) {
+            onException(e)
+        }
+        finally {
+            onFinally()
+        }
+        assert exceptionCalled , "should have invoked the catch clause"
+        assert finallyCalled , "should have invoked the finally clause"
+        // println("Success!")
+    }
+
+    public void testAutoboxingProperty() {
+        def p = new Groovy779OneProfit(signal:"bar", rate:15)
+        assert p.signal == "bar"
+        assert p.rate == 15.0
+
+        p = new Groovy779OneProfit(signal:111+22, rate:new java.math.BigDecimal("15"))
+        assert p.signal == "133"
+        assert p.rate == 15.0
+
+        def p2 = new Groovy779AnotherProfit(signal:"bar~", rate:15)
+        assert p2.signal == "bar~"
+        assert p2.rate == 15.0
+
+        p2 = new Groovy779AnotherProfit(signal:111-22, rate:new java.math.BigDecimal("15"))
+        assert p2.signal == "89"
+        assert p2.rate == 15.0
+    }
+
+    void onException(e) {
+        assert e != null
+        exceptionCalled = true
+    }
+	
+    void onFinally() {
+        finallyCalled = true
+    }
+
+}
+
+class Groovy779OnePerson {
+   def public String nameID
+   def public int age
+}
+
+class Groovy779AnotherPerson {
+   String nameID
+   int age
+}
+
+class Groovy779OneProfit {
+   public String signal
+   public double rate
+}
+
+class Groovy779AnotherProfit {
+   String signal
+   double rate
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy831_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy831_Bug.groovy
new file mode 100644
index 0000000..f87fe69
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy831_Bug.groovy
@@ -0,0 +1,38 @@
+package groovy.bugs
+
+/**
+ * Test for fixing the Jira issue GROOVY-831
+ *
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+class Groovy831_Bug extends GroovyTestCase {
+    
+    String[] cities = ['Seoul', 'London', 'Wasington']
+    int[] intArrayData = [1, 3, 5]
+
+    public String[] countries = [ 'Republic of Korea', 'United Kingdom', 'United State of America']
+    public  int[] intArray  = [ 2, 4, 6 ]
+
+    void testSetFieldProperty() {
+        assert cities.size() == 3
+        assert cities[0] == 'Seoul'
+        assert cities[1] == 'London'
+        assert cities[2] == 'Wasington'
+        assert intArrayData.size() == 3
+        assert intArrayData[0] == 1
+        assert intArrayData[1] == 3
+        assert intArrayData[2] == 5
+    }
+
+    void testSetFieldVariable() {
+        assert countries.size() == 3
+        assert countries[0] == 'Republic of Korea'
+        assert countries[1] == 'United Kingdom'
+        assert countries[2] == 'United State of America'
+        assert intArray[0] == 2
+        assert intArray[1] == 4
+        assert intArray[2] == 6
+    }
+}
+
diff --git a/groovy/src/test/groovy/bugs/Groovy872.groovy b/groovy/src/test/groovy/bugs/Groovy872.groovy
new file mode 100644
index 0000000..d6abf16
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy872.groovy
@@ -0,0 +1,22 @@
+package groovy.bugs
+
+class Groovy872 extends GroovyTestCase {
+  void testScript ( ) {
+    assertScript ( """
+def cal = new GregorianCalendar ( )
+cal.set ( Calendar.DAY_OF_MONTH , 1 )
+println ( cal.get ( Calendar.DAY_OF_MONTH ) )
+""")
+  }
+  void testCode ( ) {
+    new MyCalendar ( ).tryit ( )
+  }   
+}
+
+class MyCalendar {
+  void tryit ( )  {
+    def cal = new GregorianCalendar ( )
+    cal.set ( Calendar.DAY_OF_MONTH , 1 )
+    println ( cal.get ( Calendar.DAY_OF_MONTH ) )
+  }
+}
diff --git a/groovy/src/test/groovy/bugs/Groovy965_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy965_Bug.groovy
new file mode 100644
index 0000000..d74168a
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy965_Bug.groovy
@@ -0,0 +1,37 @@
+package groovy.bugs
+
+/**
+ *  A test case to ensure that Groovy can compile class names and variable names with non-ASCII
+ *  characters and that non-ASCII characters in Strings do the right thing.
+ *
+ *  <p>Unfortunately, we cannot actually have this test in the Subversion store since it
+ *  requires having an encoding.  Java internally uses UTF-16.  Most Linux/UNIX/Mac OS X users
+ *  use UTF-8 (or if they haven't caught up yet ISO-8859-{1..15], in Europe anyway).  Windows
+ *  internally is UTF-16 LE but it appears that the Europe region defaults to ISO-8859-1 which
+ *  is very silly.  All in all we cannot gurantee an ecoding so we cannot have the tests.</p>
+ *
+ *  <p>If anyone spots any errors in the rationale or finds a way to fix things please update at
+ *  will.</p>
+ *
+ *  @suthor Russel Winder
+ *  @version $LastChangedRevision$ $LastChangedDate$
+ */
+class Groovy965_Bug extends GroovyTestCase {
+  /* void test to avoid assertion failure because of the lack of test method in the class */
+  void testVoid() {}
+  /*
+  void testUnicodeVariableNamesAndStrings ( ) {
+    def âøñè = 'âøñè'
+    assertEquals ( 'âøñè' , âøñè )
+  }
+  void testUnicodeMëthødName ( ) { }
+  void testUnicodeClassName ( ) {
+    def object = new Bläh ( ) 
+    assert true
+  }
+  */
+}
+
+/*
+class Bläh { }
+*/
diff --git a/groovy/src/test/groovy/bugs/Groovy996_Bug.groovy b/groovy/src/test/groovy/bugs/Groovy996_Bug.groovy
new file mode 100644
index 0000000..e160738
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/Groovy996_Bug.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs
+
+class Groovy996_Bug extends GroovyTestCase {
+    void testAccessToSuperProtectedField() {
+        def a = new Groovy996_SubClass()
+        a.out()
+    }
+}
+
+class Groovy996_SuperClass {
+    protected String x = 'This is an X'
+}
+
+class Groovy996_SubClass extends Groovy996_SuperClass {
+    void out() {
+       println( x )
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/GuillaumesBug.groovy b/groovy/src/test/groovy/bugs/GuillaumesBug.groovy
new file mode 100644
index 0000000..3e8750e
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/GuillaumesBug.groovy
@@ -0,0 +1,13 @@
+package groovy.bugs
+
+/**
+ * @author Guillaume Laforge 
+ * @version $Revision$
+ */
+class GuillaumesBug extends GroovyTestCase {
+    
+    void testBug() {
+        if (true) 
+            println("true")
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/GuillaumesMapBug.groovy b/groovy/src/test/groovy/bugs/GuillaumesMapBug.groovy
new file mode 100644
index 0000000..d5a956d
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/GuillaumesMapBug.groovy
@@ -0,0 +1,42 @@
+package groovy.bugs
+
+/**
+ * @author Guillaume Laforge
+ * @version $Revision$
+ */
+class GuillaumesMapBug extends GroovyTestCase {
+    
+    void testBug2() {
+        def list = [1, 2, 3]
+        def map = [:]
+        
+        doLoop(list, map)
+    
+        assert map[0] == 1 
+        assert map[1] == 2 
+        assert map[2] == 3 
+    }
+    
+    void doLoop(list, map) {
+        def i = 0
+        for (it in list) {
+            map[i++] = it
+        }
+    }
+    
+    
+    void testBug() {
+        def list = [1, 2, 3]
+        def map = [:]
+        doClosureLoop(list, map)
+        
+        assert map[0] == 1 
+        assert map[1] == 2 
+        assert map[2] == 3 
+    }
+    
+    void doClosureLoop(list, map) {
+        def i = 0
+        list.each { map[i++] = it }
+    }    
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/IanMaceysBug.java b/groovy/src/test/groovy/bugs/IanMaceysBug.java
new file mode 100644
index 0000000..3186ba7
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/IanMaceysBug.java
@@ -0,0 +1,66 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.bugs;
+
+import org.codehaus.groovy.classgen.TestSupport;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class IanMaceysBug extends TestSupport {
+
+    public void testBug() throws Exception {
+        try {
+            assertScript("dummy = 0; for ( i in 0..9 ) {  dummy += i }\n println 'done'", "dummy.groovy");
+            fail("Should throw a syntax exception");
+        }
+        catch (Exception e) {
+            System.out.println("Worked. Caught: " + e);
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/InconsistentStackHeightBug.groovy b/groovy/src/test/groovy/bugs/InconsistentStackHeightBug.groovy
new file mode 100644
index 0000000..c9a34b2
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/InconsistentStackHeightBug.groovy
@@ -0,0 +1,34 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class InconsistentStackHeightBug extends GroovyTestCase {
+
+    void testBug() {
+        def server = 0
+        def tmp = server + 1
+        try {
+        }
+        finally {
+        }
+    }
+
+    void testBug2() {
+        def server = 0
+        def tmp = server == 2
+        try {
+        }
+        finally {
+        }
+    }
+
+    void testBug3() {
+        def server = 0
+        def foo = server
+        try {
+        }
+        finally {
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/InterfaceImplBug.groovy b/groovy/src/test/groovy/bugs/InterfaceImplBug.groovy
new file mode 100644
index 0000000..99eabc0
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/InterfaceImplBug.groovy
@@ -0,0 +1,20 @@
+package groovy.bugs
+
+import java.io.Reader
+import org.codehaus.groovy.dummy.FooHandler
+
+/**
+ * @author Robert Fuller
+ * @version $Revision$
+ */
+class InterfaceImplBug extends GroovyTestCase implements FooHandler {
+
+    void testMethodCall() {
+        handle(null)
+    }
+    
+    void handle(Reader reader){
+        println("in handle method")
+        def called = true
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/InvokeNormalMethodFromBuilder_Bug657.groovy b/groovy/src/test/groovy/bugs/InvokeNormalMethodFromBuilder_Bug657.groovy
new file mode 100644
index 0000000..7d3782e
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/InvokeNormalMethodFromBuilder_Bug657.groovy
@@ -0,0 +1,41 @@
+package groovy.bugs
+
+/**
+  * <p>
+  * Test that ensures that:
+  * <ul>
+  *   <li>it is possible to write a builder in Groovy</li>
+  *   <li>it is possible to call normal methods from the builder,
+  *       without the methods being trapped endlessly by createNode()</li>
+  * </ul>
+  * </p>
+  *
+  * @author Guillaume Laforge
+  */
+class InvokeNormalMethodFromBuilder_Bug657 extends GroovyTestCase {
+    void testInvokeNormalMethod() {
+        def b = new Builder()
+        assert b.callNormalMethod() == "first"
+
+        def value = b.someNode() {}
+        assert value == "second"
+    }
+}
+
+class Builder extends BuilderSupport {
+
+    void setParent(Object parent, Object child) {}
+
+    Object createNode(Object name)                 { return createNode(name, [:], null) }
+    Object createNode(Object name, Map attributes) { return createNode(name, attributes, null) }
+    Object createNode(Object name, Object value)   { return createNode(name, [:], value) }
+
+    Object createNode(Object name, Map attributes, Object value) {
+        println "create ${name}"
+        return callOtherStaticallyTypedMethod()
+    }
+
+    String callNormalMethod()               { println "normalMethod"; return "first" }
+    String callOtherStaticallyTypedMethod() { println "otherMethod";  return "second" }
+    
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/IterateOverCustomTypeBug.groovy b/groovy/src/test/groovy/bugs/IterateOverCustomTypeBug.groovy
new file mode 100644
index 0000000..61d0d7f
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/IterateOverCustomTypeBug.groovy
@@ -0,0 +1,21 @@
+package groovy.bugs
+
+/**
+ * @version $Revision: 1.3 $
+ */
+class IterateOverCustomTypeBug extends TestSupport {
+    
+    void testBug() {
+        def object = this
+        
+        def answer = []
+        for (i in object) {
+            answer << i
+        }
+        assert answer == ['a', 'b', 'c']
+        
+        answer = []
+        object.each { answer << it }
+        assert answer == ['a', 'b', 'c']
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/MarkupAndMethodBug.groovy b/groovy/src/test/groovy/bugs/MarkupAndMethodBug.groovy
new file mode 100644
index 0000000..fd03943
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/MarkupAndMethodBug.groovy
@@ -0,0 +1,31 @@
+package groovy.bugs
+
+/**
+ * Mixes variables, closures and method calls in markup
+ *
+ * @version $Revision$
+ */
+class MarkupAndMethodBug extends GroovyTestCase {
+    
+    void testBug() {
+        def tree = createTree()
+        def name = tree.person[0]['@name']
+        assert name == 'James'
+    }
+    
+    protected def createTree() {
+        def builder = NodeBuilder.newInstance()
+        
+        def root = builder.people() {
+            person(name:getTestName())
+        }
+        
+        assert root != null
+        
+        return root
+    }
+    
+    protected def getTestName() {
+        "James"
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/MarkupInScriptBug.java b/groovy/src/test/groovy/bugs/MarkupInScriptBug.java
new file mode 100644
index 0000000..cf58d88
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/MarkupInScriptBug.java
@@ -0,0 +1,60 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.bugs;
+
+import org.codehaus.groovy.classgen.TestSupport;
+
+
+/**
+ * @version $Revision$
+ */
+public class MarkupInScriptBug extends TestSupport {
+
+    public void testBug() throws Exception {
+        assertScriptFile("src/test/groovy/script/AtomTestScript.groovy");
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/MethodCallWithoutParensInStaticMethodBug.groovy b/groovy/src/test/groovy/bugs/MethodCallWithoutParensInStaticMethodBug.groovy
new file mode 100644
index 0000000..be118e2
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/MethodCallWithoutParensInStaticMethodBug.groovy
@@ -0,0 +1,12 @@
+package groovy.bugs
+
+class MethodCallWithoutParensInStaticMethodBug extends GroovyTestCase {
+
+    void testBug() {
+        staticMethod()
+    }
+    
+    static void staticMethod() {
+        println 'hello'[1]
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/MethodClosureTest.groovy b/groovy/src/test/groovy/bugs/MethodClosureTest.groovy
new file mode 100644
index 0000000..9e1317a
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/MethodClosureTest.groovy
@@ -0,0 +1,36 @@
+package groovy.bugs
+
+import org.codehaus.groovy.runtime.MethodClosure
+
+class MethodClosureTest extends GroovyTestCase {
+
+    def aa(x) {
+        x
+    }
+    
+    static bb(it) { it}
+
+    void testMetodClosure() {   
+        Class[] c1 =  [ Exception.class, Throwable.class ]
+        Class[] c2 = [ IllegalStateException.class ]
+
+        def cl = this.&aa
+
+        assert cl instanceof Closure
+        assert cl instanceof MethodClosure
+
+        assert [c1, c2].collect(cl) == [c1,c2]
+    }
+    
+    void testStaticMethodAccess() {
+       def list = [1].collect (this.&bb)
+       assert list == [1]
+       list = [1].collect (MethodClosureTest.&bb)
+       assert list == [1]
+       def mct = new MethodClosureTest()
+       list = [1].collect (mct.&bb)
+       assert list == [1]
+    }
+}
+
+
diff --git a/groovy/src/test/groovy/bugs/MethodDispatchBug.groovy b/groovy/src/test/groovy/bugs/MethodDispatchBug.groovy
new file mode 100644
index 0000000..68f8147
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/MethodDispatchBug.groovy
@@ -0,0 +1,42 @@
+package groovy.bugs
+
+class MethodDispatchBug extends GroovyTestCase {
+    def doit(Object parameter1, Object parameter2) {
+        "OO"
+    }
+
+    def doit(Boolean parameter1, Object parameter2) {
+        "BO"
+    }
+
+    def doit(Object parameter1, Boolean parameter2) {
+        "OB"
+    }
+
+    def doit(Boolean parameter1, Boolean parameter2) {
+        "BB"
+    }
+
+    def testBug() {
+        def o = this;
+
+        assert "BB" == o.doit(true, true);
+        assert "BO" == o.doit(true, 9);
+        assert "OB" == o.doit(9, true);
+        assert "OO" == o.doit(9, 9);
+    }
+    
+    def methodWithDefaults(a,b,c=1000) {
+      a+b+c
+    }
+    
+    void testListExpansion() {
+       // there was a bug discovered while looking at GROOVY-1803
+       // a list expansion was cached like 
+       // methodWithDefaults(List) -> methodWithDefaults(Object,Object,Object)
+       // but this cached version can't handle lists with an arbitrary length 
+       // of parameters, resulting in the second call here to fail
+       assert methodWithDefaults([1,10,100]) == 111
+       assert methodWithDefaults([1,10]) == 1011    
+    }    
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/MethodPointerBug.groovy b/groovy/src/test/groovy/bugs/MethodPointerBug.groovy
new file mode 100644
index 0000000..bcc3e12
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/MethodPointerBug.groovy
@@ -0,0 +1,47 @@
+package groovy.bugs
+
+/**
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+class MethodPointerBug extends GroovyTestCase {
+
+    def void sayHello() { 
+        println "hello" 
+    } 
+
+    def MethodPointerBug getThisObject() { 
+        return this
+    } 
+
+    // Test a standard method pointer operator ".&".  For example, foo.&bar.
+    void testMethodPointer() {
+        def bug = new MethodPointerBug()
+        def x = bug.&sayHello
+        x()
+    } 
+
+    // Test a standard method pointer operator ".&" with this object.  For example, this.&bar.
+    void testThisMethodPointer() {
+        def y = this.&sayHello
+        y()
+    } 
+
+    ///////////////////////////////////////////////////////////////////////////////////////////
+    // Test a default method pointer operator "&" with this object.  For example, &bar.
+    // This shows that the issue GROOVY-826 has been fixed in groovy-1.0-jar-02.
+/*
+  todo - commented out due to groovy.g non-determinisms
+    void testDefaultMethodPointer() {
+        def z = &sayHello
+        z()
+    } 
+*/
+    // Test a default method pointer operator ".&" with returned object.  For example, someMethod().&bar.
+    void testMethodPointerWithReturn() {
+        def u = getThisObject().&sayHello
+        u()
+        def v = thisObject.&sayHello
+        v()
+    } 
+}
diff --git a/groovy/src/test/groovy/bugs/MorgansBug.groovy b/groovy/src/test/groovy/bugs/MorgansBug.groovy
new file mode 100644
index 0000000..d2a7403
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/MorgansBug.groovy
@@ -0,0 +1,13 @@
+package groovy.bugs
+
+/**
+ * @author Morgan Hankins
+ * @version $Revision$
+ */
+class MorgansBug extends GroovyTestCase {
+
+    void testBug() {
+        def result = 4 + "x"
+        assert result == "4x"
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/NestedClosure2Bug.groovy b/groovy/src/test/groovy/bugs/NestedClosure2Bug.groovy
new file mode 100644
index 0000000..6dd5ef2
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/NestedClosure2Bug.groovy
@@ -0,0 +1,68 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class NestedClosure2Bug extends TestSupport {
+     
+    Object f
+     
+    void testFieldBug() {
+    	def closure = {
+    		return {
+	    		f = 123
+	    		return null
+	        }
+	    }
+        def value = closure()
+        value = value()
+        assert f == 123
+    }
+     
+    void testBugOutsideOfScript() {
+    	def a = 123
+    	def b = 456
+    	def closure = {
+    		println b
+    		def c = 999
+    		return {
+    			f = 2222111
+    			
+    			println f
+    			
+    			println c
+    			def d = 678
+    			return { 
+    				println f
+    				assert f == 2222111
+    				println d
+    				return a
+    			}
+    		}
+    	}
+    	def c2 = closure()
+    	def c3 = c2()
+    	def value = c3()
+
+		assert f == 2222111    	
+    	assert value == 123
+    }
+    
+    void testBug() {
+    	assertScript """
+	    	def a = 123
+	    	def closure = {
+	    		return {
+	    			return { 
+	    				return a
+	    			}
+	    		}
+	    	}
+	    	def c2 = closure()
+	    	def c3 = c2()
+	    	value = c3()
+	    	
+	    	assert value == 123
+"""
+	}
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/NestedClosureBug.groovy b/groovy/src/test/groovy/bugs/NestedClosureBug.groovy
new file mode 100644
index 0000000..9aef29c
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/NestedClosureBug.groovy
@@ -0,0 +1,21 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class NestedClosureBug extends GroovyTestCase {
+     
+    void testBug() {
+    	def a = 123
+    	getValues().each { 
+    		println it
+    		it.each { 
+    			assert a == 123
+    		}
+    	}
+    }
+    
+    def getValues() {
+    	[5, 6, 7]
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/NullCompareBug.groovy b/groovy/src/test/groovy/bugs/NullCompareBug.groovy
new file mode 100644
index 0000000..ec12f2a
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/NullCompareBug.groovy
@@ -0,0 +1,13 @@
+package groovy.bugs
+
+/**
+ * @version $Revision: 1.3 $
+ */
+class NullCompareBug extends GroovyTestCase {
+    
+    void testBug() {
+        assert "dog" > null
+        assert null < "dog"
+        assert null == null
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/OverloadInvokeMethodBug.groovy b/groovy/src/test/groovy/bugs/OverloadInvokeMethodBug.groovy
new file mode 100644
index 0000000..0ec3cce
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/OverloadInvokeMethodBug.groovy
@@ -0,0 +1,32 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+ 
+class OverloadInvokeMethodBug extends GroovyTestCase {
+     
+    void testBug() {
+    	def a = new OverloadA()
+    	a.duh()
+    	
+    	def b = new OverloadB()
+    	b.duh()
+    }
+
+}
+
+class OverloadA {
+        def invokeMethod(String name, Object args) {
+                try {
+                        metaClass.invokeMethod(this, name, args)
+                } catch(MissingMethodException e) {
+                        println "Missing method: ${name}"
+                }
+        } 
+}
+
+class OverloadB extends OverloadA {
+
+}
+
diff --git a/groovy/src/test/groovy/bugs/PrimitivePropertyBug.groovy b/groovy/src/test/groovy/bugs/PrimitivePropertyBug.groovy
new file mode 100644
index 0000000..6b2db6f
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/PrimitivePropertyBug.groovy
@@ -0,0 +1,50 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ *
+ * Fix Bug GROOVY-683
+ * @author Pilho Kim
+ */
+class PrimitivePropertyBug extends GroovyTestCase {
+     
+    double x1
+    float x2
+    long x3
+    int x4
+    short x5
+    byte x6
+    char x7
+
+    void testBug() {
+        def y = new PrimitivePropertyBug()
+        y.x1 = 10.0
+        y.x2 = 10.0
+        y.x3 = 10.0
+        y.x4 = 10.0
+        y.x5 = 10.0
+        y.x6 = 10.0
+        y.x7 = 10.0
+        
+        assert y.x1 == 10.0
+        assert y.x2 == 10.0
+        assert y.x3 == 10.0
+        assert y.x4 == 10.0
+        assert y.x5 == 10.0
+        assert y.x6 == 10.0
+        assert y.x1.class == Double.class
+        assert y.x2.class == Float.class
+        assert y.x3.class == Long.class
+        assert y.x4.class == Integer.class
+        assert y.x5.class == Short.class
+        assert y.x6.class == Byte.class
+        assert y.x7.class == Character.class
+        assert y.x1 + y.x1 == y.x1 * 2
+        assert y.x2 - 1 == 9.0f
+        assert y.x3 * 2 == 20L
+        assert y.x4 == 10
+        assert y.x5 == 10
+        assert y.x6 + 3 == 13
+        assert "Hello" + y.x7 + "World!" == "Hello\nWorld!"
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/PrintlnWithNewBug.groovy b/groovy/src/test/groovy/bugs/PrintlnWithNewBug.groovy
new file mode 100644
index 0000000..02b1624
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/PrintlnWithNewBug.groovy
@@ -0,0 +1,15 @@
+package groovy.bugs
+
+import groovy.Foo
+
+/**
+ * @author Mark Volkmann 
+ * @version $Revision: 1.3 $
+ */
+class PrintlnWithNewBug extends GroovyTestCase {
+    
+    void testBug() {
+        println(new Foo(name:'abc')) 
+        println new Foo(name:'def') 
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/PropertyBug.groovy b/groovy/src/test/groovy/bugs/PropertyBug.groovy
new file mode 100644
index 0000000..4a02070
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/PropertyBug.groovy
@@ -0,0 +1,20 @@
+package groovy.bugs
+
+import javax.swing.JButton
+import javax.swing.JPanel
+
+/**
+ * @version $Revision$
+ */
+class PropertyBug extends GroovyTestCase {
+     
+    void testBug() {
+        def panel = new JPanel()
+        def bean = new JButton()
+        
+        panel.add(bean)
+        
+        def value = bean.parent
+        assert value != null
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/PropertyNameBug.groovy b/groovy/src/test/groovy/bugs/PropertyNameBug.groovy
new file mode 100644
index 0000000..630f9b0
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/PropertyNameBug.groovy
@@ -0,0 +1,34 @@
+package groovy.bugs
+
+/**
+ * Test to fix the issue GROOVY-843.
+ *
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+
+public class PropertyNameBug extends GroovyTestCase {
+    void testNonJavaIdentifierChacactersWithJavaSyntax() {
+        Map map = new HashMap()
+        map.put("foo.bar", "FooBar")
+        map.put("foo.bar-bar", "FooBar-Bar")
+        map.put("foo.=;&|^*-+-/\\'?.*:arbitrary()[]{}%#@!", "Any character")
+
+        println("foo.bar1 = ${map.get("foo.bar1")}")
+        println("foo.bar-bar = ${map.get("foo.bar-bar")}")
+        println("Specical Character Test: ${map.get("foo.=;&|^*-+-/\\'?.*:arbitrary()[]{}%#@!")}")
+    }
+
+    void testNonJavaIdentifierChacactersWithGroovySyntax() {
+        def map = [:]
+        map."foo.bar" = "FooBar"
+        map."foo.bar-bar" = "FooBar-Bar"
+        map."foo.=;&|^*-+-/\\'?.*:arbitrary()[]{}%#@!" = "Any character"
+
+        println("foo.bar1 = ${map."foo.bar1"}")
+        println("foo.bar-bar = ${map."foo.bar-bar"}")
+        println("Specical Character Test: ${map."foo.=;&|^*-+-/\\'?.*:arbitrary()[]{}%#@!"}")
+    }
+}
+
+
diff --git a/groovy/src/test/groovy/bugs/RodsBooleanBug.groovy b/groovy/src/test/groovy/bugs/RodsBooleanBug.groovy
new file mode 100644
index 0000000..1319b6c
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/RodsBooleanBug.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class RodsBooleanBug extends GroovyTestCase {
+
+    def item = "hi"
+    
+    void testBug() {
+        assert isIt()
+    }
+    
+    def isIt() {
+        return item != null && item == "hi"
+    }
+    
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/RodsBug.groovy b/groovy/src/test/groovy/bugs/RodsBug.groovy
new file mode 100644
index 0000000..bf76fb5
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/RodsBug.groovy
@@ -0,0 +1,45 @@
+package groovy.bugs
+
+/**
+ * @author Rod Cope
+ * @version $Revision$
+ */
+class RodsBug extends GroovyTestCase {
+    
+    void testBug() {
+        doTest(true)
+        /*
+         def x = 1
+         if (x > 0) {
+         String name = "Rod"
+         println(name)
+         }
+         */
+    }
+    
+    void testBug2() {
+        def x = 1
+        if (x > 0) {
+            //String name = "Rod"
+            def name = "Rod"
+            println(name)
+        }
+    }
+    
+    void doTest(flag) {
+        if (flag) {
+            String name = "Rod"
+            //def name = "Rod"
+            doAssert(name)
+        }
+    }
+    
+    void doTest() {
+        String name = "Rod"
+        doAssert(name)
+    }
+    
+    void doAssert(text) {
+        assert text != null
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/RussellsOptionalParenTest.groovy b/groovy/src/test/groovy/bugs/RussellsOptionalParenTest.groovy
new file mode 100644
index 0000000..2b62796
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/RussellsOptionalParenTest.groovy
@@ -0,0 +1,11 @@
+package groovy.bugs
+
+class RussellsOptionalParenTest extends GroovyTestCase {
+
+    void testMethodCallWithOneParam() {
+        def adob = new ArrayList()
+        adob.add "hello"
+        println adob.get(0)
+        println adob.size()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ScriptBug.java b/groovy/src/test/groovy/bugs/ScriptBug.java
new file mode 100644
index 0000000..1e8d3d9
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ScriptBug.java
@@ -0,0 +1,61 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.bugs;
+
+import org.codehaus.groovy.classgen.TestSupport;
+
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ScriptBug extends TestSupport {
+
+    public void testBug() throws Exception {
+        assertScript("println 'hello world'");
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/SeansBug.java b/groovy/src/test/groovy/bugs/SeansBug.java
new file mode 100644
index 0000000..65677a5
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/SeansBug.java
@@ -0,0 +1,92 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.bugs;
+
+import groovy.lang.GroovyShell;
+import org.codehaus.groovy.classgen.TestSupport;
+
+/**
+ * @author Sean Timm
+ * @version $Revision$
+ */
+public class SeansBug extends TestSupport {
+
+    public void testBug() throws Exception {
+        String code = "for (i in 1..10) \n{\n  println(i)\n}";
+        GroovyShell shell = new GroovyShell();
+        shell.evaluate(code);
+    }
+
+    public void testMarkupBug() throws Exception {
+        String[] lines =
+                {
+                        "package groovy.xml",
+                        "",
+                        "b = new MarkupBuilder()",
+                        "",
+                        "b.root1(a:5, b:7) { ",
+                        "    elem1('hello1') ",
+                        "    elem2('hello2') ",
+                        "    elem3(x:7) ",
+                        "}"};
+        String code = asCode(lines);
+        GroovyShell shell = new GroovyShell();
+        shell.evaluate(code);
+    }
+
+    /**
+     * Converts the array of lines of text into one string with newlines
+     */
+    protected String asCode(String[] lines) {
+        StringBuffer buffer = new StringBuffer();
+        for (int i = 0; i < lines.length; i++) {
+            buffer.append(lines[i]);
+            buffer.append("\n");
+        }
+        return buffer.toString();
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/SimpleModel.java b/groovy/src/test/groovy/bugs/SimpleModel.java
new file mode 100644
index 0000000..905b07f
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/SimpleModel.java
@@ -0,0 +1,68 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.bugs;
+
+/**
+ * @author
+ * @version $Revision$
+ */
+public class SimpleModel {
+    public String s;
+
+    SimpleModel() {
+        s = "Hi!";
+    }
+
+    public void show() {
+        System.out.println(s);
+    }
+
+    public static void main(String[] args) {
+        SimpleModel simp = new SimpleModel();
+        simp.show();
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/StaticClosurePropertyBug.groovy b/groovy/src/test/groovy/bugs/StaticClosurePropertyBug.groovy
new file mode 100644
index 0000000..2d23b54
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/StaticClosurePropertyBug.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs
+
+/** 
+ * @author Robert Kuzelj
+ * @version $Revision$
+ */
+class StaticClosurePropertyBug extends GroovyTestCase {
+
+    static def out = {System.out.println(it)}
+    
+    void testCallStaticClosure() {
+        callStaticClosure()
+    }
+    
+    static def callStaticClosure() {
+        out("TEST")
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/StaticMarkupBug.groovy b/groovy/src/test/groovy/bugs/StaticMarkupBug.groovy
new file mode 100644
index 0000000..5c7f1bf
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/StaticMarkupBug.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs
+
+import groovy.xml.MarkupBuilder
+
+class StaticMarkupBug extends GroovyTestCase {
+
+    void testBug() {
+        doMarkup(new MarkupBuilder())
+    }
+    
+    static void doMarkup(builder) {
+        builder.html {
+            head {
+                title("Groovy")
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/StaticMethodCallBug.groovy b/groovy/src/test/groovy/bugs/StaticMethodCallBug.groovy
new file mode 100644
index 0000000..e85c377
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/StaticMethodCallBug.groovy
@@ -0,0 +1,17 @@
+package groovy.bugs
+
+/** 
+ * @version $Revision$
+ */
+class StaticMethodCallBug extends GroovyTestCase {
+
+    void testBug() {
+        def value = TestSupport.mockStaticMethod()
+        assert value == "cheese"
+    }
+    
+    void testStaticProperty() {
+        def value = TestSupport.mockStaticProperty
+        assert value == "cheese"
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/StaticMethodImportBug.groovy b/groovy/src/test/groovy/bugs/StaticMethodImportBug.groovy
new file mode 100644
index 0000000..ee2540b
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/StaticMethodImportBug.groovy
@@ -0,0 +1,13 @@
+package groovy.bugs;
+
+import org.codehaus.groovy.dummy.*
+
+/**
+ * Test case to check if imports can use wildcard (*) for static method calls.
+ * Bug reference: Explicit import needed to call static method, GROOVY-935
+ */
+class StaticMethodImportBug extends GroovyTestCase {
+    void testBug() {
+        assert ClassWithStaticMethod.staticMethod()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/StaticMethodImportBug2.groovy b/groovy/src/test/groovy/bugs/StaticMethodImportBug2.groovy
new file mode 100644
index 0000000..f7f2ce3
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/StaticMethodImportBug2.groovy
@@ -0,0 +1,13 @@
+package groovy.bugs;
+
+import org.codehaus.groovy.dummy.ClassWithStaticMethod
+
+/**
+ * Test case to check if imports can use fully qualified classes for static method calls.
+ * Bug reference: Explicit import needed to call static method, GROOVY-935
+ */
+class StaticMethodImportBug2 extends GroovyTestCase {
+    void testBug() {
+        assert ClassWithStaticMethod.staticMethod()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/SubscriptAndExpressionBug.groovy b/groovy/src/test/groovy/bugs/SubscriptAndExpressionBug.groovy
new file mode 100644
index 0000000..48b8866
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/SubscriptAndExpressionBug.groovy
@@ -0,0 +1,68 @@
+package groovy.bugs
+
+class SubscriptAndExpressionBug extends GroovyTestCase {
+    
+    void testBug() {
+        def foo = ["nice cheese grommit"]
+        
+        def cheese = foo[0].startsWith("nice")
+        
+        assert cheese == true
+    }
+
+    void testSubscriptIncrement() {
+        def foo = [5, 6, 7]
+        foo[0] += 5
+        
+        assert foo[0] == 10
+        
+        def i = 0
+        foo[i++] = 1
+        assert foo[0] == 1
+        assert i == 1
+        
+        foo[i++] += 5
+        assert i == 2
+        assert foo[1] == 11
+    }
+
+    void testLargeSubscript() {
+        def foo = [1]
+        
+        foo[10] = 123
+        
+        assert foo[10] == 123
+        
+        foo.putAt(12, 55)
+        assert foo[12] == 55
+        
+        def i = 20
+        foo[i] = 1
+        foo[i++] += 5
+        
+        assert i == 21
+        assert foo[20] == 6
+    }
+    
+    void testDoubleSubscript() {
+        def foo = ["nice cheese grommit"]
+        
+        def cheese = foo[0][5..10]
+        
+        assert cheese == "cheese"
+    }
+    
+    void testSubscriptAndProperty() {
+        def foo = [['gromit':'cheese']]
+        
+        def cheese = foo[0].gromit
+        
+        assert cheese == "cheese"
+    }
+    
+    void testBooleanExpression() {
+       int[] a = new int[1]
+       assert (a[0] = 42) == 42 
+       assert a[0] == 42
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/SubscriptOnPrimitiveTypeArrayBug.groovy b/groovy/src/test/groovy/bugs/SubscriptOnPrimitiveTypeArrayBug.groovy
new file mode 100644
index 0000000..31630c0
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/SubscriptOnPrimitiveTypeArrayBug.groovy
@@ -0,0 +1,40 @@
+package groovy.bugs
+
+class SubscriptOnPrimitiveTypeArrayBug extends TestSupport {
+    int[] ia;  // type is not necessary
+    int i1;
+
+    void testBug() {
+        def array = getIntArray() // this function returns [I, true primitive array
+        
+        def value = array[2]
+        
+        assert value == 3
+        
+        array[2] = 8
+
+        value = array[2]
+        assert value == 8
+        
+        // lets test a range
+        def range = array[1..2]
+        
+        assert range == [2, 8]
+    }
+
+    void testGroovyIntArray() {
+        int[] ia = [1, 2]
+        int[] ia1 = ia; // type is not necessary
+        def i1 = ia1[0]
+        int i2 = i1
+        assert i2 == 1
+    }
+
+    void testIntArrayObjectRangeSelection() {
+        int[] ia = [1000, 1100, 1200, 1300, 1400]
+        def range = new ObjectRange(new Integer(1), new Integer(3))
+        def selected = ia[range]
+        assert selected == [1100, 1200, 1300]
+    }
+
+}
diff --git a/groovy/src/test/groovy/bugs/SubscriptOnStringArrayBug.groovy b/groovy/src/test/groovy/bugs/SubscriptOnStringArrayBug.groovy
new file mode 100644
index 0000000..b8fa0c8
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/SubscriptOnStringArrayBug.groovy
@@ -0,0 +1,22 @@
+package groovy.bugs
+
+class SubscriptOnStringArrayBug extends TestSupport {
+
+    void testArraySubscript() {
+        def array = getMockArguments()
+ 
+        assert array[1] == "b"
+        
+        array[0] = "d"
+        
+        assert array[0] == "d"
+        
+        println("Contents of array are ${array.inspect()}")
+    }
+    
+    void testRobsTestCase() {
+        def array = "one two three".split(" ")
+        
+        assert array[1] == "two"
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/SuperMethod2Bug.groovy b/groovy/src/test/groovy/bugs/SuperMethod2Bug.groovy
new file mode 100644
index 0000000..f377708
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/SuperMethod2Bug.groovy
@@ -0,0 +1,129 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+ 
+class SuperMethod2Bug extends GroovyTestCase {
+     
+    void testBug() {
+    	def base = new SuperBase()
+    	def value = base.doSomething()
+    	assert value == "TestBase"
+    	
+    	
+    	base = new SuperDerived()
+    	value = base.doSomething()
+    	assert value == "TestDerivedTestBase"
+    }
+
+    void testBug2() {
+    	def base = new SuperBase()
+    	def value = base.foo(2)
+    	assert value == "TestBase2"
+    	
+    	
+    	base = new SuperDerived()
+    	value = base.foo(3)
+    	assert value == "TestDerived3TestBase3"
+    }
+
+    void testBug3() {
+    	def base = new SuperBase()
+    	def value = base.foo(2,3)
+    	assert value == "foo(x,y)Base2,3"
+    	
+    	
+    	base = new SuperDerived()
+    	value = base.foo(3,4)
+    	assert value == "foo(x,y)Derived3,4foo(x,y)Base3,4"
+    }
+
+    void testBug4() {
+    	def base = new SuperBase("Cheese")
+    	def value = base.name
+    	assert value == "Cheese"
+    	
+    	
+    	base = new SuperDerived("Cheese")
+    	value = base.name
+    	assert value == "CheeseDerived"
+    }
+    
+    void testCallsToSuperMethodsReturningPrimitives(){
+       def base = new SuperBase("super cheese")
+       assert base.longMethod() == 1
+       assert base.intMethod() == 1
+       assert base.boolMethod() == true
+       
+       base = new SuperDerived("derived super cheese")
+       assert base.longMethod() == 1
+       assert base.intMethod() == 1
+       assert base.boolMethod() == true       
+    }
+}
+
+class SuperBase {
+    String name
+
+    SuperBase() {
+    }
+    
+    SuperBase(String name) {
+        this.name = name
+    }
+    
+    def doSomething() {
+    	"TestBase"
+    }
+
+    def foo(param) {
+    	"TestBase" + param
+    }
+    
+    def foo(x, y) {
+    	"foo(x,y)Base" + x + "," + y
+    }
+    
+    boolean boolMethod(){true}
+    long longMethod(){1l}
+    int intMethod(){1i}
+}
+
+class SuperDerived extends SuperBase {
+    
+	def calls = 0
+	
+	SuperDerived() {
+	}
+	
+	SuperDerived(String name) {
+	    super(name + "Derived")
+	}
+	
+    def doSomething() {
+    	/** @todo ++calls causes bug */
+    	//calls++
+    	/*
+    	calls = calls + 1
+    	assert calls < 3
+    	*/
+    	
+    	"TestDerived" + super.doSomething()
+    }
+	
+    def foo(param) {
+    	"TestDerived" + param + super.foo(param)
+    }
+	
+    def foo(x, y) {
+    	"foo(x,y)Derived" + x + "," + y + super.foo(x, y)
+    }
+    
+    // we want to ensure that a call with super, which is directly added into 
+    // bytecode without calling MetaClass does correct boxing
+    boolean booMethod(){super.boolMethod()}
+    int intMethod(){super.intMethod()}
+    long longMethod(){super.longMethod()}
+}
+
diff --git a/groovy/src/test/groovy/bugs/SuperMethodBug.groovy b/groovy/src/test/groovy/bugs/SuperMethodBug.groovy
new file mode 100644
index 0000000..5eead63
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/SuperMethodBug.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class SuperMethodBug extends GroovyTestCase {
+     
+    void testBug() {
+    	def base = new TestBase("yyy")
+    	def value = base.doSomething()
+    	assert value == "TestBase"
+    	
+    	base = new TestDerived("abc")
+    	value = base.doSomething()
+    	assert value == "TestDerivedTestBase"
+    }
+
+}
diff --git a/groovy/src/test/groovy/bugs/SynchronizedBytecodeBug.groovy b/groovy/src/test/groovy/bugs/SynchronizedBytecodeBug.groovy
new file mode 100644
index 0000000..ea048de
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/SynchronizedBytecodeBug.groovy
@@ -0,0 +1,113 @@
+package groovy.bugs
+
+/**
+ * @author Guillaume Laforge
+ */
+class SynchronizedBytecodeBug extends GroovyTestCase {
+
+    /**
+     * Groovy's bytecode associated with syncrhonized(foo) construct used to generate invalid bytecode
+     * This test method shows that the standard wait()/notify() works.
+     */
+    void testSynchronized() {
+        Integer foo = 0
+
+        Thread.start{
+            println "sleeping for a moment"
+            sleep 1000
+            println "slept and synchronizing from thread"
+            synchronized(foo) {
+                println "notifying"
+                foo.notify()
+                println "notified"
+            }
+        }
+
+        println "synchronizing"
+        synchronized(foo) {
+            println "starting to wait"
+            foo.wait()
+            println "waited"
+        }
+
+        // if this point is reached, the test worked :-)
+        assert true
+    }
+  
+  /* more tests to ensure a monitor exit is done at the right place */
+    
+  void testBreakInSynchronized() {
+    Object lock = new Object()
+    while (true) {
+	    synchronized(lock) {
+    		break
+    	}
+    }
+    checkNotHavingAMonitor(lock)
+  }
+  
+  void testContinueInSynchronized() {
+    Object lock = new Object()  
+    boolean b = true
+    while (b) {
+	    synchronized(lock) {
+	        b = false
+    		continue
+    	}
+    }
+    checkNotHavingAMonitor(lock)
+  }
+  
+  void testReturnInSynchronized() {
+    Object lock = new Object()  
+    methodWithReturn(lock)
+    checkNotHavingAMonitor(lock)
+  }
+  
+  def methodWithReturn(lock) {
+    synchronized (lock) {
+      return
+    }
+  }
+  
+  void testBreakInClosureWithSynchronized() {
+    Object lock = new Object()
+    def c = {
+      while (true) {
+	      synchronized(lock) {
+    	    break
+    	  }
+      }
+      checkNotHavingAMonitor(lock)
+    }
+    c()
+    checkNotHavingAMonitor(lock)
+  }
+  
+  void testContinueInClosureWithSynchronized() {
+    Object lock = new Object()
+    def c = {
+      boolean b = true
+      while (b) {
+	      synchronized(lock) {
+    	    b = false
+    	    continue
+    	  }
+      }
+      checkNotHavingAMonitor(lock)
+    }
+    c()
+    checkNotHavingAMonitor(lock)
+  }
+  
+  def checkNotHavingAMonitor(Object lock){
+    // if we call notify* or wait without having the
+    // monitor, we get an exception.
+    try {
+      lock.notifyAll()
+      assert false,"should have no monitor!"
+    } catch (IllegalMonitorStateException imse) {
+      assert true
+    }
+  }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/TedsClosureBug.groovy b/groovy/src/test/groovy/bugs/TedsClosureBug.groovy
new file mode 100644
index 0000000..dd58b65
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/TedsClosureBug.groovy
@@ -0,0 +1,93 @@
+package groovy.bugs
+
+import groovy.xml.MarkupBuilder
+
+/**
+ * @author Ted Leung
+ * @version $Revision$
+ */
+class TedsClosureBug extends GroovyTestCase {
+    def EXPECTED= '''<atom>
+  <title>Ted Leung off the air</title>
+  <link>http://www.sauria.com/noblog</link>
+  <author>
+    <person>
+      <name>Ted Leung</name>
+      <url>http://www.sauria.com/blog</url>
+      <email>twl@sauria.com</email>
+    </person>
+  </author>
+  <entry>
+    <title>one</title>
+    <summary>first post</summary>
+  </entry>
+  <entry>
+    <title>two</title>
+    <summary>the second post</summary>
+  </entry>
+  <entry>
+    <title>three</title>
+    <summary>post the third</summary>
+  </entry>
+  <entry>
+    <title>four</title>
+    <summary>the ponderous fourth post</summary>
+  </entry>
+</atom>'''
+
+    void testBug() {
+		def f = new Feed()
+		f.author = new Person(name:'Ted Leung',url:'http://www.sauria.com/blog', email:'twl@sauria.com')
+		f.entries = [ new Entry(title:'one',summary:'first post'), new Entry(title:'two',summary:'the second post'), new Entry(title:'three', summary:'post the third'), new Entry(title:'four',summary:'the ponderous fourth post') ]
+		def sw = new StringWriter()
+		def xml = new MarkupBuilder(sw)
+
+		def atom = xml.atom() {
+		  title("Ted Leung off the air")
+		  link("http://www.sauria.com/noblog")
+		  author() {
+		    person() {
+		      name(f.author.name)
+		      url(f.author.url)
+		      email(f.author.email)
+		    }
+		  }
+		  for (e in f.entries) {
+		    entry() {
+		      title(e.title)
+		      summary(e.summary)
+		    }
+		  }
+		}
+		StringTestUtil.assertMultilineStringsEqual(EXPECTED, sw.toString())
+	}
+}
+
+class Feed {
+    String title
+    String link
+    Person author
+    String tagline
+    String generator
+    String copyright
+    String modified
+    List entries
+}
+
+class Entry {
+    String title
+    String link
+    String id
+    String summary
+    String content
+    Person author
+    String created
+    String issued
+    String modified
+}
+
+class Person {
+    String name
+    String url
+    String email
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/TestBase.groovy b/groovy/src/test/groovy/bugs/TestBase.groovy
new file mode 100644
index 0000000..e2dd9ce
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/TestBase.groovy
@@ -0,0 +1,25 @@
+package groovy.bugs
+
+/**
+ * A base class for testing constructors
+ * 
+ * @version $Revision$
+ */
+
+ class TestBase {
+
+     String foo
+     
+     TestBase() {
+     }
+     
+     TestBase(String aFoo) {
+         this.foo = aFoo
+     }
+     /** @todo fix bug
+     */
+     
+     def doSomething() {
+     	"TestBase"
+     }
+ }
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/TestCaseBug.groovy b/groovy/src/test/groovy/bugs/TestCaseBug.groovy
new file mode 100644
index 0000000..2ee291b
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/TestCaseBug.groovy
@@ -0,0 +1,24 @@
+package groovy.bugs
+
+import junit.framework.TestCase
+
+/**
+ * @version $Revision$
+ */
+class TestCaseBug extends TestCase {
+
+    // using def here is wrong
+    TestCaseBug(String name) {
+    		super(name)
+    	}
+    	
+    	void testDummy() {
+    		println "worked!"
+    	}
+
+    static void main(args) {
+        def foo = new TestCaseBug("hey")
+        foo.testDummy()
+    }
+    
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/TestDerived.groovy b/groovy/src/test/groovy/bugs/TestDerived.groovy
new file mode 100644
index 0000000..666be03
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/TestDerived.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs
+
+/**
+ * A base class for testing constructors
+ * 
+ * @version $Revision$
+ */
+
+ class TestDerived extends TestBase {
+
+     TestDerived(String aFoo) {
+         super(aFoo)
+     }
+     
+     def doSomething() {
+     	"TestDerived" + super.doSomething()
+     }
+ }
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/TestSupport.java b/groovy/src/test/groovy/bugs/TestSupport.java
new file mode 100644
index 0000000..24531d8
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/TestSupport.java
@@ -0,0 +1,83 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.bugs;
+
+import groovy.util.GroovyTestCase;
+
+import java.util.Arrays;
+import java.util.Iterator;
+
+/**
+ * Base class for test cases
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TestSupport extends GroovyTestCase {
+
+    public String[] getMockArguments() {
+        return new String[]{"a", "b", "c"};
+    }
+
+    public static String mockStaticMethod() {
+        return "cheese";
+    }
+
+    public static String getMockStaticProperty() {
+        return "cheese";
+    }
+
+    public static int[] getIntArray() {
+        return new int[]{1, 2, 3, 4, 5};
+    }
+
+    public Iterator iterator() {
+        System.out.println("Calling custom iterator() method for " + this);
+
+        return Arrays.asList(getMockArguments()).iterator();
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/ToStringBug.groovy b/groovy/src/test/groovy/bugs/ToStringBug.groovy
new file mode 100644
index 0000000..c08e79e
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ToStringBug.groovy
@@ -0,0 +1,21 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class ToStringBug extends GroovyTestCase {
+     
+    void testBug() {
+    	println "Starting test"
+    	
+    	def value = toString()
+    	assert value != null
+    	
+    	println value
+    	println "Found value ${value}"
+    }
+    
+    String toString() {
+    	return super.toString() + "[hey]"
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/TryCatch2Bug.groovy b/groovy/src/test/groovy/bugs/TryCatch2Bug.groovy
new file mode 100644
index 0000000..2382d62
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/TryCatch2Bug.groovy
@@ -0,0 +1,21 @@
+package groovy.bugs
+
+/**
+ * @author Morgan Hankins 
+ * @version $Revision$
+ */
+class TryCatch2Bug extends GroovyTestCase {
+    
+    void testBug() {
+        try {
+        }
+        catch (Throwable t) { 
+        } 
+    }
+    
+    void testBug2() {
+    	try {
+    		def x = 123
+    	}
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/TryCatchBug.groovy b/groovy/src/test/groovy/bugs/TryCatchBug.groovy
new file mode 100644
index 0000000..9619a30
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/TryCatchBug.groovy
@@ -0,0 +1,17 @@
+package groovy.bugs
+
+/**
+ * @author John Wilson
+ * @version $Revision$
+ */
+class TryCatchBug extends GroovyTestCase {
+    
+    void testBug() {
+        try {
+            println("Hello")
+        }
+        finally {
+            println("Finally")
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/UnknownVariableBug.groovy b/groovy/src/test/groovy/bugs/UnknownVariableBug.groovy
new file mode 100644
index 0000000..85ed923
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/UnknownVariableBug.groovy
@@ -0,0 +1,19 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class UnknownVariableBug extends GroovyTestCase {
+    void testBug() {
+        def shell = new GroovyShell()
+        shouldFail {
+            shell.evaluate """
+                def x = foo
+            """
+        }
+        shell.evaluate """
+            foo = 1
+            def x = foo
+        """
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/UseClosureInClosureBug.groovy b/groovy/src/test/groovy/bugs/UseClosureInClosureBug.groovy
new file mode 100644
index 0000000..0343668
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/UseClosureInClosureBug.groovy
@@ -0,0 +1,14 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class UseClosureInClosureBug extends GroovyTestCase {
+    void testBugWithPrintln() {
+        def inner = { it * 3 }
+        def outer1 = { inner(it) + 5 }
+        def outer2 = { inner(it + 5) }
+        assert 17 == outer1(4)
+        assert 27 == outer2(4)
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/UseStaticInClosureBug.groovy b/groovy/src/test/groovy/bugs/UseStaticInClosureBug.groovy
new file mode 100644
index 0000000..560d745a
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/UseStaticInClosureBug.groovy
@@ -0,0 +1,27 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class UseStaticInClosureBug extends GroovyTestCase {
+
+    static def stuff = [:]
+
+    void testBug() {
+        [1,2,3].each { stuff[it] = "dog" }
+
+        assert stuff.size() == 3
+        assert stuff[2] == "dog"
+    }
+
+    void testBug2() {
+        doStatic()
+    }
+
+    static def doStatic() {
+        [1,2,3].each { stuff[it] = "dog" }
+
+        assert stuff.size() == 3
+        assert stuff[2] == "dog"
+    }
+}
diff --git a/groovy/src/test/groovy/bugs/VariablePrecedence.groovy b/groovy/src/test/groovy/bugs/VariablePrecedence.groovy
new file mode 100644
index 0000000..29949ab
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/VariablePrecedence.groovy
@@ -0,0 +1,55 @@
+package groovy.bugs
+
+/**
+ * @author John Wilson
+ * @version $Revision$
+ */
+class VariblePrecedence extends GroovyTestCase {
+    
+    void testVariablePrecedence() {
+ 
+        assertScript( """
+            class VariableFoo {
+                def x = 100
+                def y = 93
+                def c = {x -> assert x == 1; assert y == 93; }
+
+                static void main(args) {
+                    def vfoo = new VariableFoo()
+                    vfoo.c.call(1)
+
+                    def z = 874;
+                    1.times { assert vfoo.x == 100; assert z == 874; z = 39; }
+                    assert z == 39;
+
+                    vfoo.local();
+                }
+
+                void local() {
+                    c.call(1);
+
+                    def z = 874;
+                    1.times { assert x == 100; assert z == 874; z = 39; }
+                    assert z == 39;
+                }
+            }
+
+        """ );
+
+    }
+
+
+/*
+ * CURRENTLY BROKEN.  Variable scoping needs an overhaul to * fix it.
+ */
+    void testVariablePrecedenceInScript_FAILS() { if (notYetImplemented()) return
+        assertScript( """
+            c = { x -> assert x == 1; assert y == 93; }
+            x = 100;
+            y = 93;
+
+            c.call(1);
+        """);
+    }
+
+}
diff --git a/groovy/src/test/groovy/bugs/VariablePrecedenceTest.groovy b/groovy/src/test/groovy/bugs/VariablePrecedenceTest.groovy
new file mode 100644
index 0000000..03900ef
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/VariablePrecedenceTest.groovy
@@ -0,0 +1,28 @@
+package groovy.bugs
+
+class VariablePrecedenceTest extends GroovyTestCase {
+    def x = 100
+    def y = 93
+    def c = {x -> assert x == 1; assert y == 93; }
+
+    void testFoo() {
+        String[] args = ["a"]
+        main(args)
+    }
+
+    static void main(args) {
+        def vfoo = new VariablePrecedenceTest()
+        vfoo.c.call(1)
+        def z = 874;
+        1.times { assert vfoo.x == 100; assert z == 874; z = 39; }
+        assert z == 39;
+        vfoo.local();
+    }
+
+    void local() {
+        c.call(1);
+        def z = 874;
+        1.times { assert x == 100; assert z == 874; z = 39; }
+        assert z == 39;
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/VariableScopingBug.groovy b/groovy/src/test/groovy/bugs/VariableScopingBug.groovy
new file mode 100644
index 0000000..86ade01
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/VariableScopingBug.groovy
@@ -0,0 +1,43 @@
+package groovy.bugs
+
+/**
+ * @version $Revision$
+ */
+class VariableScopingBug extends TestSupport {
+    
+    void testBug() {
+        // undeclared variable x
+
+        shouldFail {
+            def shell = new GroovyShell()
+            shell.evaluate("""
+                class SomeTest {
+                    void run() {
+                        for (z in 0..2) {
+                            def x = [1, 2, 3]
+                        }
+
+                        for (t in 0..3) {
+                            for (y in x) {
+                                println x
+                            }
+                        }
+                    }
+               }
+               new SomeTest().run()""")
+           }
+    }
+
+    void testVariableReuse() {
+        def shell = new GroovyShell()
+        shell.evaluate("""
+            for (z in 0..2) {
+                def x = [1, 2, 3]
+            }
+
+            for (t in 0..3) {
+                def x = 123
+                println x
+            }""")
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/ZoharsBug.groovy b/groovy/src/test/groovy/bugs/ZoharsBug.groovy
new file mode 100644
index 0000000..4d27aa7
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/ZoharsBug.groovy
@@ -0,0 +1,18 @@
+package groovy.bugs
+
+/**
+ * @author Zohar Melamed
+ * @version $Revision$
+ */
+class ZoharsBug extends GroovyTestCase {
+    
+    void testBug() {
+        def values = [1,2,3,4]
+        def result = bloo(values, {it > 1})
+        assert result == [2, 3, 4]
+    }
+    
+    def bloo(a,b){
+        return a.findAll{b.call(it)}
+    }    
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/bugs/bug1567_script.groovy b/groovy/src/test/groovy/bugs/bug1567_script.groovy
new file mode 100644
index 0000000..ba63851
--- /dev/null
+++ b/groovy/src/test/groovy/bugs/bug1567_script.groovy
@@ -0,0 +1,5 @@
+package groovy.bugs
+
+t = new Thread() { println "Groovy" }
+t.start()
+t.join()
diff --git a/groovy/src/test/groovy/execute/ExecuteTest_LinuxSolaris.groovy b/groovy/src/test/groovy/execute/ExecuteTest_LinuxSolaris.groovy
new file mode 100644
index 0000000..ac0ef6e
--- /dev/null
+++ b/groovy/src/test/groovy/execute/ExecuteTest_LinuxSolaris.groovy
@@ -0,0 +1,57 @@
+#! /usr/bin/env groovy 
+
+package groovy.execute
+
+/**
+ *  Test to ensure that the execute mechanism works fine on *nix-like systems.  For these OSs we
+ *  can effectively guarantee the existance of some programs that we can run.  Assume the search
+ *  path is partway reasonable so we can access sh and echo.
+ *
+ *  <p>These test are a bit trivial but at least they are here :-)</p>
+ *
+ *  @author Russel Winder
+ *  @version $Revision$
+ */
+class ExecuteTest_LinuxSolaris extends GroovyTestCase {
+  void testShellEchoOneArray ( ) {
+    def process = ( [ "sh" , "-c" , "echo 1" ] as String[] ).execute ( )
+    process.waitFor ( )
+    assert process.in.text.trim ( ) == "1"
+  }
+  void testShellEchoOneList ( ) {
+    def process = [ "sh" , "-c" , "echo 1" ].execute ( )
+    process.waitFor ( )
+    assert process.in.text.trim ( ) == "1"
+  }
+  void testEchoOneArray ( ) {
+    try {
+      def process = ( [ "echo 1" ] as String[] ).execute ( )
+      process.waitFor ( )
+       fail ( "Should have thrown java.io.IOException: echo 1: not found" )
+    }
+    catch ( IOException ioe ) { }
+  }
+  void testEchoOneList ( ) {
+    try {
+      def process = [ "echo 1" ].execute ( )
+      process.waitFor ( )
+       fail ( "Should have thrown java.io.IOException: echo 1: not found" )
+    }
+    catch ( IOException ioe ) { }
+  }
+  void testEchoOneScalar ( ) {
+    def process = "echo 1".execute ( )
+    process.waitFor ( )
+    assert process.in.text.trim ( ) == "1"
+  }
+  void testEchoArray ( ) {
+    def process = ( [ "echo" , "1" ] as String[] ).execute ( )
+    process.waitFor ( )
+    assert process.in.text.trim ( ) == "1"
+   }
+  void testEchoList ( ) {
+    def process = [ "echo" , "1" ].execute ( )
+    process.waitFor ( )
+    assert process.in.text.trim ( ) == "1"
+   }
+}
diff --git a/groovy/src/test/groovy/execute/ExecuteTest_Windows.groovy b/groovy/src/test/groovy/execute/ExecuteTest_Windows.groovy
new file mode 100644
index 0000000..e368230
--- /dev/null
+++ b/groovy/src/test/groovy/execute/ExecuteTest_Windows.groovy
@@ -0,0 +1,53 @@
+package groovy.execute
+
+/**
+ *  Test to ensure that the execute mechanism works fine on Windows systems.
+ *
+ *  <p>These test are a bit trivial but at least they are here :-)</p>
+ *
+ *  @author Paul King
+ *  @version $Revision: 6165 $
+ */
+class ExecuteTest_Windows extends GroovyTestCase {
+  void testCmdEchoOneArray() {
+    def process = ( [ "cmd.exe" , "/c" , "echo 1" ] as String[] ).execute()
+    process.waitFor()
+    assert process.in.text.trim() == "1"
+  }
+
+  void testCmdEchoOneList() {
+    def process = [ "cmd.exe" , "/c" , "echo 1" ].execute ( )
+    process.waitFor()
+    assert process.in.text.trim() == "1"
+  }
+
+  void testCmdDate() {
+    def process = "cmd.exe /c date.exe /t".execute()
+    process.waitFor()
+    def theDate = process.in.text.trim()
+    def minLen = 8
+      // dk: the length depends on the locale settings and usually contains two digits for
+      //     each day/month/year where the separation char may differ. This test may fail for
+      //     locales with even shorter date representations. As soon as this happens, please
+      //     adapt the minLen value.
+    assert theDate.size() >= minLen, "Expected '$theDate' to be at least $minLen chars long"
+  }
+
+  void testEchoOneArray() {
+    try {
+      def process = ( [ "echo 1" ] as String[] ).execute()
+      process.waitFor()
+      fail ( "Should have thrown java.io.IOException: echo 1: not found" )
+    }
+    catch ( IOException ioe ) { }
+  }
+
+  void testEchoOneList() {
+    try {
+      def process = [ "echo 1" ].execute()
+      process.waitFor()
+      fail ( "Should have thrown java.io.IOException: echo 1: not found" )
+    }
+    catch ( IOException ioe ) { }
+  }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/gpath/GPathTest.groovy b/groovy/src/test/groovy/gpath/GPathTest.groovy
new file mode 100644
index 0000000..faee8e0
--- /dev/null
+++ b/groovy/src/test/groovy/gpath/GPathTest.groovy
@@ -0,0 +1,23 @@
+package groovy.gpath
+
+/**
+ * Some GPath tests using maps and lists
+ */
+class GPathTest extends GroovyTestCase {
+
+    void testSimpleGPathExpressions() {
+        def tree = createTree()
+        assert tree.people.find { it.name == 'James' }.location == 'London'
+        assert tree.people.name == ['James', 'Bob']
+        def expected = ['James works on 2 project(s)', 'Bob works on 2 project(s)']
+        assert tree.people.findAll { it.projects.size() > 1 }.collect { it.name + ' works on ' + it.projects.size() + " project(s)" } == expected
+    }
+    protected def createTree() {
+        return [	
+            'people': [
+                ['name' : 'James', 'location':'London', 'projects':['geronimo', 'groovy'] ],
+                ['name' : 'Bob', 'location':'Atlanta', 'projects':['drools', 'groovy'] ]
+			] 
+		]
+    }
+}
diff --git a/groovy/src/test/groovy/gpath/NodeGPathTest.groovy b/groovy/src/test/groovy/gpath/NodeGPathTest.groovy
new file mode 100644
index 0000000..f4cf9d6
--- /dev/null
+++ b/groovy/src/test/groovy/gpath/NodeGPathTest.groovy
@@ -0,0 +1,44 @@
+package groovy.gpath
+
+/**
+ * Some GPath tests using trees
+ */
+class NodeGPathTest extends GroovyTestCase {
+
+    void testFind() {
+        def tree = createTree()
+        assert tree.person.find { it['@name'] == 'James' }.location[0]['@name'] == 'London'
+    }
+
+    void testFindAll() {
+        def tree = createTree()
+        def peopleWithNameBob = tree.person.findAll { it['@name'] != 'Bob' }
+        assert peopleWithNameBob.size() == 1
+    }
+
+    void testCollect() {
+        def tree = createTree()
+        def namesOfAllPeople = tree.person.collect { it['@name'] }
+        assert namesOfAllPeople == ['James', 'Bob']
+    }
+
+    protected def createTree() {
+        def builder = NodeBuilder.newInstance()
+        def root = builder.people() {
+            person(name:'James') {
+                location(name:'London')
+                projects {
+                    project(name:'geronimo')
+                }
+            }
+            person(name:'Bob') {
+                location(name:'Atlanta')
+                projects {
+                    project(name:'drools')
+                }
+            }
+        }
+        assert root != null
+        return root
+    }
+}
diff --git a/groovy/src/test/groovy/inspect/InspectorTest.java b/groovy/src/test/groovy/inspect/InspectorTest.java
new file mode 100644
index 0000000..6252dd1
--- /dev/null
+++ b/groovy/src/test/groovy/inspect/InspectorTest.java
@@ -0,0 +1,248 @@
+package groovy.inspect;
+
+import groovy.lang.GroovyShell;
+import groovy.lang.MetaMethod;
+import groovy.lang.MetaProperty;
+import groovy.lang.PropertyValue;
+import org.jmock.Mock;
+import org.jmock.cglib.MockObjectTestCase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.*;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class InspectorTest extends MockObjectTestCase implements Serializable {
+    public String someField = "only for testing";
+    public static final String SOME_CONST = "only for testing";
+
+    public InspectorTest(String name) {
+        super(name);
+    }
+
+    // additional constructor not used directly but exercises inspection code
+    public InspectorTest(String name, Object other) throws RuntimeException, Throwable {
+        super(name);
+    }
+
+    public void testCtor() {
+        Object object = new Object();
+        Inspector inspector = new Inspector(object);
+        assertEquals(object, inspector.getObject());
+        try {
+            new Inspector(null);
+            fail("should have thown IllegalArgumentException");
+        } catch (Exception expected) {
+        }
+    }
+
+    public void testClassPropsJava() {
+        Inspector insp = new Inspector(this);
+        String[] classProps = insp.getClassProps();
+        assertEquals("package groovy.inspect", classProps[Inspector.CLASS_PACKAGE_IDX]);
+        assertEquals("public class InspectorTest", classProps[Inspector.CLASS_CLASS_IDX]);
+        assertEquals("implements Serializable ", classProps[Inspector.CLASS_INTERFACE_IDX]);
+        assertEquals("extends MockObjectTestCase", classProps[Inspector.CLASS_SUPERCLASS_IDX]);
+        assertEquals("is Primitive: false, is Array: false, is Groovy: false", classProps[Inspector.CLASS_OTHER_IDX]);
+    }
+
+    public void testClassPropsGroovy() throws RuntimeException, Throwable {
+        Object testObject = new GroovyShell().evaluate("class Test {def meth1(a,b){}}\nreturn new Test()");
+        Inspector insp = new Inspector(testObject);
+        String[] classProps = insp.getClassProps();
+        assertEquals("package n/a", classProps[Inspector.CLASS_PACKAGE_IDX]);
+        assertEquals("public class Test", classProps[Inspector.CLASS_CLASS_IDX]);
+        assertEquals("implements GroovyObject ", classProps[Inspector.CLASS_INTERFACE_IDX]);
+        assertEquals("extends Object", classProps[Inspector.CLASS_SUPERCLASS_IDX]);
+        assertEquals("is Primitive: false, is Array: false, is Groovy: true", classProps[Inspector.CLASS_OTHER_IDX]);
+    }
+
+    public void testMethods() {
+        Inspector insp = new Inspector(new Object());
+        Object[] methods = insp.getMethods();
+        assertEquals(10, methods.length);
+        String[] names = {"hashCode", "getClass", "wait", "wait", "wait", "equals", "notify", "notifyAll", "toString", "java.lang.Object"};
+        assertNameEquals(names, methods);
+        String[] details = {"JAVA", "public final", "Object", "void", "wait", "long, int", "InterruptedException"};
+        assertContains(methods, details);
+        // ctors are not considered static !
+        String[] ctorDetails = {"JAVA", "public", "Object", "Object", "java.lang.Object", "", ""};
+        assertContains(methods, ctorDetails);
+    }
+
+    public void testStaticMethods() {
+        Inspector insp = new Inspector(this);
+        Object[] methods = insp.getMethods();
+        for (int i = 0; i < methods.length; i++) {
+            String[] strings = (String[]) methods[i];
+            if (strings[1].indexOf("static") > -1) return; // ok, found one static method
+        }
+        fail("there should have been at least one static method in this TestCase, e.g. 'fail'.");
+    }
+
+    public void testMetaMethods() {
+        Inspector insp = new Inspector(new Object());
+        Object[] metaMethods = insp.getMetaMethods();
+        String[] names = {"sleep", "sleep", "println", "println", "println", "find", "print", "print", "each", "invokeMethod", "asType",
+                "inspect", "is", "isCase", "identity", "getAt", "putAt", "dump", "getMetaPropertyValues", "getProperties",
+                "use", "use", "use", "printf", "printf", "eachWithIndex", "every", "every", "any", "any", "grep", "collect", "collect", "findAll",
+                "findIndexOf", "iterator", "addShutdownHook", "sprintf", "sprintf", "with", "inject", "getMetaClass"
+        };
+        assertEquals(names.length, metaMethods.length);
+        assertNameEquals(names, metaMethods);
+        String[] details = {"GROOVY", "public", "Object", "void", "println", "Object", "n/a"};
+        assertContains(metaMethods, details);
+    }
+
+    static class ClassWithPrivate {
+        private String hidden = "you can't see me";
+    }
+
+    // TODO: if our code can never access inspect in this way, it would be better
+    // to move this to a boundary class and then we wouldn't need this test
+    public void testInspectPrivateField() throws NoSuchFieldException {
+        ClassWithPrivate underInspection = new ClassWithPrivate();
+        Field field = underInspection.getClass().getDeclaredField("hidden");
+        Inspector inspector = getTestableInspector(underInspection);
+        String[] result = inspector.fieldInfo(field);
+        assertEquals(Inspector.NOT_APPLICABLE, result[Inspector.MEMBER_VALUE_IDX]);
+    }
+
+    // TODO: if our code can never access inspect in this way, it would be better
+    // to move this to a boundary class and then we wouldn't need this test
+    public void testInspectUninspectableProperty() {
+        Object dummyInstance = new Object();
+        Inspector inspector = getTestableInspector(dummyInstance);
+        Class[] paramTypes = {Object.class, MetaProperty.class};
+        Object[] params = {null, null};
+        Mock mock = mock(PropertyValue.class, paramTypes, params);
+        mock.expects(once()).method("getType");
+        mock.expects(once()).method("getName");
+        mock.expects(once()).method("getValue").will(throwException(new RuntimeException()));
+        PropertyValue propertyValue = (PropertyValue) mock.proxy();
+        String[] result = inspector.fieldInfo(propertyValue);
+        assertEquals(Inspector.NOT_APPLICABLE, result[Inspector.MEMBER_VALUE_IDX]);
+    }
+
+    private Inspector getTestableInspector(Object objectUnderInspection) {
+        return new Inspector(objectUnderInspection) {
+            public String[] fieldInfo(Field field) {
+                return super.fieldInfo(field);
+            }
+
+            public String[] fieldInfo(PropertyValue pv) {
+                return super.fieldInfo(pv);
+            }
+        };
+    }
+
+    public void testSortWithDifferentOrigin() {
+        String[] details2 = {"JAVA", "public", "Object", "void", "println", "Object", "n/a"};
+        String[] details1 = {"GROOVY", "public", "Object", "void", "println", "Object", "n/a"};
+        String[] first = sortWithMemberComparator(details1, details2);
+        assertEquals("GROOVY", first[0]);
+    }
+
+    public void testSortWithDifferentModifier() {
+        String[] details2 = {null, "public", "Object", "void", "println", "Object", "n/a"};
+        String[] details1 = {null, "private", "Object", "void", "println", "Object", "n/a"};
+        String[] first = sortWithMemberComparator(details1, details2);
+        assertEquals("private", first[1]);
+    }
+
+    private String[] sortWithMemberComparator(String[] details1, String[] details2) {
+        List details = new ArrayList();
+        details.add(details1);
+        details.add(details2);
+        Inspector.sort(details);
+        return (String[]) details.get(0);
+    }
+
+    public void testStaticMetaMethods() {
+        Matcher matcher = Pattern.compile("").matcher("");
+        Inspector insp = new Inspector(matcher);
+        Object[] metaMethods = insp.getMetaMethods();
+        assertUnique(Inspector.sort(Arrays.asList(metaMethods)));
+        String[] details = {"GROOVY", "public static", "Matcher", "Matcher", "getLastMatcher", "", "n/a"};
+        assertContains(metaMethods, details);
+    }
+
+    public void testFields() {
+        Inspector insp = new Inspector(this);
+        Object[] fields = insp.getPublicFields();
+        assertEquals(5, fields.length); // 3 from JMock
+        String[] names = {"someField", "SOME_CONST", "ANYTHING", "NULL", "NOT_NULL"};
+        assertNameEquals(names, fields);
+        String[] details = {"JAVA", "public", "InspectorTest", "String", "someField", "\"only for testing\""};
+        assertContains(fields, details);
+    }
+
+    public void testProperties() {
+        Inspector insp = new Inspector(this);
+        Object[] properties = insp.getPropertyInfo();
+        assertEquals(2, properties.length);
+        String[] names = {"class", "name"};
+        assertNameEquals(names, properties);
+        String[] details = {"GROOVY", "public", "n/a", "Class", "class", "class groovy.inspect.InspectorTest"};
+        assertContains(properties, details);
+    }
+
+    public void testPrint() {
+        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
+        PrintStream printStream = new PrintStream(bytes);
+        String ls = System.getProperty("line.separator");
+        String[] first = {"a", "b"};
+        String[] second = {"x", "y"};
+        Object[] memberInfo = {first, second};
+        Inspector.print(printStream, memberInfo);
+        assertEquals("0:\ta b " + ls + "1:\tx y " + ls, bytes.toString());
+        // just for coverage, print to System.out (yuck)
+        Inspector.print(memberInfo);
+    }
+
+    private void assertNameEquals(String[] names, Object[] metaMethods) {
+        Set metaSet = new HashSet();
+        for (int i = 0; i < metaMethods.length; i++) {
+            String[] strings = (String[]) metaMethods[i];
+            metaSet.add(strings[Inspector.MEMBER_NAME_IDX]);
+        }
+        Set nameSet = new HashSet(Arrays.asList(names));
+        assertEquals(nameSet, metaSet);
+    }
+
+    private void assertContains(Object[] candidates, String[] sample) {
+        String sampleBuffer = concat(sample);
+        for (int i = 0; i < candidates.length; i++) {
+            String[] entry = (String[]) candidates[i];
+            if (sampleBuffer.equals(concat(entry))) return;
+        }
+        fail("should have found sample: " + sampleBuffer);
+    }
+
+    private void assertUnique(Collection sortedMembers) {
+        if (sortedMembers.size() < 2) return;
+        Comparator comp = new Inspector.MemberComparator();
+        Iterator iter = sortedMembers.iterator();
+        Object last = iter.next();
+        while (iter.hasNext()) {
+            Object element = iter.next();
+            if (0 == comp.compare(last, element)) {
+                fail("found duplication for element " + element);
+            }
+            last = element;
+        }
+    }
+
+    private String concat(String[] details) {
+        StringBuffer detailBuffer = new StringBuffer();
+        for (int i = 0; i < details.length; i++) {
+            detailBuffer.append(details[i]);
+            detailBuffer.append(" ");
+        }
+        return detailBuffer.toString();
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/io/PlatformLineWriterTest.java b/groovy/src/test/groovy/io/PlatformLineWriterTest.java
new file mode 100644
index 0000000..eb6b239
--- /dev/null
+++ b/groovy/src/test/groovy/io/PlatformLineWriterTest.java
@@ -0,0 +1,27 @@
+package groovy.io;
+
+import groovy.text.SimpleTemplateEngine;
+import groovy.text.Template;
+import junit.framework.TestCase;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+public class PlatformLineWriterTest extends TestCase {
+
+    public void testPlatformLineWriter() throws IOException, ClassNotFoundException {
+        String LS = System.getProperty("line.separator");
+        Map binding = new HashMap();
+        binding.put("first", "Tom");
+        binding.put("last", "Adams");
+        Template template = new SimpleTemplateEngine().createTemplate("<%= first %>\n<% println last %>");
+        StringWriter stringWriter = new StringWriter();
+        Writer platformWriter = new PlatformLineWriter(stringWriter);
+        template.make(binding).writeTo(platformWriter);
+        assertEquals("Tom" + LS + "Adams" + LS, stringWriter.toString());
+    }
+}
diff --git a/groovy/src/test/groovy/lang/BenchmarkInterceptorTest.groovy b/groovy/src/test/groovy/lang/BenchmarkInterceptorTest.groovy
new file mode 100644
index 0000000..823dce8
--- /dev/null
+++ b/groovy/src/test/groovy/lang/BenchmarkInterceptorTest.groovy
@@ -0,0 +1,35 @@
+package groovy.lang
+
+/**
+* Test for the BenchmarkInterceptor
+* @author Dierk Koenig
+**/
+class BenchmarkInterceptorTest extends GroovyTestCase{
+
+    Interceptor benchmarkInterceptor
+    def proxy
+
+    void setUp() {
+        benchmarkInterceptor = new BenchmarkInterceptor()
+        proxy = ProxyMetaClass.getInstance(Date.class)
+        proxy.setInterceptor(benchmarkInterceptor)
+    }
+
+    void testSimpleInterception() {
+        proxy.use {
+             def x = new Date(0)
+             x++
+        }
+        def stats = benchmarkInterceptor.statistic()
+        assertEquals 2, stats.size()
+        assert stats.find{it[0] == 'ctor'}
+        assert stats.find{it[0] == 'next'}
+        assert stats.every{it[1] == 1}
+        assert stats.every{it[2] < 200}
+    }
+
+
+}
+
+
+
diff --git a/groovy/src/test/groovy/lang/BigDecimalRangeTest.java b/groovy/src/test/groovy/lang/BigDecimalRangeTest.java
new file mode 100644
index 0000000..6772b35
--- /dev/null
+++ b/groovy/src/test/groovy/lang/BigDecimalRangeTest.java
@@ -0,0 +1,71 @@
+/*
+ $Id: BigDecimalRangeTest.java,v 1.1 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+import java.math.BigDecimal;
+
+/**
+ * Tests {@link ObjectRange}s of {@link BigDecimal}s.
+ *
+ * @author Edwin Tellman
+ */
+public class BigDecimalRangeTest extends NumberRangeTest {
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Range createRange(int from, int to) {
+        return new ObjectRange(new BigDecimal(from), new BigDecimal(to));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Comparable createValue(int value) {
+        return new BigDecimal(value);
+    }
+
+}
diff --git a/groovy/src/test/groovy/lang/CharacterRangeTest.java b/groovy/src/test/groovy/lang/CharacterRangeTest.java
new file mode 100644
index 0000000..0722bf2
--- /dev/null
+++ b/groovy/src/test/groovy/lang/CharacterRangeTest.java
@@ -0,0 +1,64 @@
+/**
+ *
+ */
+package groovy.lang;
+
+import junit.framework.TestCase;
+
+import java.util.Iterator;
+
+/**
+ * Provides a few unit tests for {@link ObjectRange}s of {@link Character}s.  More tests are needed.
+ *
+ * @author Edwin Tellman
+ */
+public class CharacterRangeTest extends TestCase {
+    /**
+     * The range to test.
+     */
+    private ObjectRange range = null;
+
+    /**
+     * The first character in the range.
+     */
+    private final Character FROM = new Character('a');
+
+    /**
+     * The last character in the range.
+     */
+    private final Character TO = new Character('d');
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void setUp() throws Exception {
+        super.setUp();
+        range = new ObjectRange(FROM, TO);
+    }
+
+    /**
+     * Tests iterating through the range.
+     */
+    public void testIterate() {
+        Iterator iter = range.iterator();
+        assertEquals("wrong first value", FROM, iter.next());
+        for (int expected = FROM.charValue() + 1; expected <= TO.charValue(); expected++) {
+            assertEquals("wrong value", new Integer(expected), iter.next());
+        }
+    }
+
+    /**
+     * Tests getting the 'from' value.
+     */
+    public void testGetFrom() {
+        assertEquals("wrong 'from' value", FROM, range.getFrom());
+    }
+
+    /**
+     * Tests getting the 'to' value.
+     */
+    public void testGetTo() {
+        assertEquals("wrong 'to' value", TO, range.getTo());
+    }
+
+}
diff --git a/groovy/src/test/groovy/lang/ClassReloadingTest.groovy b/groovy/src/test/groovy/lang/ClassReloadingTest.groovy
new file mode 100644
index 0000000..df806d3
--- /dev/null
+++ b/groovy/src/test/groovy/lang/ClassReloadingTest.groovy
@@ -0,0 +1,45 @@
+package groovy.lang
+
+class ClassReloadingTest extends GroovyTestCase {
+
+    public void testReloading() {
+        def file = File.createTempFile("TestReload", ".groovy", new File("target"))
+        file.deleteOnExit()
+        def className = file.name - ".groovy"
+
+        def cl = new GroovyClassLoader(this.class.classLoader);
+        def currentDir = file.parentFile.absolutePath
+        cl.addClasspath(currentDir)
+        cl.shouldRecompile = true
+
+        try {
+            file.write """
+              class $className {
+                def greeting = "hello"
+              }
+            """
+            def groovyClass = cl.loadClass(className, true, false)
+            def message = groovyClass.newInstance().greeting
+            assert "hello" == message
+
+            sleep 1500
+
+            // change class
+            file.write """
+              class $className {
+                def greeting = "goodbye"
+              }
+            """
+            def success = file.setLastModified(System.currentTimeMillis())
+            assert success
+            sleep 500
+
+            // reload
+            groovyClass = cl.loadClass(className, true, false)
+            message = groovyClass.newInstance().greeting
+            assert "goodbye" == message
+        } finally {
+            file.delete()
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/ClosureResolvingTest.groovy b/groovy/src/test/groovy/lang/ClosureResolvingTest.groovy
new file mode 100644
index 0000000..e68acb9
--- /dev/null
+++ b/groovy/src/test/groovy/lang/ClosureResolvingTest.groovy
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang
+
+/**
+ * Tests how closures resolve to either a delegate or an owner for a given resolveStrategy
+ 
+ * @author Graeme Rocher
+ * @since 1.1
+  *
+ * Created: Jul 12, 2007
+ * Time: 5:45:33 PM
+ * 
+ */
+
+class ClosureResolvingTest extends GroovyTestCase {
+
+    def foo = "bar"
+    def bar = "foo"
+
+    void testResolveToSelf() {
+        def c = { foo }
+        assertEquals "bar", c.call()
+
+        c.resolveStrategy = Closure.TO_SELF
+
+        shouldFail {
+            c.call()
+        }
+
+        def metaClass = c.class.metaClass
+        metaClass.getFoo = {-> "hello!" }
+
+        c.metaClass = metaClass
+
+        assertEquals "hello!", c.call()
+
+        c = { doStuff() }
+        c.resolveStrategy = Closure.TO_SELF
+        shouldFail {
+            c.call()
+        }
+        metaClass = c.class.metaClass
+        metaClass.doStuff = {-> "hello" }
+        c.metaClass = metaClass
+
+        assertEquals "hello", c.call()
+    }
+
+    def doStuff() { "stuff" }
+
+    void testResolveDelegateFirst() {
+
+        def c = { foo }
+
+        assertEquals "bar", c.call()
+
+        c.setResolveStrategy(Closure.DELEGATE_FIRST)
+        c.delegate = [foo:"hello!"]
+
+        assertEquals "hello!", c.call()                        
+
+
+        c = { doStuff() }
+        c.setResolveStrategy(Closure.DELEGATE_FIRST)
+        
+        assertEquals "stuff", c.call()
+        c.delegate = new TestResolve1()
+        assertEquals "foo", c.call()
+
+    }
+
+    void testResolveOwnerFirst() {
+        def c = { foo }
+
+        assertEquals "bar", c.call()
+        
+        c.delegate = [foo:"hello!"]
+
+        assertEquals "bar", c.call()
+
+        c = { doStuff() }
+        c.delegate = new TestResolve1()
+        assertEquals "stuff", c.call()
+    }
+
+    void testResolveDelegateOnly() {
+        def c = { foo + bar }
+
+        assertEquals "barfoo", c.call()
+
+        c.resolveStrategy = Closure.DELEGATE_FIRST
+
+        c.delegate = new TestResolve1()
+
+        assertEquals "hellofoo", c.call()
+
+        c.resolveStrategy = Closure.DELEGATE_ONLY
+        shouldFail {
+            c.call()
+        }
+
+        c.delegate = new TestResolve2()
+
+        assertEquals "helloworld", c.call()
+
+        c = { doStuff() }
+        c.resolveStrategy = Closure.DELEGATE_ONLY
+        c.delegate = new TestResolve1()
+        assertEquals "foo", c.call()
+
+    }
+
+    void testResolveOwnerOnly() {
+        def c = { foo + bar }
+
+        assertEquals "barfoo", c.call()
+        c.resolveStrategy = Closure.OWNER_ONLY
+
+        c.delegate = new TestResolve2()
+        assertEquals "barfoo", c.call()
+
+        c = { doStuff() }
+        assertEquals "stuff", c.call()
+        c.resolveStrategy = Closure.OWNER_ONLY
+        c.delegate = new TestResolve1()
+        assertEquals "stuff", c.call()
+
+    }
+    
+    void testOwnerDelegateChain() {
+        def outerdel= new TestResolve3(del: "outer delegate" )
+        def innerdel= new TestResolve3(del: "inner delegate")
+        
+        def cout= {
+            assert delegate == outerdel
+            assert delegate.whoisThis() == outerdel
+            assert delegate.del == "outer delegate"
+            assert delegate.met() == "I'm the method inside 'outer delegate'"
+
+            assert whoisThis() == outerdel
+            assert del == "outer delegate"
+            assert met() == "I'm the method inside 'outer delegate'"
+
+            def cin= {
+                assert delegate == innerdel
+                assert delegate.whoisThis() == innerdel
+                assert delegate.del == "inner delegate"
+                assert delegate.met() == "I'm the method inside 'inner delegate'"
+
+                assert whoisThis() == outerdel
+                assert del == "outer delegate"
+                assert met() == "I'm the method inside 'outer delegate'"
+            
+            }
+
+            cin.delegate= innerdel
+            cin()
+        }
+
+        cout.delegate= outerdel
+        cout() 
+    }
+    
+}
+
+class TestResolve1 {
+    def foo = "hello"
+
+    def doStuff() { "foo" }
+}
+class TestResolve2 {
+    def foo = "hello"
+    def bar = "world"
+
+    def doStuff() { "bar" }
+}
+
+ class TestResolve3 {
+    def del;
+    String toString(){del}
+    def whoisThis() { return this }
+    def met() { return "I'm the method inside '"+del+"'" }
+ }
+
diff --git a/groovy/src/test/groovy/lang/DelegatingMetaClassTest.groovy b/groovy/src/test/groovy/lang/DelegatingMetaClassTest.groovy
new file mode 100644
index 0000000..da32764
--- /dev/null
+++ b/groovy/src/test/groovy/lang/DelegatingMetaClassTest.groovy
@@ -0,0 +1,19 @@
+package groovy.lang
+
+
+/**
+ * Tests for the DelegatingMetaClass
+ *
+ * @author Graeme Rocher
+ **/
+
+class DelegatingMetaClassTest extends GroovyTestCase {
+
+    void testIsGroovyObject() {
+        def metaClass = new DelegatingMetaClass(getClass())
+
+        assert metaClass.isGroovyObject()
+        assertEquals DelegatingMetaClassTest, metaClass.getTheClass()
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/DerivedScript.java b/groovy/src/test/groovy/lang/DerivedScript.java
new file mode 100644
index 0000000..5eec456
--- /dev/null
+++ b/groovy/src/test/groovy/lang/DerivedScript.java
@@ -0,0 +1,50 @@
+/*
+ * $Id$version
+ * Nov 23, 2003 9:02:55 PM $user Exp $
+ * 
+ * Copyright 2003 (C) Sam Pullara. All Rights Reserved.
+ * 
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met: 1. Redistributions of source code must retain
+ * copyright statements and notices. Redistributions must also contain a copy
+ * of this document. 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution. 3.
+ * The name "groovy" must not be used to endorse or promote products derived
+ * from this Software without prior written permission of The Codehaus. For
+ * written permission, please contact info@codehaus.org. 4. Products derived
+ * from this Software may not be called "groovy" nor may "groovy" appear in
+ * their names without prior written permission of The Codehaus. "groovy" is a
+ * registered trademark of The Codehaus. 5. Due credit should be given to The
+ * Codehaus - http://groovy.codehaus.org/
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *  
+ */
+package groovy.lang;
+
+/**
+ * A derived Script which adds some 'global' methods available to the script
+ */
+public abstract class DerivedScript extends Script {
+
+    public String getCheese() {
+        return "Cheddar";
+    }
+
+    public String doSomething(String food) {
+        return "I like " + food;
+    }
+
+}
diff --git a/groovy/src/test/groovy/lang/DummyGString.java b/groovy/src/test/groovy/lang/DummyGString.java
new file mode 100644
index 0000000..6bb249d
--- /dev/null
+++ b/groovy/src/test/groovy/lang/DummyGString.java
@@ -0,0 +1,84 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.lang;
+
+/**
+ * A hand crafted example GString
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DummyGString extends GString {
+
+    private String[] strings;
+    private MetaClass metaClass;
+
+    public DummyGString(Object[] values) {
+        this(values, new String[]{"Hello ", "!"});
+    }
+
+    public DummyGString(Object[] values, String[] strings) {
+        super(values);
+        this.strings = strings;
+    }
+
+    public String[] getStrings() {
+        return strings;
+    }
+
+    public MetaClass getMetaClass() {
+        return metaClass;
+    }
+
+    public void setMetaClass(MetaClass metaClass) {
+        this.metaClass = metaClass;
+    }
+
+    public Object invokeMethod(String name, Object arguments) {
+        return metaClass.invokeMethod(this, name, arguments);
+    }
+}
diff --git a/groovy/src/test/groovy/lang/EmptyRangeTest.java b/groovy/src/test/groovy/lang/EmptyRangeTest.java
new file mode 100644
index 0000000..0ee010b
--- /dev/null
+++ b/groovy/src/test/groovy/lang/EmptyRangeTest.java
@@ -0,0 +1,390 @@
+/**
+ *
+ */
+package groovy.lang;
+
+import groovy.util.GroovyTestCase;
+
+import java.util.*;
+
+/**
+ * Provides unit tests for the {@link EmptyRange} class.
+ *
+ * @author Edwin Tellman
+ */
+public class EmptyRangeTest extends GroovyTestCase {
+
+    /**
+     * The 'from' value for the {@link Range}.
+     */
+    private static final Integer AT = new Integer(17);
+
+    /**
+     * The {@link Range} to test.
+     */
+    private Range range = null;
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        range = new EmptyRange(AT);
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#getFrom()}.
+     */
+    public void testGetFrom() {
+        assertEquals("wrong 'from' value", AT, range.getFrom());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#getTo()}.
+     */
+    public void testGetTo() {
+        assertEquals("wrong 'from' value", AT, range.getTo());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#isReverse()}.
+     */
+    public void testIsReverse() {
+        assertFalse("empty range reversed", range.isReverse());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#inspect()}.
+     */
+    public void testInspect() {
+        assertEquals("wrong 'inspect' value", AT + "..<" + AT, range.inspect());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#inspect()()} with a range with a <code>null</code> 'at' value.
+     */
+    public void testInspectNullAt() {
+        final Range nullAtRange = new EmptyRange(null);
+        assertEquals("wrong inspect value", "null..<null", nullAtRange.inspect());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#toString()}.
+     */
+    public void testToString() {
+        assertEquals("wrong string value", AT + "..<" + AT, range.toString());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#toString()} with a range with a <code>null</code> 'at' value.
+     */
+    public void testToStringNullAt() {
+        final Range nullAtRange = new EmptyRange(null);
+        assertEquals("wrong string value", "null..<null", nullAtRange.toString());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#size()}.
+     */
+    public void testSize() {
+        assertEquals("wrong size", 0, range.size());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#clear()}.
+     */
+    public void testClear() {
+        range.clear();
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#isEmpty()}.
+     */
+    public void testIsEmpty() {
+        assertTrue("range not empty", range.isEmpty());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#toArray()}.
+     */
+    public void testToArray() {
+        assertArrayEquals(new Object[0], range.toArray());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#get(int)}.
+     */
+    public void testGet() {
+        try {
+            range.get(0);
+            fail("got value from empty range");
+        }
+        catch (IndexOutOfBoundsException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#remove(int)}.
+     */
+    public void testRemoveInt() {
+        try {
+            range.remove(0);
+            fail("removed value from empty range");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#add(int,java.lang.Object)}.
+     */
+    public void testAddIntObject() {
+        try {
+            range.add(0, new Integer(12));
+            fail("added value to empty range");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#indexOf(java.lang.Object)}.
+     */
+    public void testIndexOf() {
+        assertEquals("found value in empty range", -1, range.indexOf(AT));
+        assertEquals("found null in empty range", -1, range.indexOf(null));
+        assertEquals("found string in empty range", -1, range.indexOf("hello"));
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#lastIndexOf(java.lang.Object)}.
+     */
+    public void testLastIndexOf() {
+        assertEquals("found value in empty range", -1, range.lastIndexOf(AT));
+        assertEquals("found null in empty range", -1, range.lastIndexOf(null));
+        assertEquals("found string in empty range", -1, range.lastIndexOf("hello"));
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#add(java.lang.Object)}.
+     */
+    public void testAddObject() {
+        try {
+            range.add(new Integer(12));
+            fail("added value to empty range");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#contains(java.lang.Object)}.
+     */
+    public void testContains() {
+        assertFalse("empty range contains a value", range.contains(AT));
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#remove(java.lang.Object)}.
+     */
+    public void testRemoveObject() {
+        try {
+            range.remove(AT);
+            fail("removed value from empty range");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#addAll(int,java.util.Collection)}.
+     */
+    public void testAddAllIntCollection() {
+        try {
+            range.addAll(0, new ArrayList());
+            fail("added values to empty range");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#addAll(java.util.Collection)}.
+     */
+    public void testAddAllCollection() {
+        try {
+            range.addAll(new ArrayList());
+            fail("added values to empty range");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#containsAll(java.util.Collection)}.
+     */
+    public void testContainsAll() {
+        final List list = new ArrayList();
+        assertTrue("range contains all elements of an empty list", range.containsAll(list));
+
+        list.add(AT);
+        assertFalse("range contains all elements of single element list", range.containsAll(list));
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#removeAll(java.util.Collection)}.
+     */
+    public void testRemoveAll() {
+        try {
+            range.removeAll(new ArrayList());
+            fail("removed values from an empty range");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#retainAll(java.util.Collection)}.
+     */
+    public void testRetainAll() {
+        try {
+            range.retainAll(new ArrayList());
+            fail("retained values in an empty range");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#iterator()}.
+     */
+    public void testIterator() {
+        final Iterator iterator = range.iterator();
+        assertFalse("iterator has next value", iterator.hasNext());
+
+        try {
+            iterator.next();
+            fail("got next value in an empty range");
+        }
+        catch (NoSuchElementException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Tests removing via an iterator.
+     */
+    public void testIteratorRemove() {
+        try {
+            final Iterator iterator = range.iterator();
+            iterator.remove();
+            fail("removed via iterator");
+        }
+        catch (IllegalStateException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#subList(int,int)}.
+     */
+    public void testSubList() {
+        final List list = range.subList(0, 0);
+        assertTrue("list not empty", list.isEmpty());
+        try {
+            range.subList(0, 1);
+            fail("got sub list in an empty range");
+        }
+        catch (IndexOutOfBoundsException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#listIterator()}.
+     */
+    public void testListIterator() {
+        final ListIterator iterator = range.listIterator();
+        assertFalse("iterator has next value", iterator.hasNext());
+        assertFalse("iterator has previous value", iterator.hasPrevious());
+
+        try {
+            iterator.next();
+            fail("got next value in an empty range");
+        }
+        catch (NoSuchElementException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#listIterator(int)}.
+     */
+    public void testListIteratorInt() {
+        final ListIterator iterator = range.listIterator(0);
+        assertFalse("iterator has next value", iterator.hasNext());
+        assertFalse("iterator has previous value", iterator.hasPrevious());
+
+        try {
+            range.listIterator(1);
+            fail("got list iterator at index 1");
+        }
+        catch (IndexOutOfBoundsException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#set(int,java.lang.Object)}.
+     */
+    public void testSet() {
+        try {
+            range.set(0, AT);
+            fail("got set value 0");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#toArray(java.lang.Object[])}.
+     */
+    public void testToArrayObjectArray() {
+        final Integer[] actual = (Integer[]) range.toArray(new Integer[0]);
+        assertArrayEquals(new Integer[0], actual);
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#step(int,groovy.lang.Closure)}.
+     */
+    public void testStepIntClosure() {
+        final List callLog = new ArrayList();
+        final Closure closure = new NumberRangeTest.RecordingClosure(callLog);
+        range.step(1, closure);
+        assertEquals("wrong number of calls to closure", 0, callLog.size());
+    }
+
+    /**
+     * Test method for {@link groovy.lang.EmptyRange#step(int)}.
+     */
+    public void testStepInt() {
+        List result = range.step(1);
+        assertTrue("too many elements", result.isEmpty());
+
+        // make sure a new list is returned each time
+        result.add(new Integer(1));
+        result = range.step(1);
+        assertTrue("too many elements", result.isEmpty());
+    }
+}
diff --git a/groovy/src/test/groovy/lang/ExpandoMetaClassCreationHandleTest.groovy b/groovy/src/test/groovy/lang/ExpandoMetaClassCreationHandleTest.groovy
new file mode 100644
index 0000000..af2b833
--- /dev/null
+++ b/groovy/src/test/groovy/lang/ExpandoMetaClassCreationHandleTest.groovy
@@ -0,0 +1,267 @@
+/*
+ * Copyright 2004-2005 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang;
+
+/**
+ * @author Graeme Rocher
+ */
+
+class ExpandoMetaClassCreationHandleTest extends GroovyTestCase {
+
+	def registry = GroovySystem.metaClassRegistry
+
+	void setUp() {
+        ExpandoMetaClass.enableGlobally()
+	}
+
+	void tearDown() {
+        ExpandoMetaClass.disableGlobally()
+	}
+
+
+    void testInheritWithExistingMetaClass() {
+        registry.removeMetaClass(String.class)
+        registry.removeMetaClass(Object.class)
+
+        String foo = "hello"
+        assertEquals "HELLO", foo.toUpperCase()
+
+        Object.metaClass.doStuff = {-> delegate.toString().toUpperCase() }
+
+        assertEquals "HELLO", foo.doStuff()
+    }
+
+    void testInheritFromInterfaceHeirarchy() {
+        registry.removeMetaClass(IBar.class)
+        registry.removeMetaClass(Foo.class)
+        registry.removeMetaClass(Test1.class)
+
+        def metaClass = registry.getMetaClass(IBar.class)
+        assertTrue(metaClass instanceof ExpandoMetaClass)
+
+        metaClass.helloWorld = {-> "goodbye!" }
+
+        def t = new Test1()
+        assertEquals "goodbye!", t.helloWorld()        
+    }
+    
+    void testExpandoInterfaceInheritanceWithOverrideDGM() {
+	    registry.removeMetaClass(Foo.class)
+	    registry.removeMetaClass(Test1.class)
+
+	    def metaClass = registry.getMetaClass(Foo.class)
+	    assertTrue(metaClass instanceof ExpandoMetaClass)
+
+        def map = [:]
+	    metaClass.getAt = { Integer i -> map[i] }
+	    metaClass.putAt = { Integer i, val -> map[i] = val }
+
+	    def t = new Test1()
+	    //assertEquals 2, t.metaClass.getExpandoMethods().size()
+	    //assert t.metaClass.getExpandoMethods().find { it.name == 'putAt' }
+
+        t[0] = "foo"
+
+        assert map.size() == 1
+
+	    assertEquals "foo", t[0]
+    }
+
+    void testOverrideSetPropertyViaInterface() {
+	    registry.removeMetaClass(Foo.class)
+	    registry.removeMetaClass(Test1.class)
+
+	    def metaClass = registry.getMetaClass(Foo.class)
+
+        def testValue = null
+	    metaClass.setProperty = { String name, value ->
+            def mp = delegate.metaClass.getMetaProperty(name)
+
+            if(mp) { mp.setProperty(delegate, value) } else { testValue = value }
+        }
+
+
+		def t = new Test1()
+
+        t.name = "Bob"
+        assertEquals "Bob", t.name
+
+        t.foo = "bar"
+        assertEquals "bar",testValue
+
+    }
+    void testOverrideGetPropertyViaInterface() {
+	    registry.removeMetaClass(Foo.class)
+	    registry.removeMetaClass(Test1.class)
+
+	    def metaClass = registry.getMetaClass(Foo.class)
+
+	    metaClass.getProperty = { String name ->
+            def mp = delegate.metaClass.getMetaProperty(name)
+
+            mp ? mp.getProperty(delegate) : "foo $name"
+        }
+
+	    def t = new Test1()
+
+	   	assertEquals "Fred", t.getProperty("name")
+	   	assertEquals "Fred", t.name
+	   	assertEquals "foo bar", t.getProperty("bar")
+	   	assertEquals "foo bar", t.bar
+    }
+
+    void testOverrideInvokeMethodViaInterface() {
+	    registry.removeMetaClass(Foo.class)
+	    registry.removeMetaClass(Object.class)
+	    registry.removeMetaClass(IBar.class)
+	    registry.removeMetaClass(Test1.class)
+
+	    def metaClass = registry.getMetaClass(Foo.class)
+
+
+	    metaClass.invokeMethod = { String name, args ->
+           def mm = delegate.metaClass.getMetaMethod(name, args)
+            mm ? mm.invoke(delegate, args) : "bar!!"
+	    }
+
+
+	    def t = new Test1()
+
+        assertEquals "bar!!", t.doStuff()
+        assertEquals "foo", t.invokeMe()
+    }
+
+	void testInterfaceMethodInheritance() {
+
+	    registry.removeMetaClass(List.class)
+	    registry.removeMetaClass(ArrayList.class)
+
+
+	    def metaClass = registry.getMetaClass(List.class)
+	    assertTrue(metaClass instanceof ExpandoMetaClass)
+
+	    metaClass.sizeDoubled = {-> delegate.size()*2 }
+	    metaClass.isFull = {-> false }
+
+	    def list = new ArrayList()
+
+	    list << 1
+	    list << 2
+
+	    assertEquals 4, list.sizeDoubled()
+
+
+
+	    list = new ArrayList()
+
+	    assert !list.isFull()
+	    assert !list.full
+    }
+
+
+
+
+	void testExpandoCreationHandle() {
+		def metaClass = registry.getMetaClass(URL.class)
+		if(!(metaClass instanceof ExpandoMetaClass)) {
+			registry.removeMetaClass(URL.class)
+		}
+
+		def url = new URL("http://grails.org")
+		metaClass = registry.getMetaClass(url.getClass())
+		assertTrue(metaClass instanceof ExpandoMetaClass)
+
+
+		metaClass.toUpperString = {->
+			delegate.toString().toUpperCase()
+		}
+
+		assertEquals "http://grails.org", url.toString()
+		assertEquals "HTTP://GRAILS.ORG", url.toUpperString()
+	}
+
+	void testExpandoInheritance() {
+		registry.removeMetaClass(String.class)
+
+		def metaClass = registry.getMetaClass(Object.class)
+		if(!(metaClass instanceof ExpandoMetaClass)) {
+			registry.removeMetaClass(Object.class)
+			metaClass = registry.getMetaClass(Object.class)
+		}
+
+		metaClass.toFoo = {-> "foo" }
+
+		def uri = new URI("http://bar.com")
+		def s = "bar"
+
+		assertEquals "foo", uri.toFoo()
+		assertEquals "foo", s.toFoo()
+
+		metaClass.toBar = {-> "bar" }
+
+		assertEquals "bar", uri.toBar()
+		assertEquals "bar", s.toBar()
+	}
+
+
+
+     void testAddMethodToChildThenParent() {
+         registry.removeMetaClass(Test1)
+         registry.removeMetaClass(EMCInheritTest)
+
+         EMCInheritTest.metaClass.foo = {-> "hello!" }
+
+         def emc = new EMCInheritTest()
+
+         assertEquals "hello!", emc.foo()
+
+         Test1.metaClass.foo = {-> "uck" }
+         emc = new EMCInheritTest()
+         // make sure original foo wasn't overridden
+         assertEquals "hello!", emc.foo()
+     }
+
+     void testAddMethodMissingToChildThenParent() {
+         registry.removeMetaClass(Test1)
+         registry.removeMetaClass(EMCInheritTest)
+
+         EMCInheritTest.metaClass.methodMissing = {String name, args-> "hello!" }
+
+         def emc = new EMCInheritTest()
+
+         assertEquals "hello!", emc.foo()
+
+         Test1.metaClass.methodMissing = {String name, args-> "uck" }
+         emc = new EMCInheritTest()
+         // make sure original foo wasn't overridden
+         assertEquals "hello!", emc.bar()
+
+     }
+
+
+}
+
+interface IBar { }
+interface Foo extends IBar {
+
+}
+class Test1 implements Foo {
+    String name = "Fred"
+      def invokeMe() { "foo" }
+}
+class EMCInheritTest extends Test1 {
+
+}
diff --git a/groovy/src/test/groovy/lang/ExpandoMetaClassTest.groovy b/groovy/src/test/groovy/lang/ExpandoMetaClassTest.groovy
new file mode 100644
index 0000000..6730bf3
--- /dev/null
+++ b/groovy/src/test/groovy/lang/ExpandoMetaClassTest.groovy
@@ -0,0 +1,573 @@
+/*
+ * Copyright 2004-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang
+
+/**
+ * @author Graeme Rocher
+ */
+
+class ExpandoMetaClassTest extends GroovyTestCase {
+
+    void testOverrideStaticMethod() {        
+        TestStatic.metaClass.'static'.f = { "first" }
+        assertEquals "first",TestStatic.f("")
+
+        TestStatic.metaClass.'static'.f = { "second" }
+        assertEquals "second",TestStatic.f("")
+    }
+
+
+    void testOverrideMethod() {
+        TestStatic.metaClass.f = { "first" }
+        assertEquals "first",new TestStatic ().f("")
+
+        TestStatic.metaClass.f = { "second" }
+        assertEquals "second",new TestStatic ().f("")
+    }
+
+
+    void testStaticBeanStyleProperties() {
+        def mc = new ExpandoMetaClass(TestInvokeMethod.class, true, true)
+        mc.initialize()
+        GroovySystem.metaClassRegistry.setMetaClass(TestInvokeMethod.class, mc)
+
+        mc.'static'.getHello = {-> "bar!"}
+
+        assertEquals "bar!", TestInvokeMethod.hello
+    }
+
+    void testOverrideInvokeStaticMethod() {
+        def mc = new ExpandoMetaClass(TestInvokeMethod.class, true, true)
+        mc.initialize()
+        GroovySystem.metaClassRegistry.setMetaClass(TestInvokeMethod.class, mc)
+
+        mc.'static'.invokeMethod = {String methodName, args ->
+            def metaMethod = mc.getStaticMetaMethod(methodName, args)
+            def result = null
+            if (metaMethod) result = metaMethod.invoke(delegate, args)
+            else {
+                result = "foo!"
+            }
+            result
+        }
+
+        assertEquals "bar!", TestInvokeMethod.myStaticMethod()
+        assertEquals "foo!", TestInvokeMethod.dynamicMethod()
+    }
+
+    void testOverrideInvokeMethod() {
+        def mc = new ExpandoMetaClass(TestInvokeMethod.class, false, true)
+        mc.initialize()
+
+
+        assert mc.hasMetaMethod("invokeMe", [String] as Class[])
+
+        mc.invokeMethod = {String name, args ->
+            def mm = delegate.metaClass.getMetaMethod(name, args)
+            mm ? mm.invoke(delegate, args) : "bar!!"
+        }
+
+        def t = new TestInvokeMethod()
+        t.metaClass = mc
+
+        assertEquals "bar!!", t.doStuff()
+        assertEquals "Foo!! hello", t.invokeMe("hello")
+    }
+
+    void testOverrideSetProperty() {
+        def mc = new ExpandoMetaClass(TestGetProperty.class, false, true)
+        mc.initialize()
+
+        assert mc.hasMetaProperty("name")
+
+        def testValue = null
+        mc.setProperty = {String name, value ->
+            def mp = delegate.metaClass.getMetaProperty(name)
+
+            if (mp) {mp.setProperty(delegate, value)} else {testValue = value}
+        }
+
+        def t = new TestGetProperty()
+        t.metaClass = mc
+
+        t.name = "Bob"
+        assertEquals "Bob", t.name
+
+        t.foo = "bar"
+        assertEquals "bar", testValue
+    }
+
+    void testOverrideGetProperty() {
+        def mc = new ExpandoMetaClass(TestGetProperty.class, false , true)
+        mc.initialize()
+
+        assert mc.hasMetaProperty("name")
+
+        mc.getProperty = {String name ->
+            def mp = delegate.metaClass.getMetaProperty(name)
+
+            mp ? mp.getProperty(delegate) : "foo $name"
+        }
+
+        def t = new TestGetProperty()
+        t.metaClass = mc
+
+        assertEquals "foo bar", t.getProperty("bar")
+        assertEquals "foo bar", t.bar
+        assertEquals "Fred", t.getProperty("name")
+        assertEquals "Fred", t.name
+    }
+
+    void testBooleanGetterWithClosure() {
+        def metaClass = new ExpandoMetaClass(Test.class, false, true)
+        metaClass.initialize()
+        metaClass.isValid = {-> true}
+
+        def t = new Test()
+        t.metaClass = metaClass
+
+        assert t.isValid()
+        assert t.valid
+    }
+
+
+    void testAllowAdditionOfProperties() {
+        def metaClass = new ExpandoMetaClass(Test.class, false, true)
+
+        metaClass.getOne << {->
+            "testme"
+        }
+        metaClass.initialize()
+        try {
+            metaClass.getTwo << {->
+                "testagain"
+            }
+        }
+        catch (RuntimeException e) {
+            fail("Should have allowed addition of new method")
+        }
+
+        def t = new Test()
+        t.metaClass = metaClass
+
+        assertEquals "testme", t.one
+        assertEquals "testagain", t.two
+    }
+
+    void testAllowAdditionOfMethods() {
+        def metaClass = new ExpandoMetaClass(Test.class, false, true)
+
+        metaClass.myMethod << {->
+            "testme"
+        }
+        metaClass.initialize()
+        try {
+            metaClass.mySecondMethod << {->
+                "testagain"
+            }
+        }
+        catch (RuntimeException e) {
+            fail("Should have allowed addition of new method")
+        }
+
+        def t = new Test()
+        t.metaClass = metaClass
+
+        assertEquals "testme", t.myMethod()
+        assertEquals "testagain", t.mySecondMethod()
+    }
+
+    void testForbiddenAdditionOfMethods() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+
+        metaClass.myMethod << {
+            "testme"
+        }
+        metaClass.initialize()
+
+        def t = new Test()
+
+        try {
+            metaClass.mySecondMethod << {
+                "testagain"
+            }
+            fail("Should have thrown exception")
+        }
+        catch (RuntimeException e) {
+            // expected
+        }
+    }
+
+    void testPropertyGetterWithClosure() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+
+        metaClass.getSomething = {-> "testme"}
+
+        metaClass.initialize()
+
+        def t = new Test()
+        t.metaClass = metaClass
+
+        assertEquals "testme", t.getSomething()
+        assertEquals "testme", t.something
+    }
+
+    void testPropertySetterWithClosure() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+
+        def testSet = null
+        metaClass.setSomething = {String txt -> testSet = txt}
+
+        metaClass.initialize()
+
+        def t = new Test()
+        t.metaClass = metaClass
+
+        t.something = "testme"
+        assertEquals "testme", testSet
+
+        t.setSomething("test2")
+        assertEquals "test2", testSet
+    }
+
+    void testNewMethodOverloading() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+
+        metaClass.overloadMe << {String txt -> txt} << {Integer i -> i}
+
+        metaClass.initialize()
+
+        def t = new Test()
+        t.metaClass = metaClass
+
+        assertEquals "test", t.overloadMe("test")
+        assertEquals 10, t.overloadMe(10)
+    }
+
+    void testOverloadExistingMethodAfterInitialize() {
+        def t = new Test()
+
+        assertEquals "test", t.doSomething("test")
+
+        def metaClass = new ExpandoMetaClass(Test.class, false, true)
+        metaClass.initialize()
+
+        metaClass.doSomething = {Integer i -> i + 1}
+
+        t.metaClass = metaClass
+
+        assertEquals "test", t.doSomething("test")
+        assertEquals 11, t.doSomething(10)
+    }
+
+    void testOverloadExistingMethodBeforeInitialize() {
+        def t = new Test()
+
+        assertEquals "test", t.doSomething("test")
+
+        def metaClass = new ExpandoMetaClass(Test.class, false, true)
+
+
+        metaClass.doSomething = {Integer i -> i + 1}
+
+        metaClass.initialize()
+
+        t.metaClass = metaClass
+
+        assertEquals "test", t.doSomething("test")
+        assertEquals 11, t.doSomething(10)
+    }
+
+    void testNewPropertyMethod() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+
+        metaClass.something = "testme"
+
+        metaClass.initialize()
+
+        def t = new Test()
+        t.metaClass = metaClass
+
+        assertEquals "testme", t.getSomething()
+        assertEquals "testme", t.something
+
+        t.something = "test2"
+        assertEquals "test2", t.something
+        assertEquals "test2", t.getSomething()
+
+        def t2 = new Test()
+        t2.metaClass = metaClass
+        // now check that they're not sharing the same property!
+        assertEquals "testme", t2.something
+        assertEquals "test2", t.something
+
+        t2.setSomething("test3")
+
+        assertEquals "test3", t2.something
+    }
+
+    void testCheckFailOnExisting() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+        try {
+            metaClass.existing << {->
+                "should fail. already exists!"
+            }
+            fail("Should have thrown exception when method already exists")
+        }
+        catch (Exception e) {
+            // expected
+        }
+    }
+
+    void testCheckFailOnExistingConstructor() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+        try {
+            metaClass.constructor << {->
+                "should fail. already exists!"
+            }
+            fail("Should have thrown exception when method already exists")
+        }
+        catch (Exception e) {
+            // expected
+        }
+    }
+
+    void testCheckFailOnExistingStaticMethod() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+        try {
+            metaClass.'static'.existingStatic << {->
+                "should fail. already exists!"
+            }
+            fail("Should have thrown exception when method already exists")
+        }
+        catch (Exception e) {
+            // expected
+        }
+    }
+
+    void testNewStaticMethod() {
+        def metaClass = new ExpandoMetaClass(Test.class, true)
+
+        metaClass.'static'.myStaticMethod << {String txt ->
+            "testme"
+        }
+        metaClass.initialize()
+
+        assertEquals "testme", Test.myStaticMethod("blah")
+    }
+
+    void testReplaceStaticMethod() {
+        def metaClass = new ExpandoMetaClass(Test.class, true)
+
+        metaClass.'static'.existingStatic = {->
+            "testme"
+        }
+        metaClass.initialize()
+
+        assertEquals "testme", Test.existingStatic()
+
+    }
+
+    void testNewZeroArgumentStaticMethod() {
+        def metaClass = new ExpandoMetaClass(Test.class, true)
+
+        metaClass.'static'.myStaticMethod = {->
+            "testme"
+        }
+        metaClass.initialize()
+
+        assertEquals "testme", Test.myStaticMethod()
+    }
+
+    void testNewInstanceMethod() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+
+        metaClass.myMethod << {
+            "testme"
+        }
+        metaClass.initialize()
+
+        def t = new Test()
+
+        t.metaClass = metaClass
+
+        assertEquals "testme", t.myMethod()
+    }
+
+    void testNewConstructor() {
+        def metaClass = new ExpandoMetaClass(Test.class, true)
+
+        metaClass.constructor << {String txt ->
+            def t = Test.class.newInstance()
+            t.name = txt
+            return t
+        }
+
+        metaClass.initialize()
+
+        def t = new Test("testme")
+        assert t
+        assertEquals "testme", t.name
+
+        GroovySystem.metaClassRegistry.removeMetaClass(Test.class)
+    }
+
+    void testReplaceConstructor() {
+        def metaClass = new ExpandoMetaClass(Test.class, true)
+
+        metaClass.constructor = {->
+            "testme"
+        }
+
+        metaClass.initialize()
+
+        def t = new Test()
+        assert t
+        assertEquals "testme", t
+
+        GroovySystem.metaClassRegistry.removeMetaClass(Test.class)
+    }
+
+    void testReplaceInstanceMethod() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+
+        metaClass.existing2 = {Object i ->
+            "testme"
+        }
+        metaClass.initialize()
+
+        def t = new Test()
+        t.metaClass = metaClass
+
+        def var = 1
+        assertEquals "testme", t.existing2(var)
+    }
+
+    void testBorrowMethodFromAnotherClass() {
+        def metaClass = new ExpandoMetaClass(Test.class)
+
+        def a = new Another()
+        metaClass.borrowMe = a.&another
+        metaClass.borrowMeToo = a.&noArgs
+        metaClass.initialize()
+
+        def t = new Test()
+        t.metaClass = metaClass
+
+        assertEquals "mine blah!", t.borrowMe("blah")
+        // GROOVY-1993
+        assertEquals "no args here!", t.borrowMeToo()
+    }
+
+    void testBorrowByName() {
+        println 'testBorrowByName'
+        def metaClass = new ExpandoMetaClass(Test.class)
+
+        def a = new Another()
+        metaClass.borrowMe = a.&'another'
+        metaClass.borrowMeToo = a.&'noArgs'
+        metaClass.initialize()
+
+        def t = new Test()
+        t.metaClass = metaClass                                                
+
+        assertEquals "mine blah!", t.borrowMe("blah")
+        // GROOVY-1993
+        assertEquals "no args here!", t.borrowMeToo()
+    }
+
+    void testAddIdenticalPropertyToChildAndParent() {
+        ExpandoMetaClass.enableGlobally()
+        doMethods(SuperClass.class)
+        doMethods(ChildClass.class)
+
+        def child = new ChildClass()
+        def parent = new SuperClass()
+
+        assert parent.errors == null
+        parent.errors = [3, 2, 1, 0]
+        assert parent.errors.size() == 4
+
+        assert child.errors == null
+        child.errors = [1, 2, 3]
+        assert child.errors.size() == 3
+        //TODO disable ExpandoMetaClass
+        ExpandoMetaClass.disableGlobally()
+    }
+
+    def doMethods(clazz) {
+        def metaClass = clazz.metaClass
+
+        metaClass.setErrors = {errors ->
+            thingo = errors
+        }
+
+        metaClass.getErrors = {->
+            return thingo
+        }
+    }
+
+}
+class SuperClass {
+    def thingo
+}
+
+class ChildClass extends SuperClass {
+
+}
+
+class TestInvokeMethod {
+    def invokeMe(String boo) {"Foo!! $boo"}
+
+    static myStaticMethod() {"bar!"}
+}
+
+class TestGetProperty {
+    String name = "Fred"
+}
+
+class Test {
+    String name
+
+    def existing2(obj) {
+        "hello2!"
+    }
+    def existing() {
+        "hello!"
+    }
+
+    def doSomething(Object txt) {txt}
+
+    static existingStatic() {
+        "I exist"
+    }
+}
+
+class Another {
+    def another(txt) {
+        "mine ${txt}!"
+    }
+    def noArgs() {
+        "no args here!"
+    }
+}
+
+class Child extends Test {
+    def aChildMethod() {
+        "hello children"
+    }
+}
+class TestStatic {}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/FloatRangeTest.java b/groovy/src/test/groovy/lang/FloatRangeTest.java
new file mode 100644
index 0000000..0eb73e2
--- /dev/null
+++ b/groovy/src/test/groovy/lang/FloatRangeTest.java
@@ -0,0 +1,69 @@
+/*
+ $Id: FloatRangeTest.java,v 1.1 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+/**
+ * Tests {@link ObjectRange}s of {@link Float}s.
+ *
+ * @author Edwin Tellman
+ */
+public class FloatRangeTest extends NumberRangeTest {
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Range createRange(int from, int to) {
+        return new ObjectRange(new Float(from), new Float(to));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Comparable createValue(int value) {
+        return new Double(value);
+    }
+
+}
diff --git a/groovy/src/test/groovy/lang/GStringTest.java b/groovy/src/test/groovy/lang/GStringTest.java
new file mode 100644
index 0000000..6b05be4
--- /dev/null
+++ b/groovy/src/test/groovy/lang/GStringTest.java
@@ -0,0 +1,96 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+ * 
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met: 1. Redistributions of source code must retain
+ * copyright statements and notices. Redistributions must also contain a copy
+ * of this document. 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution. 3.
+ * The name "groovy" must not be used to endorse or promote products derived
+ * from this Software without prior written permission of The Codehaus. For
+ * written permission, please contact info@codehaus.org. 4. Products derived
+ * from this Software may not be called "groovy" nor may "groovy" appear in
+ * their names without prior written permission of The Codehaus. "groovy" is a
+ * registered trademark of The Codehaus. 5. Due credit should be given to The
+ * Codehaus - http://groovy.codehaus.org/
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *  
+ */
+package groovy.lang;
+
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * Tests the use of the structured Attribute type
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class GStringTest extends GroovyTestCase {
+
+    public void testIterateOverText() {
+        DummyGString compString = new DummyGString(new Object[]{"James"});
+        assertArrayEquals(new String[]{"Hello ", "!"}, compString.getStrings());
+        assertArrayEquals(new Object[]{"James"}, compString.getValues());
+        assertEquals("Hello James!", compString.toString());
+    }
+
+    public void testAppendString() {
+        DummyGString a = new DummyGString(new Object[]{"James"});
+        GString result = a.plus(" how are you?");
+        assertEquals("Hello James! how are you?", result.toString());
+        assertEquals('J', a.charAt(6));
+        assertEquals("o J", a.subSequence(4, 7));
+    }
+
+    public void testAppendString2() {
+        DummyGString a = new DummyGString(new Object[]{"James"}, new String[]{"Hello "});
+        GString result = a.plus(" how are you?");
+        System.out.println("Strings: " + InvokerHelper.toString(result.getStrings()));
+        System.out.println("Values: " + InvokerHelper.toString(result.getValues()));
+        assertEquals("Hello James how are you?", result.toString());
+    }
+
+    public void testAppendGString() {
+        DummyGString a = new DummyGString(new Object[]{"James"});
+        DummyGString b = new DummyGString(new Object[]{"Bob"});
+        GString result = a.plus(b);
+        assertEquals("Hello James!Hello Bob!", result.toString());
+    }
+
+    public void testAppendGString2() {
+        DummyGString a = new DummyGString(new Object[]{"James"}, new String[]{"Hello "});
+        DummyGString b = new DummyGString(new Object[]{"Bob"}, new String[]{"Hello "});
+        GString result = a.plus(b);
+        assertEquals("Hello JamesHello Bob", result.toString());
+    }
+
+    public void testEqualsAndHashCode() {
+        DummyGString a = new DummyGString(new Object[]{new Integer(1)});
+        DummyGString b = new DummyGString(new Object[]{new Long(1)});
+        Comparable c = new DummyGString(new Object[]{new Double(2.3)});
+
+        assertTrue("a == b", a.equals(b));
+        assertEquals("hashcode a == b", a.hashCode(), b.hashCode());
+        assertFalse("a != c", a.equals(c));
+        assertTrue("hashcode a != c", a.hashCode() != c.hashCode());
+        assertEquals("a <=> b", 0, a.compareTo(b));
+        assertEquals("a <=> b", -1, a.compareTo(c));
+    }
+}
diff --git a/groovy/src/test/groovy/lang/GetMethodsTest.groovy b/groovy/src/test/groovy/lang/GetMethodsTest.groovy
new file mode 100644
index 0000000..79468e6
--- /dev/null
+++ b/groovy/src/test/groovy/lang/GetMethodsTest.groovy
@@ -0,0 +1,73 @@
+/**
+ * Tests the behaviour of the runtime evaluating methods of Groovy's MetaClass system
+ 
+ * @author Graeme Rocher
+ * @since 1.1
+  *
+ * Created: Sep 10, 2007
+ * Time: 8:17:29 AM
+ * 
+ */
+package groovy.lang
+class GetMethodsTest extends GroovyTestCase {
+
+
+    void testGetMethods() {
+       GMTest2.metaClass.doStuff = {-> "foo" }
+       GMTest2.metaClass.getFoo = {-> "foo" }
+       GMTest2.metaClass.bar = "bar"
+       GMTest2.metaClass.'static'.doMoreStuff = {-> "more" }
+       def t = new GMTest2()
+
+
+       assert t.metaClass.methods.find { it.name == 'one' }
+       assert t.metaClass.methods.find { it.name == 'three' }
+       assert t.metaClass.methods.find { it.name == 'foo' }
+       assert t.metaClass.methods.find { it.name == 'getSeven' }
+       assert t.metaClass.methods.find { it.name == 'getEight' }
+       assert t.metaClass.methods.find { it.name == 'bar' }
+       assert t.metaClass.methods.find { it.name == 'toString' }
+       assert t.metaClass.methods.find { it.name == 'getEight' }
+       assert t.metaClass.methods.find { it.name == 'stuff' }
+       assert t.metaClass.methods.find { it.name == 'doStuff' }
+       assert t.metaClass.methods.find { it.name == 'getFoo' }
+       assert t.metaClass.methods.find { it.name == 'getBar' }
+       assert t.metaClass.methods.find { it.name == 'setBar' }
+       assert t.metaClass.methods.find { it.name == 'doMoreStuff' }
+    }
+
+    void testGetProperties() {
+
+        GMTest2.metaClass.getFoo = {-> "foo" }
+        GMTest2.metaClass.bar = "bar"
+
+        def t = new GMTest2()
+
+        assert t.metaClass.properties.find { it.name == 'five' }
+        assert t.metaClass.properties.find { it.name == 'six' }
+        assert t.metaClass.properties.find { it.name == 'seven' }
+        assert t.metaClass.properties.find { it.name == 'eight' }
+        assert t.metaClass.properties.find { it.name == 'foo' }
+        assert t.metaClass.properties.find { it.name == 'bar' }
+    }
+}
+class GMTest1 {
+    String five
+    def two = { "three" }
+    def one() { "two"}
+    def one(String one) { "two: $one" }
+    def three(String one) { "four" }
+    def three(Integer one) { "four" }
+    def foo(String name) {
+        "bar"
+    }
+
+    String getSeven() { "seven" }
+}
+class GMTest2 extends GMTest1 {
+    String six
+    def bar(String name) { "foo" }
+    static stuff() { "goodie" }
+
+    String getEight() { "eight" }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/GroovyClassLoaderTest.groovy b/groovy/src/test/groovy/lang/GroovyClassLoaderTest.groovy
new file mode 100644
index 0000000..420d44f
--- /dev/null
+++ b/groovy/src/test/groovy/lang/GroovyClassLoaderTest.groovy
@@ -0,0 +1,190 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 2005 The Codehaus - http://groovy.codehaus.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and limitations under the License.
+ *
+ */
+
+
+package groovy.lang;
+
+import java.security.CodeSource
+import org.codehaus.groovy.control.*
+import org.codehaus.groovy.ast.*
+import org.codehaus.groovy.classgen.*
+
+
+public class GroovyClassLoaderTest extends GroovyTestCase {
+
+    private final GroovyClassLoader classLoader = new GroovyClassLoader()
+
+    private boolean contains(String[] paths, String eval) {
+        try {
+            eval = (new File(eval)).toURI().toURL().getFile();
+        } catch (MalformedURLException e) {
+            return false;
+        }
+        for (it in paths) {
+            if(eval.equals(it)) return true;
+        }
+        return false;
+    }
+    
+    public void testAddsAClasspathEntryOnlyIfItHasNotAlreadyBeenAdded() {
+        String newClasspathEntry = "/tmp"
+        int initialNumberOfClasspathEntries = classLoader.getClassPath().length
+
+        classLoader.addClasspath(newClasspathEntry)
+        assert initialNumberOfClasspathEntries + 1 == classLoader.getClassPath().length
+        assert contains(classLoader.getClassPath(),newClasspathEntry)
+
+        classLoader.addClasspath(newClasspathEntry);
+        assert initialNumberOfClasspathEntries + 1 == classLoader.getClassPath().length
+        assert contains(classLoader.getClassPath(),newClasspathEntry)
+    }
+    
+    def getPaths(URLClassLoader ucl) {
+        def urls = ucl.getURLs()
+        return urls.findAll {!it.file.endsWith(".jar")}
+    }
+
+    def getPaths(String path) {
+        int start = 0, end=0
+        String sep = File.pathSeparator
+        def ret = []
+        while (end<path.length()) {
+          start = end
+          end = path.indexOf(sep,end)
+          if (end==-1) break
+          def sub = path.substring(start,end)
+          if (!sub.endsWith(".jar")) {
+            ret << ((new File(sub)).toURL())
+          }
+          end++
+        }
+      return ret 
+    }
+
+    
+    public void testClassNotFoundIsNotHidden() {
+      def paths=[]
+      def loader = this.class.classLoader
+      while (loader!=null) {
+        if (loader instanceof URLClassLoader) { 
+          paths += getPaths(loader)
+        }
+        loader = loader.parent
+      }
+      paths += getPaths(System.getProperty("java.class.path"))
+      paths=paths.unique()
+
+      def file = File.createTempFile("Foo",".groovy")
+      def name = file.name-".groovy"
+      def script = """
+        class $name extends GroovyTestCase{}
+      """
+      file << script
+      paths << file.parentFile.toURL()
+      def cl = new URLClassLoader(paths as URL[],null)
+      def gcl = new GroovyClassLoader(cl)
+      try {
+        gcl.loadClass(name)
+        assert false
+      } catch (NoClassDefFoundError ncdfe) {
+        // TODO: hack for running when under coverage - find a better way
+        assert ncdfe.message.indexOf("TestCase")>0 || ncdfe.message.indexOf("cobertura")>0 
+      }
+    }
+    
+    public void testClassPathNotDerived() {
+      def config = new CompilerConfiguration()
+      def loader1 = new GroovyClassLoader(null,config)
+      config = new CompilerConfiguration()
+      config.setClasspath("foo")
+      def loader2 = new GroovyClassLoader(loader1,config)
+      config = new CompilerConfiguration()
+      def loader3 = new GroovyClassLoader(loader2,config)
+      def urls = loader1.URLs
+      assert urls.length == 0
+      urls = loader2.URLs
+      assert urls.length == 1
+      assert urls[0].toString().endsWith("foo")
+      urls = loader3.URLs
+      assert urls.length == 0    
+    }
+    
+    public void testMultithreading() {
+      def config = new CompilerConfiguration()
+      config.recompileGroovySource = true;
+
+      def loader = new GroovyClassLoaderTestCustomGCL(config)
+
+      def ts = new Thread[100]
+      for (i in 0..<100) {
+        ts[i] = Thread.start {
+          if (i%2==1) sleep(100)
+          assert GroovyClassLoaderTestFoo1==loader.loadClass("Foox")
+        } 
+      }
+      sleep(100)
+      for (i in 0..<100) {ts[i].join()}
+
+      assert GroovyClassLoaderTestFoo2==loader.loadClass("Foox")
+   }
+   
+   public void testAdditionalPhaseOperation(){
+      def loader = new GroovyClassLoaderTestCustomPhaseOperation()
+      def ret = loader.parseClass("""class Foo{}""")
+      def field = ret.declaredFields.find {it.name=="id" && it.type==Long.TYPE}
+      assert  field != null
+  }
+}
+
+class GroovyClassLoaderTestFoo1{}
+class GroovyClassLoaderTestFoo2{}
+
+class GroovyClassLoaderTestCustomGCL extends GroovyClassLoader{
+    def GroovyClassLoaderTestCustomGCL(config) {
+      super(null,config);
+    }
+    def counter = 0
+    protected Class recompile(URL source, String name, Class oldClass) {
+        if (name=="Foox") {
+    		if (counter<100) {
+    		  counter++
+    		  return GroovyClassLoaderTestFoo1
+    		} else {
+    		  return GroovyClassLoaderTestFoo2
+    		}
+    	}
+    	return super.recompile(source,name,oldClass)
+    }
+
+    protected boolean isSourceNewer(URL source, Class cls) {
+      return true
+    }
+}
+
+class GroovyClassLoaderTestPropertyAdder extends CompilationUnit.PrimaryClassNodeOperation {
+  void call(SourceUnit source, GeneratorContext context, ClassNode classNode) {
+     classNode.addProperty("id", ClassNode.ACC_PUBLIC, ClassHelper.long_TYPE,null,null,null);
+  }
+} 
+
+class GroovyClassLoaderTestCustomPhaseOperation extends GroovyClassLoader {
+  CompilationUnit createCompilationUnit(CompilerConfiguration config, CodeSource source) {
+      def cu = super.createCompilationUnit(config,source)
+      cu.addPhaseOperation(new GroovyClassLoaderTestPropertyAdder(),Phases.CONVERSION) 
+      return cu
+  }    
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/GroovyLogTestCaseTest.groovy b/groovy/src/test/groovy/lang/GroovyLogTestCaseTest.groovy
new file mode 100644
index 0000000..7e11ed7
--- /dev/null
+++ b/groovy/src/test/groovy/lang/GroovyLogTestCaseTest.groovy
@@ -0,0 +1,33 @@
+package groovy.lang
+
+import java.util.logging.*
+
+/**
+Showing usage of the GroovyLogTestCase
+@author Dierk Koenig
+**/
+
+class GroovyLogTestCaseTest extends GroovyLogTestCase {
+
+    static final LOG = Logger.getLogger('groovy.lang.GroovyLogTestCaseTest')
+
+    void loggedMethod() {
+        LOG.finer 'some log entry'
+    }
+
+    void testStringLog(){
+        def result = stringLog(Level.FINER, 'groovy.lang.GroovyLogTestCaseTest') {
+            loggedMethod()
+        }
+        assertTrue result, result.contains('some log entry')
+    }
+
+    void testCombinedUsageForMetaClass(){
+        def result = withLevel(Level.FINER, 'groovy.lang.MetaClass') {
+            stringLog(Level.FINER, 'methodCalls'){
+                'hi'.toString()
+            }
+        }
+        assertTrue result, result.contains('java.lang.String toString()')
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/GroovyShellTest.java b/groovy/src/test/groovy/lang/GroovyShellTest.java
new file mode 100644
index 0000000..971213f
--- /dev/null
+++ b/groovy/src/test/groovy/lang/GroovyShellTest.java
@@ -0,0 +1,112 @@
+/*
+ * $Id$version
+ * Nov 23, 2003 9:02:55 PM $user Exp $
+ * 
+ * Copyright 2003 (C) Sam Pullara. All Rights Reserved.
+ * 
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met: 1. Redistributions of source code must retain
+ * copyright statements and notices. Redistributions must also contain a copy
+ * of this document. 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution. 3.
+ * The name "groovy" must not be used to endorse or promote products derived
+ * from this Software without prior written permission of The Codehaus. For
+ * written permission, please contact info@codehaus.org. 4. Products derived
+ * from this Software may not be called "groovy" nor may "groovy" appear in
+ * their names without prior written permission of The Codehaus. "groovy" is a
+ * registered trademark of The Codehaus. 5. Due credit should be given to The
+ * Codehaus - http://groovy.codehaus.org/
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *  
+ */
+package groovy.lang;
+
+import groovy.util.GroovyTestCase;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import org.codehaus.groovy.control.CompilerConfiguration;
+
+import java.io.ByteArrayInputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author sam
+ *         <p/>
+ *         To change the template for this generated type comment go to Window -
+ *         Preferences - Java - Code Generation - Code and Comments
+ */
+public class GroovyShellTest extends GroovyTestCase {
+
+    private String script1 = "test = 1";
+
+    public static void main(String[] args) {
+        TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        return new TestSuite(GroovyShellTest.class);
+    }
+
+    public void testExecuteScript() {
+        GroovyShell shell = new GroovyShell();
+        try {
+            Object result = shell.evaluate(new ByteArrayInputStream(script1.getBytes()), "Test.groovy");
+            assertEquals(new Integer(1), result);
+        }
+        catch (Exception e) {
+            fail(e.toString());
+        }
+    }
+
+    private static class PropertyHolder {
+        private Map map = new HashMap();
+
+        public void set(String key, Object value) {
+            map.put(key, value);
+        }
+
+        public Object get(String key) {
+            return map.get(key);
+        }
+    }
+
+    private String script2 = "test.prop = 2\nreturn test.prop";
+
+    public void testExecuteScriptWithContext() {
+        Binding context = new Binding();
+        context.setVariable("test", new PropertyHolder());
+        GroovyShell shell = new GroovyShell(context);
+        try {
+            Object result = shell.evaluate(new ByteArrayInputStream(script2.getBytes()), "Test.groovy");
+            assertEquals(new Integer(2), result);
+        }
+        catch (Exception e) {
+            fail(e.toString());
+        }
+    }
+
+    public void testScriptWithDerivedBaseClass() throws Exception {
+        Binding context = new Binding();
+        CompilerConfiguration config = new CompilerConfiguration();
+        config.setScriptBaseClass(DerivedScript.class.getName());
+        GroovyShell shell = new GroovyShell(context, config);
+        Object result = shell.evaluate("x = 'abc'; doSomething(cheese)");
+        assertEquals("I like Cheddar", result);
+        assertEquals("abc", context.getVariable("x"));
+    }
+}
diff --git a/groovy/src/test/groovy/lang/GroovySystemTest.groovy b/groovy/src/test/groovy/lang/GroovySystemTest.groovy
new file mode 100644
index 0000000..cba3659
--- /dev/null
+++ b/groovy/src/test/groovy/lang/GroovySystemTest.groovy
@@ -0,0 +1,17 @@
+package groovy.lang
+
+import java.util.logging.*
+
+/**
+ * Tests for the GroovySystem class
+ *
+ * @author Graeme Rocher
+ **/
+
+class GroovySystemTest extends GroovyTestCase {
+
+    void testGetMetaClassRegistry() {
+        assert GroovySystem.metaClassRegistry
+        assert GroovySystem.getMetaClassRegistry()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/InnerClassResolvingTest.groovy b/groovy/src/test/groovy/lang/InnerClassResolvingTest.groovy
new file mode 100644
index 0000000..c3eae31
--- /dev/null
+++ b/groovy/src/test/groovy/lang/InnerClassResolvingTest.groovy
@@ -0,0 +1,20 @@
+package groovy.lang

+

+class InnerClassResolvingTest extends GroovyTestCase {

+    public void testInnerClass() {

+        // Thread.UncaughtExceptionHandler was added in Java 1.5

+        if (System.properties.'java.version'[2] >= '5') {

+            def script = '''

+                def caught = false

+                def t = Thread.start {

+                    Thread.setDefaultUncaughtExceptionHandler(

+                        {thread,ex -> caught=true} as Thread.UncaughtExceptionHandler)

+                    throw new Exception("huhu")

+                }

+                t.join()

+                assert caught==true

+            '''

+            new GroovyShell().evaluate(script)

+        }

+    }

+}

diff --git a/groovy/src/test/groovy/lang/IntRangeTest.java b/groovy/src/test/groovy/lang/IntRangeTest.java
new file mode 100644
index 0000000..2e3fdaa
--- /dev/null
+++ b/groovy/src/test/groovy/lang/IntRangeTest.java
@@ -0,0 +1,102 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+/**
+ * Provides unit tests for the <code>IntRange</code> class.
+ *
+ * @author James Strachan
+ * @version $Revision$
+ */
+public class IntRangeTest extends NumberRangeTest {
+
+    public void testCreateTooBigRange() {
+        try {
+            createRange(0, Integer.MAX_VALUE);
+            fail("too large range accepted");
+        }
+        catch (IllegalArgumentException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Tests providing invalid arguments to the protected constructor.
+     */
+    public void testInvalidArgumentsToConstructor() {
+        try {
+            new IntRange(2, 1, true);
+            fail("invalid range created");
+        }
+        catch (IllegalArgumentException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Tests getting the to and from values as <code>int</code>s.
+     */
+    public void testGetToFromInt() {
+        final int from = 3, to = 7;
+        final IntRange range = new IntRange(from, to);
+        assertEquals("wrong 'from'", from, range.getFromInt());
+        assertEquals("wrong 'to'", to, range.getToInt());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Comparable createValue(int value) {
+        return new Integer(value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Range createRange(int from, int to) {
+        return new IntRange(from, to);
+    }
+}
diff --git a/groovy/src/test/groovy/lang/IntegerRangeTest.java b/groovy/src/test/groovy/lang/IntegerRangeTest.java
new file mode 100644
index 0000000..6b453b1
--- /dev/null
+++ b/groovy/src/test/groovy/lang/IntegerRangeTest.java
@@ -0,0 +1,70 @@
+/*
+ $Id: IntegerRangeTest.java,v 1.1 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+
+/**
+ * Tests {@link ObjectRange}s of {@link Integer}s.
+ *
+ * @author Edwin Tellman
+ */
+public class IntegerRangeTest extends NumberRangeTest {
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Range createRange(int from, int to) {
+        return new ObjectRange(new Integer(from), new Integer(to));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Comparable createValue(int value) {
+        return new Integer(value);
+    }
+
+}
diff --git a/groovy/src/test/groovy/lang/InterceptorTest.groovy b/groovy/src/test/groovy/lang/InterceptorTest.groovy
new file mode 100644
index 0000000..df16d6f
--- /dev/null
+++ b/groovy/src/test/groovy/lang/InterceptorTest.groovy
@@ -0,0 +1,91 @@
+package groovy.lang
+
+import org.codehaus.groovy.runtime.StringBufferWriter
+
+/**
+* Test for the Interceptor Interface usage as implemented by the
+* TracingInterceptor. Makes also use of the ProxyMetaClass and
+* shows the collaboration.
+* As a side Effect, the ProxyMetaClass is also partly tested.
+* @author Dierk Koenig
+**/
+class InterceptorTest extends GroovyTestCase{
+
+    def Interceptor logInterceptor
+    def StringBuffer log
+    def interceptable   // the object to intercept method calls on
+    def proxy
+
+    void setUp() {
+        logInterceptor = new TracingInterceptor()
+        log = new StringBuffer("\n")
+        logInterceptor.writer = new StringBufferWriter(log)
+        // we intercept calls from Groovy to the java.lang.String object
+        interceptable = 'Interceptable String'
+        proxy = ProxyMetaClass.getInstance(interceptable.class)
+        proxy.setInterceptor(logInterceptor)
+    }
+
+    void testSimpleInterception() {
+        proxy.use {
+            assertEquals 20, interceptable.size()
+            assertEquals 20, interceptable.length()
+            assertTrue interceptable.startsWith('I',0)
+        }
+        assertEquals(
+"""
+before java.lang.String.size()
+after  java.lang.String.size()
+before java.lang.String.length()
+after  java.lang.String.length()
+before java.lang.String.startsWith(java.lang.String, java.lang.Integer)
+after  java.lang.String.startsWith(java.lang.String, java.lang.Integer)
+""", log.toString())
+    }
+
+    void testNoInterceptionWithNullInterceptor() {
+        proxy.setInterceptor(null)
+        proxy.use {
+            interceptable.size()
+        }
+    }
+
+    void testConstructorInterception() {
+        proxy.use {
+            new String('some string')
+        }
+        assertEquals(
+"""
+before java.lang.String.ctor(java.lang.String)
+after  java.lang.String.ctor(java.lang.String)
+""", log.toString())
+    }
+
+    void testStaticMethodInterception() {
+        proxy.use {
+            assertEquals 'true', String.valueOf(true)
+        }
+        assertEquals(
+"""
+before java.lang.String.valueOf(java.lang.Boolean)
+after  java.lang.String.valueOf(java.lang.Boolean)
+""", log.toString())
+    }
+
+    void testInterceptionOfGroovyClasses(){
+        def slicer = new groovy.mock.example.CheeseSlicer()
+        def proxy = ProxyMetaClass.getInstance(slicer.class)
+        proxy.setInterceptor(logInterceptor)
+        proxy.use(slicer) {
+            slicer.coffeeBreak('')
+        }
+        assertEquals(
+"""
+before groovy.mock.example.CheeseSlicer.coffeeBreak(java.lang.String)
+after  groovy.mock.example.CheeseSlicer.coffeeBreak(java.lang.String)
+""", log.toString())
+    }
+}
+
+
+
diff --git a/groovy/src/test/groovy/lang/LongRangeTest.java b/groovy/src/test/groovy/lang/LongRangeTest.java
new file mode 100644
index 0000000..e8e73c6
--- /dev/null
+++ b/groovy/src/test/groovy/lang/LongRangeTest.java
@@ -0,0 +1,70 @@
+/*
+ $Id: LongRangeTest.java,v 1.1 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+
+/**
+ * Tests {@link ObjectRange}s of {@link Long}s.
+ *
+ * @author Edwin Tellman
+ */
+public class LongRangeTest extends NumberRangeTest {
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Range createRange(int from, int to) {
+        return new ObjectRange(new Long(from), new Long(to));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Comparable createValue(int value) {
+        return new Long(value);
+    }
+
+}
diff --git a/groovy/src/test/groovy/lang/MapOfClosureTest.groovy b/groovy/src/test/groovy/lang/MapOfClosureTest.groovy
new file mode 100644
index 0000000..3e34fd1
--- /dev/null
+++ b/groovy/src/test/groovy/lang/MapOfClosureTest.groovy
@@ -0,0 +1,95 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package groovy.lang

+

+/**

+ * Tests maps of closures coerced to classes by asType()

+ *

+ * @author Jochen Theodorou

+ * @author Guillaume Laforge

+ */

+class MapOfClosureTest extends GroovyTestCase {

+

+    void testInterfaceProxy() {

+        def outer = 1

+        def x = [run: { outer++ }] as Runnable

+        x.run()

+

+        assert x instanceof Runnable

+        assert outer == 2

+    }

+

+    void testObject() {

+        def m = [bar: { "foo" }]

+        def x = m as Object

+

+        assert x.is(m)

+        assert "foo" == x.bar()

+    }

+

+    void testAbstractClassSubclassing() {

+        def outer = 1

+        def x = [run: { outer++ }] as TimerTask

+        x.run()

+        assert x instanceof TimerTask

+        assert outer == 2

+    }

+

+    /**

+     * Checks public and protected methods from parents can also be overriden by the Map coercion to classes.

+     */

+    void testOverrideProtectedMethods() {

+        def b = [pub: { "map pub" }, prot: { "map prot" }, child: { "map child" }] as B

+

+        assert "map pub" == b.pub()

+        assert "map prot" == b.prot()

+        assert "map child" == b.child()

+        assert "abstract" == b.abstractMethod()

+    }

+

+    /**

+     * Checks that abstract methods can also be overriden.

+     */

+    void testAbstractMethodIsOverrided() {

+        def a = [abstractMethod: { "map abstract" }] as A

+

+        assert "map abstract" == a.abstractMethod()

+    }

+

+    /**

+     * Verify that complex method signatures, even with primitive types and arrays, can be overriden.

+     */

+    void testComplexMethodSignature() {

+        def c = [foo: { int a, List b, Double[] c -> ["map foo"] as String[] }] as C

+

+        assert ["map foo"] as String[] == c.foo(1, ['a', 'b'], [0.2, 0.3] as Double[])

+    }

+}

+

+abstract class A {

+    protected prot() { "prot" }

+    def pub() { "pub" }

+    abstract abstractMethod()

+}

+

+class B extends A {

+    protected child() { "child" }

+    def abstractMethod() { "abstract" }

+}

+

+class C {

+    String[] foo(int a, List b, Double[] c) { ["foo"] as String[] }

+}

diff --git a/groovy/src/test/groovy/lang/MetaClassPropertyTest.groovy b/groovy/src/test/groovy/lang/MetaClassPropertyTest.groovy
new file mode 100644
index 0000000..165605b
--- /dev/null
+++ b/groovy/src/test/groovy/lang/MetaClassPropertyTest.groovy
@@ -0,0 +1,20 @@
+package groovy.lang
+
+class MetaClassPropertyTest extends GroovyTestCase {
+	void testForJavaClass() {
+		def foo = "hello world"
+		
+		def metaClass = foo.metaClass
+		assertEquals String, metaClass.theClass
+		assert String.metaClass instanceof ExpandoMetaClass
+	}                                          
+	
+	void testForGroovyClass() {
+		 def t = new MCPTest1()
+		
+		assertEquals MCPTest1, t.metaClass.theClass        
+		assert MCPTest1.metaClass instanceof ExpandoMetaClass
+	}
+}
+class MCPTest1 {	
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/MetaClassTest.java b/groovy/src/test/groovy/lang/MetaClassTest.java
new file mode 100644
index 0000000..94e474f
--- /dev/null
+++ b/groovy/src/test/groovy/lang/MetaClassTest.java
@@ -0,0 +1,235 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+ * 
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met: 1. Redistributions of source code must retain
+ * copyright statements and notices. Redistributions must also contain a copy
+ * of this document. 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution. 3.
+ * The name "groovy" must not be used to endorse or promote products derived
+ * from this Software without prior written permission of The Codehaus. For
+ * written permission, please contact info@codehaus.org. 4. Products derived
+ * from this Software may not be called "groovy" nor may "groovy" appear in
+ * their names without prior written permission of The Codehaus. "groovy" is a
+ * registered trademark of The Codehaus. 5. Due credit should be given to The
+ * Codehaus - http://groovy.codehaus.org/
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *  
+ */
+
+package groovy.lang;
+
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MetaClassTest extends GroovyTestCase {
+
+    public void testMetaClass() {
+        Class foo = String[].class;
+        System.out.println(foo + " name: " + foo.getName());
+
+        MetaClass metaClass = InvokerHelper.getMetaClass(this);
+
+        assertTrue("got metaclass", metaClass != null);
+
+        metaClass.invokeMethod(this, "doSomething", new Object[0]);
+    }
+
+    public void testArray() {
+        String[] value = new String[]{"hello"};
+
+        MetaClass metaClass = InvokerHelper.getMetaClass(value);
+
+        assertTrue("got metaclass", metaClass != null);
+
+        metaClass.invokeMethod(value, "toString", new Object[0]);
+    }
+
+    public void testString() {
+        String value = "hello";
+
+        MetaClass metaClass = InvokerHelper.getMetaClass(value);
+
+        assertTrue("got metaclass", metaClass != null);
+
+        Object answer = metaClass.invokeMethod(value, "toString", new Object[0]);
+
+        assertEquals("hello", answer);
+    }
+
+    public void testObject() {
+        Object value = new Object();
+
+        MetaClass metaClass = InvokerHelper.getMetaClass(value);
+
+        assertTrue("got metaclass", metaClass != null);
+
+        metaClass.invokeMethod(value, "toString", new Object[0]);
+    }
+
+    public void testPublicField() {
+        DymmyClass dymmyClass = new DymmyClass();
+
+        MetaClass metaClass = InvokerHelper.getMetaClass(dymmyClass);
+
+        assertEquals(metaClass.getProperty(dymmyClass, "x"), new Integer(0));
+        assertEquals(metaClass.getProperty(dymmyClass, "y"), "none");
+
+        metaClass.setProperty(dymmyClass, "x", new Integer(25));
+        assertEquals(dymmyClass.x, 25);
+
+        metaClass.setProperty(dymmyClass, "y", "newvalue");
+        assertEquals(dymmyClass.y, "newvalue");
+    }
+
+    public void testSetPropertyWithInt() {
+        DymmyClass dymmyClass = new DymmyClass();
+        MetaClass metaClass = InvokerHelper.getMetaClass(dymmyClass);
+        metaClass.setProperty(dymmyClass, "anInt", new Integer(10));
+    }
+
+    public void testSetPropertyWithDoubleArray() {
+        DymmyClass dymmyClass = new DymmyClass();
+        MetaClass metaClass = InvokerHelper.getMetaClass(dymmyClass);
+        Double[][] matrix2 =
+                {
+                        {
+                                new Double(35), new Double(50), new Double(120)
+                        },
+                        {
+                                new Double(75), new Double(80), new Double(150)
+                        }
+                };
+        metaClass.setProperty(dymmyClass, "matrix", matrix2);
+        metaClass.setProperty(dymmyClass, "matrix2", matrix2);
+    }
+
+    public void testSetPropertyWithArray() {
+        DymmyClass dymmyClass = new DymmyClass();
+        MetaClass metaClass = InvokerHelper.getMetaClass(dymmyClass);
+
+        // test int[]
+        int[] ints = new int[]{
+                0, 1, 2, 3
+        };
+        metaClass.setProperty(dymmyClass, "ints", ints);
+        assertEquals(ints, metaClass.getProperty(dymmyClass, "ints"));
+
+        // test Integer[]
+        Integer[] integers = new Integer[]{
+                new Integer(0), new Integer(1), new Integer(2), new Integer(3)
+        };
+        metaClass.setProperty(dymmyClass, "integers", integers);
+        assertEquals(integers, metaClass.getProperty(dymmyClass, "integers"));
+    }
+
+    public void testSetPropertyWithList() {
+        DymmyClass dymmyClass = new DymmyClass();
+        MetaClass metaClass = InvokerHelper.getMetaClass(dymmyClass);
+
+        // test list
+        ArrayList list = new ArrayList();
+        list.add(new Integer(120));
+        list.add(new Integer(150));
+
+        // test int[]
+        metaClass.setProperty(dymmyClass, "ints", list);
+
+        // test Integer[]
+        metaClass.setProperty(dymmyClass, "integers", list);
+    }
+
+    public void testMetaMethodsOnlyAddedOnce() {
+        MetaClass metaClass = InvokerHelper.getMetaClass("some String");
+
+        List methods = metaClass.getMetaMethods();
+        for (Iterator iter = methods.iterator(); iter.hasNext();) {
+            MetaMethod method = (MetaMethod) iter.next();
+            int count = 0;
+            for (Iterator inner = methods.iterator(); inner.hasNext();) {
+                MetaMethod runner = (MetaMethod) inner.next();
+                if (method.equals(runner)) {
+                    System.out.println("runner = " + runner);
+                    System.out.println("method = " + method);
+                    count++;
+                }
+            }
+            assertEquals("count of Method " + method.getName(), 1, count);
+        }
+
+    }
+
+
+    public void doSomething() {
+        System.out.println("Called doSomething()");
+    }
+}
+
+
+class DymmyClass {
+    public int x = 0;
+    public String y = "none";
+
+    private int anInt;
+    private int[] ints;
+    private Integer[] integers;
+    double[][] matrix2;
+    Double[][] matrix;
+
+    public Integer[] getIntegers() {
+        return integers;
+    }
+
+    public void setIntegers(Integer[] integers) {
+        this.integers = integers;
+    }
+
+    public int[] getInts() {
+        return ints;
+    }
+
+    public void setInts(int[] ints) {
+        this.ints = ints;
+    }
+
+    public int getAnInt() {
+        return anInt;
+    }
+
+    public void setAnInt(int anInt) {
+        this.anInt = anInt;
+    }
+
+    public void setMatrix(Double[][] matrix) {
+        this.matrix = matrix;
+    }
+
+    public void setMatrix2(double[][] matrixReloaded) {
+        this.matrix2 = matrixReloaded;
+    }
+
+}
+
diff --git a/groovy/src/test/groovy/lang/MethodMissingTest.groovy b/groovy/src/test/groovy/lang/MethodMissingTest.groovy
new file mode 100644
index 0000000..b44610f
--- /dev/null
+++ b/groovy/src/test/groovy/lang/MethodMissingTest.groovy
@@ -0,0 +1,103 @@
+/**
+ * Tests for method missing handling in Groovy
+ 
+ * @author Graeme Rocher
+ * @since 1.1
+  *
+ * Created: Jul 17, 2007
+ * Time: 3:48:15 PM
+ * 
+ */
+
+package groovy.lang
+class MethodMissingTest extends GroovyTestCase {
+
+
+    void testOverrideStaticMethodMissingTwice() {
+        MMTest2.metaClass.'static'.methodMissing = { String name, args -> "foo" }
+        assertEquals "foo",MMTest2.doStuff()
+        MMTest2.metaClass.'static'.methodMissing = { String name, args -> "bar" }
+        assertEquals "bar",MMTest2.doStuff()
+    }
+
+    void testSimpleMethodMissing() {
+        def t = new MMTest()
+        assertEquals "world", t.hello()
+        assertEquals "foo", t.stuff()
+    }
+
+    void testMethodMissingViaMetaClass() {
+        def t1 = new MMTest2()
+        shouldFail(MissingMethodException) {
+            t1.stuff()
+        }
+        assertEquals "world", t1.hello()
+        MMTest2.metaClass.methodMissing = { String name, args ->
+            "foo"
+        }
+        def t2 = new MMTest2()
+        assertEquals "world", t2.hello()
+        assertEquals "foo", t2.stuff()
+    }
+
+    void testMethodMissingWithRegistration() {
+        MMTest2.metaClass.methodMissing = { String name, args ->
+             MMTest2.metaClass."$name" = {-> "bar" }        
+            "foo"
+        }
+        def t2 = new MMTest2()
+        assertEquals "world", t2.hello()
+        assertEquals "foo", t2.stuff()
+        assertEquals "bar", t2.stuff()
+
+    }
+
+
+    void testStaticMethodMissingViaMetaClass() {
+
+        assertEquals "world", MMTest3.hello()
+        shouldFail(MissingMethodException) {
+            MMTest3.stuff()            
+        }
+        MMTest3.metaClass.'static'.methodMissing = { String name, args->
+             "foo"
+        }
+
+        assertEquals "world", MMTest3.hello()
+        assertEquals "foo", MMTest3.stuff()
+    }
+
+    void testMethodMissingWithInheritance() {
+         assertEquals "world",MMTest6.hello()
+         assertEquals "cruel world",MMTest6.goodbye()
+
+         MMTest6.metaClass.'static'.methodMissing = { String name, args ->
+            "foo"
+         }
+         assertEquals "foo",MMTest6.bar()
+
+         shouldFail(MissingMethodException) {
+             MMTest5.bar()
+         }
+    }
+
+}
+class MMTest {
+    def hello() { "world" }
+    def methodMissing(String name, args) {
+        "foo"
+    }
+}
+class MMTest2 {
+    def hello() { "world" }
+}
+class MMTest3 {
+    static hello() { "world" }
+}
+
+class MMTest5 {
+    static hello() { "world" }
+}
+class MMTest6 extends MMTest5 {
+    static goodbye() { "cruel world" }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/MockWriter.java b/groovy/src/test/groovy/lang/MockWriter.java
new file mode 100644
index 0000000..d1b4c56
--- /dev/null
+++ b/groovy/src/test/groovy/lang/MockWriter.java
@@ -0,0 +1,81 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.lang;
+
+
+/**
+ * A mock class for testing writer based code
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MockWriter {
+
+    private String output;
+
+    public String getOutput() {
+        String answer = output;
+        output = null;
+        return answer;
+    }
+
+    public void setOutput(String output) {
+        this.output = output;
+    }
+
+    public void println() {
+        setOutput("println()");
+    }
+
+    public void println(Object object) {
+        setOutput("println(" + object + ")");
+    }
+
+    public void print(Object object) {
+        setOutput("print(" + object + ")");
+    }
+}
diff --git a/groovy/src/test/groovy/lang/NumberRangeTest.java b/groovy/src/test/groovy/lang/NumberRangeTest.java
new file mode 100644
index 0000000..35f6746
--- /dev/null
+++ b/groovy/src/test/groovy/lang/NumberRangeTest.java
@@ -0,0 +1,760 @@
+/*
+ $Id: NumberRangeTest.java,v 1.2 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Provides unit tests for ranges of numbers.
+ *
+ * @author Edwin Tellman
+ */
+public abstract class NumberRangeTest extends TestCase {
+
+    /**
+     * Records the values passed to a closure.
+     */
+    protected static class RecordingClosure extends Closure {
+        /**
+         * Holds the values passed in
+         */
+        final List callLog;
+
+        /**
+         * Creates a new <code>RecordingClosure</code>
+         *
+         * @param callLog is filled with the values passed to <code>doCall</code>
+         */
+        RecordingClosure(final List callLog) {
+            super(null);
+            this.callLog = callLog;
+        }
+
+        /**
+         * Stores <code>params</code> in the <code>callLog</code>.
+         *
+         * @param params the parameters.
+         * @return null
+         */
+        public Object doCall(final Object params) {
+            callLog.add(params);
+            return null;
+        }
+    }
+
+    /**
+     * Creates a {@link Range} to test.
+     *
+     * @param from the first value in the range.
+     * @param to   the last value in the range.
+     * @return a {@link Range} to test
+     */
+    protected abstract Range createRange(final int from, final int to);
+
+    /**
+     * Creates a value in the range.
+     *
+     * @param value the value to create.
+     * @return a value in the range.
+     */
+    protected abstract Comparable createValue(final int value);
+
+    /**
+     * Tests <code>hashCode</code> and <code>equals</code> comparing one {@link IntRange} to another {@link IntRange}.
+     */
+    public final void testHashCodeAndEquals() {
+        Range a = createRange(1, 11);
+        Range b = createRange(1, 11);
+        Range c = createRange(2, 11);
+
+        assertEquals("hashcode", a.hashCode(), b.hashCode());
+        assertTrue("hashcode", a.hashCode() != c.hashCode());
+
+        assertEquals("a and b", a, b);
+        assertFalse("a != c", a.equals(c));
+    }
+
+    /**
+     * Tests using different classes for 'from' and 'to'.
+     */
+    public void testDifferentClassesForFromAndTo() {
+        final Integer from = new Integer(1);
+        final Comparable to = createValue(5);
+        final Range range = new ObjectRange(from, to);
+
+        assertEquals("wrong 'from' value", from, range.getFrom());
+        assertEquals("wrong 'to' value", to, range.getTo());
+    }
+
+    /**
+     * Tests a <code>null</code> 'from' value.
+     */
+    public void testNullFrom() {
+        try {
+            new ObjectRange(null, createValue(5));
+            fail("null 'from' accepted");
+        }
+        catch (IllegalArgumentException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Tests a <code>null</code> 'to' value.
+     */
+    public void testNullTo() {
+        try {
+            new ObjectRange(createValue(23), null);
+            fail("null 'to' accepted");
+        }
+        catch (IllegalArgumentException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Tests stepping through a range by two with a closure.
+     */
+    public void testStepByTwoWithClosure() {
+        final List callLog = new ArrayList();
+        final Closure closure = new RecordingClosure(callLog);
+
+        final Range range = createRange(0, 4);
+        range.step(2, closure);
+
+        assertEquals("wrong number of calls to closure", 3, callLog.size());
+        final Iterator iter = callLog.iterator();
+        for (int i = 0; i <= 4; i += 2) {
+            assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+        }
+    }
+
+    /**
+     * Tests iterating over a one-element range.
+     */
+    public void testOneElementRange() {
+        final Range range = createRange(1, 1);
+        int next = 1;
+        for (Iterator iter = range.iterator(); iter.hasNext();) {
+            final Number number = (Number) iter.next();
+            assertEquals("wrong number", createValue(next++), number);
+        }
+        assertEquals("wrong number of elements in iteration", 2, next);
+    }
+
+    /**
+     * Tests stepping through a reversed range by two with a closure.
+     */
+    public void testReverseStepByTwoWithClosure() {
+        final List callLog = new ArrayList();
+        final Closure closure = new RecordingClosure(callLog);
+
+        final Range range = createRange(4, 0);
+        range.step(2, closure);
+
+        assertEquals("wrong number of calls to closure", 3, callLog.size());
+        final Iterator iter = callLog.iterator();
+        for (int i = 4; i >= 0; i -= 2) {
+            assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+        }
+    }
+
+    /**
+     * Tests stepping through a range with a closure.
+     */
+    public void testStepByOneWithClosure() {
+        final List callLog = new ArrayList();
+        final Closure closure = new RecordingClosure(callLog);
+
+        final Range range = createRange(1, 5);
+        range.step(1, closure);
+
+        assertEquals("wrong number of calls to closure", 5, callLog.size());
+        final Iterator iter = callLog.iterator();
+        for (int i = 1; i <= 5; i++) {
+            assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+        }
+    }
+
+    /**
+     * Tests stepping through a reversed range by one with a closure.
+     */
+    public void testReverseStepByOneWithClosure() {
+        final List callLog = new ArrayList();
+        final Closure closure = new RecordingClosure(callLog);
+
+        final Range range = createRange(5, 1);
+        range.step(1, closure);
+
+        assertEquals("wrong number of calls to closure", 5, callLog.size());
+        final Iterator iter = callLog.iterator();
+        for (int i = 5; i >= 1; i--) {
+            assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+        }
+    }
+
+    /**
+     * Tests stepping backwards through a range with a closure.
+     */
+    public void testNegativeStepByOneWithClosure() {
+        final List callLog = new ArrayList();
+        final Closure closure = new RecordingClosure(callLog);
+
+        final Range range = createRange(1, 5);
+        range.step(-1, closure);
+
+        assertEquals("wrong number of calls to closure", 5, callLog.size());
+        final Iterator iter = callLog.iterator();
+        for (int i = 5; i >= 1; i--) {
+            assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+        }
+    }
+
+    /**
+     * Tests stepping backwards through a reversed range with a closure.
+     */
+    public void testNegativeReverseStepByOneWithClosure() {
+        final List callLog = new ArrayList();
+        final Closure closure = new RecordingClosure(callLog);
+
+        final Range range = createRange(5, 1);
+        range.step(-1, closure);
+
+        assertEquals("wrong number of calls to closure", 5, callLog.size());
+        final Iterator iter = callLog.iterator();
+        for (int i = 1; i <= 5; i++) {
+            assertEquals("wrong argument passed to closure", createValue(i), iter.next());
+        }
+    }
+
+    /**
+     * Tests stepping backwards through a range with a step size greater than the range size.
+     */
+    public void testStepLargerThanRange() {
+        final List callLog = new ArrayList();
+        final Closure closure = new RecordingClosure(callLog);
+
+        final Range range = createRange(1, 5);
+
+        range.step(6, closure);
+        assertEquals("wrong number of calls to closure", 1, callLog.size());
+        assertEquals("wrong value", createValue(1), callLog.get(0));
+
+        final List stepList = range.step(6);
+        assertEquals("wrong number of values in result", 1, stepList.size());
+        assertEquals("wrong value", createValue(1), callLog.get(0));
+    }
+
+    /**
+     * Tests stepping through a range by one.
+     */
+    public void testStepByOne() {
+        final Range range = createRange(1, 5);
+        final List result = range.step(1);
+
+        assertEquals("wrong number of calls", 5, result.size());
+        final Iterator iter = result.iterator();
+        for (int i = 1; i <= 5; i++) {
+            assertEquals("incorrect value in result", createValue(i), iter.next());
+        }
+    }
+
+    /**
+     * Tests stepping through a range by two.
+     */
+    public void testStepByTwo() {
+        final Range range = createRange(1, 5);
+        final List result = range.step(2);
+
+        assertEquals("wrong number of calls", 3, result.size());
+        final Iterator iter = result.iterator();
+        for (int i = 1; i <= 5; i += 2) {
+            assertEquals("incorrect value in result", createValue(i), iter.next());
+        }
+    }
+
+    /**
+     * Tests getting the size.
+     */
+    public void testSize() {
+        Range range = createRange(0, 10);
+        assertEquals("Size of " + range, 11, range.size());
+        range = createRange(0, 1);
+        assertEquals("Size of " + range, 2, range.size());
+        range = createRange(0, 0);
+        assertEquals("Size of " + range, 1, range.size());
+    }
+
+    /**
+     * Tests asking for an index outside of the valid range
+     */
+    public void testGetOutOfRange() {
+        Range r = createRange(10, 20);
+
+        try {
+            r.get(-1);
+            fail("Should have thrown IndexOutOfBoundsException");
+        }
+        catch (IndexOutOfBoundsException e) {
+            assertTrue("expected exception thrown", true);
+        }
+        try {
+            r.get(11);
+            fail("Should have thrown IndexOutOfBoundsException");
+        }
+        catch (IndexOutOfBoundsException e) {
+            assertTrue("expected exception thrown", true);
+        }
+
+    }
+
+    /**
+     * Tests getting a sub list.
+     */
+    public void testSubList() {
+        Range range = createRange(0, 5);
+
+        List subList = range.subList(2, 4);
+        assertEquals("size", 2, subList.size());
+
+        assertTrue("sublist not a range", subList instanceof Range);
+        Range subListRange = (Range) subList;
+
+        assertEquals("from", createValue(2), subListRange.getFrom());
+        assertEquals("to", createValue(3), subListRange.getTo());
+
+        subList = range.subList(0, 6);
+        assertEquals("size", 6, subList.size());
+
+        assertTrue("sublist not a range", subList instanceof Range);
+        subListRange = (Range) subList;
+
+        assertEquals("from", createValue(0), subListRange.getFrom());
+        assertEquals("to", createValue(5), subListRange.getTo());
+    }
+
+    /**
+     * Tests creating a sub list with a negative "from" index.
+     */
+    public void testSubListNegativeFrom() {
+        try {
+            final Range range = createRange(1, 5);
+            range.subList(-1, 3);
+            fail("accepted sub list with negative index");
+        }
+        catch (IndexOutOfBoundsException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Tests creating a sub list with an out of range "to" index.
+     */
+    public void testSubListOutOfRangeTo() {
+        try {
+            final Range range = createRange(0, 3);
+            range.subList(0, 5);
+            fail("accepted sub list with invalid 'to'");
+        }
+        catch (IndexOutOfBoundsException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Tests creating a sub list with "from" grater than "to."
+     */
+    public void testSubListFromGreaterThanTo() {
+        try {
+            final Range range = createRange(1, 5);
+            range.subList(3, 2);
+            fail("accepted sub list with 'from' greater than 'to'");
+        }
+        catch (IllegalArgumentException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Tests creating an empty sub list.
+     */
+    public void testEmptySubList() {
+        final Range range = createRange(1, 5);
+
+        List subList = range.subList(0, 0);
+        assertEquals("wrong number of elements in sub list", 0, subList.size());
+
+        subList = range.subList(2, 2);
+        assertEquals("wrong number of elements in sub list", 0, subList.size());
+    }
+
+    /**
+     * Tests iterating over a non-reversed range.
+     */
+    public void testIterate() {
+        final Range range = createRange(1, 5);
+        int next = 1;
+        final Iterator iter = range.iterator();
+        while (iter.hasNext()) {
+            final Object value = iter.next();
+            assertEquals("wrong next value", createValue(next++), value);
+        }
+        assertEquals("wrong number of elements in iteration", 6, next);
+        assertNull("got element after iterator finished", iter.next());
+    }
+
+    /**
+     * Tests removing an element from the range using an iterator (not supported).
+     */
+    public void testRemoveFromIterator() {
+        final Range range = createRange(1, 5);
+
+        try {
+            final Iterator iter = range.iterator();
+            iter.remove();
+            fail("successfully removed an element using an iterator");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Tests iterating over a reversed range.
+     */
+    public void testIterateReversed() {
+        final Range range = createRange(5, 1);
+        int next = 5;
+        for (Iterator iter = range.iterator(); iter.hasNext();) {
+            assertEquals("wrong number", createValue(next--), iter.next());
+        }
+        assertEquals("wrong number of elements in iteration", 0, next);
+    }
+
+    /**
+     * Tests creating an <code>IntRange</code> with from > to.
+     */
+    public void testFromGreaterThanTo() {
+        final int from = 9;
+        final int to = 0;
+        final Range range = createRange(from, to);
+
+        assertTrue("range not reversed", range.isReverse());
+
+        // make sure to/from are swapped
+        assertEquals("from incorrect", createValue(to), range.getFrom());
+        assertEquals("to incorrect", createValue(from), range.getTo());
+
+        assertEquals("wrong size", 10, range.size());
+
+        assertEquals("wrong first element", createValue(9), range.get(0));
+        assertEquals("wrong last element", createValue(0), range.get(9));
+    }
+
+    /**
+     * Tests creating an <code>IntRange</code> with from == to.
+     */
+    public void testFromEqualsTo() {
+        final Range range = createRange(5, 5);
+
+        assertFalse("range reversed", range.isReverse());
+        assertEquals("wrong size", 1, range.size());
+    }
+
+    /**
+     * Tests creating an <code>IntRange</code> with from < to.
+     */
+    public void testFromLessThanTo() {
+        final int from = 1;
+        final int to = 4;
+        final Range range = createRange(from, to);
+
+        assertFalse("range reversed", range.isReverse());
+
+        assertEquals("to incorrect", createValue(from), range.getFrom());
+        assertEquals("from incorrect", createValue(to), range.getTo());
+
+        assertEquals("wrong size", 4, range.size());
+    }
+
+    /**
+     * Making a range equal a list is not actually possible, since list.equals(range) will not evaluate to
+     * <code>true</code> and <code>equals</code> should be symmetric.
+     */
+    public void testEqualsList() {
+        final List list = new ArrayList();
+        list.add(createValue(1));
+        list.add(createValue(2));
+
+        final Range range = createRange(1, 2);
+
+        // cast to Object to test routing through equals(Object)
+        assertTrue("range does not equal list", range.equals((Object) list));
+        assertTrue("list does not equal range", list.equals(range));
+        assertEquals("hash codes are not equal", range.hashCode(), list.hashCode());
+
+        // compare lists that are the same size but contain different elements
+        list.set(0, createValue(3));
+        assertFalse("range equals list", range.equals(list));
+        assertFalse("list equals range", list.equals(range));
+        assertFalse("hash codes are equal", range.hashCode() == list.hashCode());
+
+        // compare a list longer than the range
+        list.set(0, createValue(1));
+        list.add(createValue(3));
+        assertFalse("range equals list", range.equals(list));
+        assertFalse("list equals range", list.equals(range));
+        assertFalse("hash are equal", range.hashCode() == list.hashCode());
+
+        // compare a list shorter than the range
+        list.remove(2);
+        list.remove(1);
+        assertFalse("range equals list", range.equals(list));
+        assertFalse("list equals range", list.equals(range));
+        assertFalse("hash are equal", range.hashCode() == list.hashCode());
+    }
+
+    /**
+     * Tests comparing {@link Range} to an object that is not a {@link Range}.
+     */
+    public void testEqualsNonRange() {
+        final Range range = createRange(1, 5);
+        assertFalse("range equal to string", range.equals("hello"));
+    }
+
+    /**
+     * Tests comparing a {@link Range} cast to an {@link Object}
+     */
+    public void testEqualsRangeAsObject() {
+        final Range range1 = createRange(1, 5);
+        final Range range2 = createRange(1, 5);
+        assertTrue("ranges not equal", range1.equals((Object) range2));
+    }
+
+    /**
+     * Tests comparing two {@link Range}s to each other.
+     */
+    public void testEqualsRange() {
+        final Range range1 = createRange(1, 5);
+        Range range2 = createRange(1, 5);
+        assertTrue("ranges not equal", range1.equals((Object) range2));
+        assertTrue("ranges not equal", range2.equals((Object) range1));
+        assertEquals("hash codes not equal", range1.hashCode(), range2.hashCode());
+
+        range2 = createRange(0, 5);
+        assertFalse("ranges equal", range1.equals((Object) range2));
+        assertFalse("ranges equal", range2.equals((Object) range1));
+        assertFalse("hash codes equal", range1.hashCode() == range2.hashCode());
+
+        range2 = createRange(1, 6);
+        assertFalse("ranges equal", range1.equals((Object) range2));
+        assertFalse("ranges equal", range2.equals((Object) range1));
+        assertFalse("hash codes equal", range1.hashCode() == range2.hashCode());
+
+        range2 = createRange(0, 6);
+        assertFalse("ranges equal", range1.equals((Object) range2));
+        assertFalse("ranges equal", range2.equals((Object) range1));
+        assertFalse("hash codes equal", range1.hashCode() == range2.hashCode());
+
+        range2 = createRange(2, 4);
+        assertFalse("ranges equal", range1.equals((Object) range2));
+        assertFalse("ranges equal", range2.equals((Object) range1));
+        assertFalse("hash codes equal", range1.hashCode() == range2.hashCode());
+
+        range2 = createRange(5, 1);
+        assertFalse("ranges equal", range1.equals((Object) range2));
+        assertFalse("ranges equal", range2.equals((Object) range1));
+        assertFalse("hash codes equal", range1.hashCode() == range2.hashCode());
+    }
+
+    /**
+     * Tests <code>toString</code> and <code>inspect</code>
+     */
+    public void testToStringAndInspect() {
+        Range range = createRange(1, 5);
+        String expected = range.getFrom() + ".." + range.getTo();
+        assertEquals("wrong string representation", expected, range.toString());
+        assertEquals("wrong string representation", expected, range.inspect());
+
+        range = createRange(5, 1);
+        expected = range.getTo() + ".." + range.getFrom();
+        assertEquals("wrong string representation", expected, range.toString());
+        assertEquals("wrong string representation", expected, range.inspect());
+    }
+
+    /**
+     * Tests <code>getFrom</code> and <code>getTo</code>.
+     */
+    public void testGetFromAndTo() {
+        final int from = 1, to = 5;
+        final Range range = createRange(from, to);
+
+        assertEquals("wrong 'from' value", createValue(from), range.getFrom());
+        assertEquals("wrong 'to' value", createValue(to), range.getTo());
+    }
+
+    /**
+     * Tests comparing a {@link Range} to <code>null</code>.
+     */
+    public void testEqualsNull() {
+        final Range range = createRange(1, 5);
+        assertFalse("range equal to null", range.equals(null));
+        assertFalse("range equal to null Object", range.equals((Object) null));
+        assertFalse("range equal to null Range", range.equals((Range) null));
+        assertFalse("range equal to null List", range.equals((List) null));
+    }
+
+    /**
+     * Tests attempting to add a value to a range.
+     */
+    public void testAddValue() {
+        try {
+            final Range range = createRange(1, 5);
+            range.add(createValue(20));
+            fail("expected exception not thrown");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    /**
+     * Tests attempting to remove a value from a range.
+     */
+    public void testRemoveValue() {
+        try {
+            final Range range = createRange(1, 5);
+            range.remove(0);
+            fail("expected exception not thrown");
+        }
+        catch (UnsupportedOperationException e) {
+            assertTrue("expected exception thrown", true);
+        }
+    }
+
+    private void doTestContains(int from, int to, Range range) {
+        // test integers
+        assertTrue("missing 'from' value", range.contains(createValue(from)));
+        assertTrue("missing 'to' value", range.contains(createValue(to)));
+        assertTrue("missing mid point", range.contains(createValue((from + to) / 2)));
+        assertFalse("contains out of range value", range.contains(createValue(from - 1)));
+        assertFalse("contains out of range value", range.contains(createValue(to + 1)));
+
+        // test ranges
+        assertTrue("missing same range", range.containsAll(createRange(from, to)));
+        assertTrue("missing same range", range.containsAll(createRange(to, from)));
+        assertTrue("missing strict subset", range.containsAll(createRange(from + 1, to - 1)));
+        assertTrue("missing subset", range.containsAll(createRange(from, to - 1)));
+        assertTrue("missing subset", range.containsAll(createRange(from + 1, to)));
+        assertFalse("contains non-subset", range.containsAll(createRange(from - 1, to)));
+        assertFalse("contains non-subset", range.containsAll(createRange(from, to + 1)));
+        assertFalse("contains non-subset", range.containsAll(createRange(from - 2, from - 1)));
+
+        // ranges don't contain other ranges
+        assertFalse("range contains sub-range", range.contains(createRange(from + 1, to - 1)));
+
+        // test list
+        final List list = new ArrayList();
+        list.add(createValue(from));
+        list.add(createValue(to));
+        assertTrue("missing strict subset", range.containsAll(list));
+
+        // test non-integer number
+        assertFalse("contains Float", range.contains(new Float((to + from) / 2.0 + 0.3)));
+    }
+
+    /**
+     * Tests whether the range contains a {@link Comparable} object which is not comparable with a {@link Number}.
+     */
+    public void testContainsIncompatibleComparable() {
+        final Range range = createRange(1, 5);
+        assertFalse("range contains string", range.contains("hello"));
+        assertFalse("range contains string", range.contains("1"));
+    }
+
+    /**
+     * Tests whether the range contains a non-comparable object.
+     */
+    public void testContainsNonComparable() {
+        final Range range = createRange(1, 5);
+        assertFalse("range contains hash map", range.contains(new HashMap()));
+    }
+
+    /**
+     * Tests whether a {@link Range} contains another {@link Range} or a specific integer.
+     */
+    public void testContains() {
+        final int from = 1, to = 5;
+        doTestContains(from, to, createRange(from, to));
+        doTestContains(from, to, createRange(to, from));
+    }
+
+    /**
+     * Tests <code>get</code> from a reversed range.
+     */
+    public void testGetFromReversedRange() {
+        final Range range = createRange(5, 1);
+
+        for (int i = 0; i < 5; i++) {
+            assertEquals("wrong element at position " + i, createValue(5 - i), range.get(i));
+        }
+    }
+
+    /**
+     * Tests getting values from the range.
+     */
+    public void testGet() {
+        final Range range = createRange(10, 20);
+        for (int i = 0; i <= 10; i++) {
+            assertEquals("Item at index: " + i, createValue(i + 10), range.get(i));
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/lang/PropertyMissingTest.groovy b/groovy/src/test/groovy/lang/PropertyMissingTest.groovy
new file mode 100644
index 0000000..6889b2b
--- /dev/null
+++ b/groovy/src/test/groovy/lang/PropertyMissingTest.groovy
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.lang
+/**
+ * Tests the behaviour of the propertyMissing functionality of Groovy
+
+ * @author Graeme Rocher
+ * @since 1.1
+  *
+ * Created: Sep 5, 2007
+ * Time: 5:50:28 PM
+ *
+ */
+class PropertyMissingTest extends GroovyTestCase {
+
+
+    void testPropertyMissingWithMethods() {
+         def t = new PMTest1()
+
+        assertEquals "bar", t.foo
+        t.foo = "changed"
+        assertEquals "changed", t.foo
+
+        assertNull t.bar
+        t.bar = "keepme"
+
+        assertEquals "keepme", t.bar
+    }
+
+    void testPropertyMissingViaMetaClass() {
+        def store = [:]
+        PMTest2.metaClass.propertyMissing = { String name ->
+           store.name
+        }
+        PMTest2.metaClass.propertyMissing = { String name, value ->
+           store.name = value
+        }
+
+        def t = new PMTest2()
+
+       assertEquals "bar", t.foo
+       t.foo = "changed"
+       assertEquals "changed", t.foo
+       assertNull t.bar
+       t.bar = "keepme"
+
+       assertEquals "keepme", t.bar
+
+    }
+
+    void testStaticPropertyMissingViaMetaClass() {
+
+        shouldFail(MissingPropertyException) {
+            PMTest1.SOME_PROP
+        }
+
+        PMTest1.metaClass.'static'.propertyMissing = { String name ->
+            name
+        }
+
+        assertEquals "SOME_PROP", PMTest1.SOME_PROP
+        assertEquals "FOO", PMTest1.FOO
+    }
+
+}
+class PMTest1 {
+    def store = [:]
+    String foo = "bar"
+    String propertyMissing(String name) {
+        store.name
+    }
+    void propertyMissing(String name, value) {
+        store.name = value
+    }
+}
+class PMTest2 {
+    String foo = "bar"
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/RangeTest.java b/groovy/src/test/groovy/lang/RangeTest.java
new file mode 100644
index 0000000..968e722
--- /dev/null
+++ b/groovy/src/test/groovy/lang/RangeTest.java
@@ -0,0 +1,246 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+import junit.framework.TestCase;
+
+import java.math.BigDecimal;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * @author James Strachan
+ * @version $Revision$
+ */
+public class RangeTest extends TestCase {
+
+    public void testSize() {
+        Range r = createRange(0, 10);
+        assertEquals("Size of " + r, 11, r.size());
+        r = createRange(0, 1);
+        assertEquals("Size of " + r, 2, r.size());
+        r = createRange(0, 0);
+        assertEquals("Size of " + r, 1, r.size());
+
+        r = createRange(new BigDecimal("2.1"), new BigDecimal("10.0"));
+        assertEquals("Size of " + r, 8, r.size());
+    }
+
+    public void testProperties() {
+        Range r = createRange(0, 10);
+        assertEquals("from", 0, r.getFrom());
+        assertEquals("to", 10, r.getTo());
+    }
+
+    public void testGet() {
+        Range r = createRange(10, 20);
+        for (int i = 0; i < 10; i++) {
+            Integer value = (Integer) r.get(i);
+            assertEquals("Item at index: " + i, i + 10, value.intValue());
+        }
+
+        r = createRange(new BigDecimal("3.2"), new BigDecimal("9.9"));
+        for (int i = 0; i < r.size(); i++) {
+            BigDecimal value = (BigDecimal) r.get(i);
+            assertEquals("Item at index: " + i, new BigDecimal("3.2").add(new BigDecimal("" + i)), value);
+        }
+    }
+
+    public void testNullForFromOrToIsIllegal() {
+        Comparable dontcare = new Integer(0);
+        try {
+            new ObjectRange((Comparable)null, dontcare);
+            fail("Should have thrown IllegalArgumentException");
+        }
+        catch (IllegalArgumentException e) {
+            // worked
+        }
+    }
+
+    public void testGetOutOfRange() {
+        Range r = createRange(10, 20);
+
+        try {
+            r.get(-1);
+            fail("Should have thrown IndexOutOfBoundsException");
+        }
+        catch (IndexOutOfBoundsException e) {
+            // worked
+        }
+        try {
+            r.get(11);
+            fail("Should have thrown IndexOutOfBoundsException");
+        }
+        catch (IndexOutOfBoundsException e) {
+            // worked
+        }
+
+        r = createRange(new BigDecimal("-4.3"), new BigDecimal("1.4"));
+
+        try {
+            r.get(-1);
+            fail("Should have thrown IndexOutOfBoundsException");
+        }
+        catch (IndexOutOfBoundsException e) {
+            // worked
+        }
+        try {
+            r.get(7);
+            fail("Should have thrown IndexOutOfBoundsException");
+        }
+        catch (IndexOutOfBoundsException e) {
+            // worked
+        }
+
+    }
+
+    public void testContains() {
+        Range r = createRange(10, 20);
+
+        assertTrue("contains 11", r.contains(new Integer(11)));
+        assertTrue("contains 10", r.contains(new Integer(10)));
+        assertTrue("contains 19", r.contains(new Integer(19)));
+        assertFalse("contains 9", r.contains(new Integer(9)));
+        assertFalse("contains 21", r.contains(new Integer(21)));
+        assertFalse("contains 100", r.contains(new Integer(100)));
+        assertFalse("contains -1", r.contains(new Integer(-1)));
+
+        r = createRange(new BigDecimal("2.1"), new BigDecimal("10.0"));
+
+        assertTrue("contains 9.1", r.contains(new BigDecimal("9.1")));
+        assertFalse("contains 10.1", r.contains(new BigDecimal("10.1")));
+        assertFalse("contains 8.0", r.contains(new BigDecimal("8.0")));
+        assertTrue("containsWithinBounds 8.0", r.containsWithinBounds(new BigDecimal("8.0")));
+        assertTrue("containsWithinBounds 9.9999", r.containsWithinBounds(new BigDecimal("9.9999")));
+        assertTrue("containsWithinBounds 10.0", r.containsWithinBounds(new BigDecimal("10.0")));
+        assertFalse("containsWithinBounds 10.0001", r.containsWithinBounds(new BigDecimal("10.0001")));
+    }
+
+    public void testContainsWithLikeNumbers() {
+        Range r = new ObjectRange(new Integer(1), new Short((short)3));
+        assertTrue("contains 2", r.contains(new Integer(2)));
+        r = new ObjectRange(new Float(1.0), new Double(3.0));
+        assertTrue("contains 2.0d", r.contains(new Double(2.0)));
+        assertTrue("contains 2.0g", r.contains(new BigDecimal(2.0)));
+        r = new ObjectRange(new BigDecimal(1.0), new BigDecimal(3.0));
+        assertTrue("contains 2.0d", r.contains(new Double(2.0)));
+        assertTrue("contains 2.0f", r.contains(new Float(2.0)));
+    }
+
+    public void testContainsWithIncompatibleType() {
+        Range r = new ObjectRange(new Integer(1), new Short((short)3));
+        assertFalse("shouldn't contain string", r.contains("String"));
+    }
+
+    public void testSubList() {
+        Range r = createRange(10, 20);
+
+        List s = r.subList(2, 4);
+
+        Range sr = (Range) s;
+
+        assertEquals("from", 12, sr.getFrom());
+        assertEquals("to", 13, sr.getTo());
+        assertEquals("size", 2, sr.size());
+
+        r = createRange(new BigDecimal("0.5"), new BigDecimal("8.5"));
+        assertEquals("size", 9, r.size());
+        s = r.subList(2, 5);
+        sr = (Range) s;
+
+        assertEquals("from", new BigDecimal("2.5"), sr.getFrom());
+        assertEquals("to", new BigDecimal("4.5"), sr.getTo());
+        assertTrue("contains 4.5", sr.contains(new BigDecimal("4.5")));
+        assertFalse("contains 5.5", sr.contains(new BigDecimal("5.5")));
+        assertEquals("size", 3, sr.size());
+
+    }
+
+    public void testHashCodeAndEquals() {
+        Range a = createRange(1, 11);
+        Range b = createRange(1, 11);
+        Range c = createRange(2, 11);
+
+        assertEquals("hashcode", a.hashCode(), b.hashCode());
+        assertTrue("hashcode", a.hashCode() != c.hashCode());
+
+        assertEquals("a and b", a, b);
+        assertFalse("a != c", a.equals(c));
+    }
+
+    public void testIterator() {
+        Range r = createRange(5, 11);
+
+        int i = 5;
+        for (Iterator it = r.iterator(); it.hasNext();) {
+            assertEquals("equals to " + i, new Integer(i), (Integer) (it.next()));
+            i++;
+        }
+
+        r = createRange(new BigDecimal("5.0"), new BigDecimal("11.0"));
+        BigDecimal one = new BigDecimal("1.0");
+
+        BigDecimal val = new BigDecimal("5.0");
+        for (Iterator it = r.iterator(); it.hasNext();) {
+            assertEquals("equals to " + val, val, (BigDecimal) (it.next()));
+            val = val.add(one);
+        }
+    }
+
+    protected Range createRange(int from, int to) {
+        return new ObjectRange(new Integer(from), new Integer(to));
+    }
+
+    protected Range createRange(BigDecimal from, BigDecimal to) {
+        return new ObjectRange(from, to);
+    }
+
+    protected void assertEquals(String msg, int expected, Object value) {
+        assertEquals(msg, new Integer(expected), value);
+    }
+
+
+}
diff --git a/groovy/src/test/groovy/lang/RangeTestSuite.java b/groovy/src/test/groovy/lang/RangeTestSuite.java
new file mode 100644
index 0000000..905c36b
--- /dev/null
+++ b/groovy/src/test/groovy/lang/RangeTestSuite.java
@@ -0,0 +1,38 @@
+/**
+ *
+ */
+package groovy.lang;
+
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+/**
+ * Calls all the range-related tests.
+ *
+ * @author Edwin Tellman
+ */
+public final class RangeTestSuite extends TestSuite {
+
+    /**
+     * Creates a new {@link RangeTestSuite}
+     */
+    public RangeTestSuite() {
+        addTestSuite(IntRangeTest.class);
+        addTestSuite(ShortRangeTest.class);
+        addTestSuite(IntegerRangeTest.class);
+        addTestSuite(LongRangeTest.class);
+        addTestSuite(FloatRangeTest.class);
+        addTestSuite(BigDecimalRangeTest.class);
+        addTestSuite(CharacterRangeTest.class);
+        addTestSuite(RangeTest.class);
+    }
+
+    /**
+     * Runs the tests in the {@link TestRunner}.
+     *
+     * @param argv not used
+     */
+    public static void main(String[] argv) {
+        junit.textui.TestRunner.run(new RangeTestSuite());
+    }
+}
diff --git a/groovy/src/test/groovy/lang/RespondsToTest.groovy b/groovy/src/test/groovy/lang/RespondsToTest.groovy
new file mode 100644
index 0000000..8fc82f7
--- /dev/null
+++ b/groovy/src/test/groovy/lang/RespondsToTest.groovy
@@ -0,0 +1,105 @@
+/**
+ * Tests the respondsTo functionality of Groovy
+ 
+ * @author Graeme Rocher
+ * @since 1.1
+  *
+ * Created: Sep 7, 2007
+ * Time: 4:35:19 PM
+ * 
+ */
+package groovy.lang
+class RespondsToTest extends GroovyTestCase {
+
+    void testRespondsToForMethodEvaluation() {
+        RespondsToTestClass.metaClass.invokeMethod = {String name, args ->
+            def methods = RespondsToTestClass.metaClass.respondsTo(delegate,name, args*.getClass() as Object[])
+            def result
+            if(methods) {
+                // only way to get var-args to work is to do this at the moment. Yuck!
+                if(methods[0].parameterTypes.length == 1 && methods[0].parameterTypes[0].cachedClass == Object[].class)
+                    result = methods[0].invoke(delegate, [args] as Object[])
+                else
+                    result = methods[0].invoke(delegate, args)
+            }
+            else {
+                result = "foo"
+            }
+            result
+        }
+
+        def t = new RespondsToTestClass()
+        assertEquals "one", t.noArgsMethod()
+        assertEquals "two", t.varArgsMethod(1,2)
+        assertEquals "two", t.varArgsMethod(null,null)
+        assertEquals "three", t.typedArgsMethod("one",1)
+        assertEquals "three", t.typedArgsMethod(null,1)
+        assertEquals "three", t.typedArgsMethod(null,null)
+        assertEquals "four", t.overloadedMethod("one")
+        assertEquals "five", t.overloadedMethod(1)
+        assertEquals "four", t.overloadedMethod(null)
+        assertEquals "six", t.overloadedMethod()
+        assertEquals "foo", t.doStuff()
+    }
+
+    void testRespondsTo() {
+
+        RTTest2.metaClass.newM = {-> "foo" }
+
+        def t = new RTTest2()
+
+        assert t.metaClass.respondsTo(t,"one")
+        assert t.metaClass.respondsTo(t,"three")
+        assert t.metaClass.respondsTo(t,"one", String)
+        assert t.metaClass.respondsTo(t,"foo", String)
+        assert t.metaClass.respondsTo(t,"bar", String)
+        assert t.metaClass.respondsTo(t,"stuff")
+        //assert t.metaClass.respondsTo(t,"two") THIS DOESN'T WORK! Should responds to deal with closure properties?
+        assert t.metaClass.respondsTo(t,"getFive")
+        assert t.metaClass.respondsTo(t,"setFive")
+        assert t.metaClass.respondsTo(t,"setFive", String)
+        assert t.metaClass.respondsTo(t,"newM")
+        assert !t.metaClass.respondsTo(t,"one", String, Integer)
+    }
+
+    void testHasProperty() {
+        RTTest2.metaClass.getNewProp = {-> "new" }
+        def t = new RTTest2()
+
+        assert t.metaClass.hasProperty(t,"two")
+        assert t.metaClass.hasProperty(t,"five")
+        assert t.metaClass.hasProperty(t,"six")
+        assert t.metaClass.hasProperty(t,"seven")
+        assert t.metaClass.hasProperty(t,"eight")
+        assert t.metaClass.hasProperty(t,"newProp")
+
+    }
+}
+class RespondsToTestClass {
+    def noArgsMethod() { "one" }
+    def varArgsMethod(Object[] args) { "two" }
+    def typedArgsMethod(String one, Integer two) { "three" }
+    def overloadedMethod(String one) { "four" }
+    def overloadedMethod(Integer one) { "five" }
+    def overloadedMethod() { "six" }
+}
+class RTTest1 {
+    String five
+    def two = { "three" }
+    def one() { "two"}
+    def one(String one) { "two: $one" }
+    def three(String one) { "four" }
+    def three(Integer one) { "four" }
+    def foo(String name) {
+        "bar"
+    }
+
+    String getSeven() { "seven" }
+}
+class RTTest2 extends RTTest1 {
+    String six
+    def bar(String name) { "foo" }
+    static stuff() { "goodie" }
+
+    String getEight() { "eight" }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/lang/ScriptIntegerDivideTest.java b/groovy/src/test/groovy/lang/ScriptIntegerDivideTest.java
new file mode 100644
index 0000000..cdfd820
--- /dev/null
+++ b/groovy/src/test/groovy/lang/ScriptIntegerDivideTest.java
@@ -0,0 +1,18 @@
+package groovy.lang;
+
+import org.codehaus.groovy.classgen.TestSupport;
+
+
+/**
+ * @author Steve Goetze
+ * @author Jeremy Rayner
+ */
+public class ScriptIntegerDivideTest extends TestSupport {
+
+    /**
+     * Check integer division which is now a method call rather than the symbol "\".
+     */
+    public void testIntegerDivide() throws Exception {
+        assertScript("assert 4.intdiv(3) == 1");
+    }
+}
diff --git a/groovy/src/test/groovy/lang/ScriptPrintTest.java b/groovy/src/test/groovy/lang/ScriptPrintTest.java
new file mode 100644
index 0000000..a0a256c
--- /dev/null
+++ b/groovy/src/test/groovy/lang/ScriptPrintTest.java
@@ -0,0 +1,64 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.lang;
+
+import org.codehaus.groovy.classgen.TestSupport;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ScriptPrintTest extends TestSupport {
+
+    public void testScriptWithCustomPrintln() throws Exception {
+        assertScript(
+                "out = new MockWriter(); println(); assert out.output == 'println()', 'value of output is: ' + out.output\n"
+                        + "print('hey'); assert out.output == 'print(hey)' , 'value is: ' + out.output\n"
+                        + "println('hey'); assert out.output == 'println(hey)', 'value is: ' + out.output\n");
+    }
+
+}
diff --git a/groovy/src/test/groovy/lang/ScriptTest.java b/groovy/src/test/groovy/lang/ScriptTest.java
new file mode 100644
index 0000000..987bfa3
--- /dev/null
+++ b/groovy/src/test/groovy/lang/ScriptTest.java
@@ -0,0 +1,89 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) Guillaume Laforge. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.lang;
+
+import org.codehaus.groovy.classgen.TestSupport;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.runtime.MethodClosure;
+
+import java.io.IOException;
+
+/**
+ * Tests some particular script features.
+ *
+ * @author Guillaume Laforge
+ */
+public class ScriptTest extends TestSupport {
+    /**
+     * When a method is not found in the current script, checks that it's possible to call a method closure from the binding.
+     *
+     * @throws IOException
+     * @throws CompilationFailedException
+     * @throws IllegalAccessException
+     * @throws InstantiationException
+     */
+    public void testInvokeMethodFallsThroughToMethodClosureInBinding() throws IOException, CompilationFailedException, IllegalAccessException, InstantiationException {
+        String text = "if (method() == 3) { println 'succeeded' }";
+
+        GroovyCodeSource codeSource = new GroovyCodeSource(text, "groovy.script", "groovy.script");
+        GroovyClassLoader loader = new GroovyClassLoader(Thread.currentThread().getContextClassLoader());
+        Class clazz = loader.parseClass(codeSource);
+        Script script = ((Script) clazz.newInstance());
+
+        Binding binding = new Binding();
+        binding.setVariable("method", new MethodClosure(new Dummy(), "method"));
+        script.setBinding(binding);
+
+        script.run();
+    }
+
+    public static class Dummy {
+        public Integer method() {
+            return new Integer(3);
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/lang/SequenceTest.java b/groovy/src/test/groovy/lang/SequenceTest.java
new file mode 100644
index 0000000..ff50092
--- /dev/null
+++ b/groovy/src/test/groovy/lang/SequenceTest.java
@@ -0,0 +1,103 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.lang;
+
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.util.List;
+
+/**
+ * Tests the use of the structured Attribute type
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class SequenceTest extends GroovyTestCase {
+
+    public void testConstruction() {
+        Sequence sequence = new Sequence(String.class);
+        sequence.add("James");
+        sequence.add("Bob");
+
+        assertEquals("Size", 2, sequence.size());
+        assertEquals("Element", "James", sequence.get(0));
+        assertEquals("Element", "Bob", sequence.get(1));
+
+        // now lets try some methods on each item in the list
+        List answer = (List) InvokerHelper.invokeMethod(sequence, "startsWith", new Object[]{"Ja"});
+        assertArrayEquals(new Object[]{Boolean.TRUE, Boolean.FALSE}, answer.toArray());
+
+        answer = (List) InvokerHelper.invokeMethod(sequence, "length", null);
+        assertArrayEquals(new Object[]{new Integer(5), new Integer(3)}, answer.toArray());
+    }
+
+    public void testAddingWrongTypeFails() {
+        try {
+            Sequence sequence = new Sequence(String.class);
+            sequence.add(new Integer(5));
+
+            fail("Should have thrown exception");
+        }
+        catch (IllegalArgumentException e) {
+            System.out.println("Caught: " + e);
+        }
+    }
+
+    public void testAddingNullFails() {
+        try {
+            Sequence sequence = new Sequence(String.class);
+            sequence.add(null);
+
+            fail("Should have thrown exception");
+        }
+        catch (NullPointerException e) {
+            System.out.println("Caught: " + e);
+        }
+    }
+
+}
diff --git a/groovy/src/test/groovy/lang/ShortRangeTest.java b/groovy/src/test/groovy/lang/ShortRangeTest.java
new file mode 100644
index 0000000..ed7d36b
--- /dev/null
+++ b/groovy/src/test/groovy/lang/ShortRangeTest.java
@@ -0,0 +1,70 @@
+/*
+ $Id: ShortRangeTest.java,v 1.1 2006/11/13 10:23:58 edwin Exp edwin $
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+
+/**
+ * Tests {@link ObjectRange}s of {@link Short}s.
+ *
+ * @author $Author$
+ * @version $Revision$
+ */
+public class ShortRangeTest extends NumberRangeTest {
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Range createRange(int from, int to) {
+        return new ObjectRange(new Short((short) from), new Short((short) to));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected Comparable createValue(int value) {
+        return new Integer(value);
+    }
+}
diff --git a/groovy/src/test/groovy/lang/TupleTest.java b/groovy/src/test/groovy/lang/TupleTest.java
new file mode 100644
index 0000000..a8cc48c
--- /dev/null
+++ b/groovy/src/test/groovy/lang/TupleTest.java
@@ -0,0 +1,114 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.lang;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+
+/**
+ * @author James Strachan
+ * @version $Revision$
+ */
+public class TupleTest extends TestCase {
+
+    final Object[] data = {"a", "b", "c"};
+    final Tuple t = new Tuple(data);
+
+    public void testSize() {
+        assertEquals("Size of " + t, 3, t.size());
+
+        assertEquals("get(0)", "a", t.get(0));
+        assertEquals("get(1)", "b", t.get(1));
+    }
+
+    public void testGetOutOfTuple() {
+        try {
+            t.get(-1);
+            fail("Should have thrown IndexOut");
+        }
+        catch (IndexOutOfBoundsException e) {
+            // worked
+        }
+        try {
+            t.get(10);
+            fail("Should have thrown IndexOut");
+        }
+        catch (IndexOutOfBoundsException e) {
+            // worked
+        }
+
+    }
+
+    public void testContains() {
+        assertTrue("contains a", t.contains("a"));
+        assertTrue("contains b", t.contains("b"));
+    }
+
+    public void testSubList() {
+        List s = t.subList(1, 2);
+
+        assertTrue("is a Tuple", s instanceof Tuple);
+
+        assertEquals("size", 1, s.size());
+    }
+
+    public void testHashCodeAndEquals() {
+        Tuple a = new Tuple(new Object[]{"a", "b", "c"});
+        Tuple b = new Tuple(new Object[]{"a", "b", "c"});
+        Tuple c = new Tuple(new Object[]{"d", "b", "c"});
+
+        assertEquals("hashcode", a.hashCode(), b.hashCode());
+        assertTrue("hashcode", a.hashCode() != c.hashCode());
+
+        assertEquals("a and b", a, b);
+        assertFalse("a != c", a.equals(c));
+    }
+
+    public void testIterator() {
+    }
+
+}
diff --git a/groovy/src/test/groovy/mock/MockTest.groovy b/groovy/src/test/groovy/mock/MockTest.groovy
new file mode 100644
index 0000000..59b8d8a
--- /dev/null
+++ b/groovy/src/test/groovy/mock/MockTest.groovy
@@ -0,0 +1,95 @@
+package groovy.mock
+
+import groovy.mock.GroovyMock
+import junit.framework.AssertionFailedError
+
+class MockTest extends GroovyTestCase {
+
+    def mock
+
+    void setUp() {
+        mock = GroovyMock.newInstance()
+    }
+
+    void testASimpleExpectationCanBeSetAndMet() {
+        // expectation
+        mock.doSomething("hello")
+
+        // execute
+        mock.instance.doSomething("hello")
+
+        // verify
+        mock.verify()
+    }
+
+    void testASimpleExpectationCanBeSetAndFailed() {
+        // expectation
+        mock.doSomething("hello")
+
+        // execute
+        try {
+            mock.instance.doSomething("goodbye")
+            fail("expected exception")
+        }
+        catch (RuntimeException goodException) {
+        }
+
+    }
+
+    void testASimpleExpectationCanBeSetButNeverCalledSoVerifyFails() {
+        // expectation
+        mock.doSomething("hello")
+
+        // execute
+        // don't call it
+
+        // verify
+        try {
+            mock.verify()
+            fail("should not have verified")
+        }
+        catch (AssertionFailedError goodException) {
+        }
+    }
+
+    void testAnExpectationWithAClosureGivesErrorIFCalledAndClosureFails() {
+        mock.doSomething( {arg -> assert arg=="poo" } )
+
+        // verify
+        try {
+            mock.instance.doSomething("hello")
+            fail("Expected verify to fail");
+        }
+        catch (RuntimeException ex) {
+            //expected
+        }
+    }
+
+    /*
+     * was GROOVY-76
+     */
+    void testAnExpectationwithAClosurePassesIfClosurePasses() {
+        mock.doSomething {arg -> assert arg=="hello" }
+
+        // execute
+        mock.instance.doSomething("hello")
+
+        //verify
+        mock.verify()
+    }
+
+    void testAnExpectationWithAClosureGivesErrorIFNotCalled() {
+        mock.doSomething( {arg -> assert arg=="poo" } )
+        // verify
+        try {
+            mock.verify()
+            fail("Expected verify to fail");
+        }
+        catch (AssertionFailedError ex) {
+            //expected
+        }
+    }
+
+}
+
+
diff --git a/groovy/src/test/groovy/mock/example/CheeseSlicer.groovy b/groovy/src/test/groovy/mock/example/CheeseSlicer.groovy
new file mode 100644
index 0000000..528e348
--- /dev/null
+++ b/groovy/src/test/groovy/mock/example/CheeseSlicer.groovy
@@ -0,0 +1,13 @@
+package groovy.mock.example
+
+class CheeseSlicer {
+
+    void slice(String name) {
+        throw new RuntimeException('whatever nasty behavior that needs to be mocked...')
+    }
+
+    void coffeeBreak(String name) {
+        // dum didum didum *slurp*, *spill*
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/mock/example/SandwichMaker.groovy b/groovy/src/test/groovy/mock/example/SandwichMaker.groovy
new file mode 100644
index 0000000..1ce5c93
--- /dev/null
+++ b/groovy/src/test/groovy/mock/example/SandwichMaker.groovy
@@ -0,0 +1,11 @@
+package groovy.mock.example
+
+class SandwichMaker {
+
+    def cheeseSlicer = new CheeseSlicer()
+
+    void makeFattySandwich() {
+        cheeseSlicer.slice("cheddar")
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/mock/example/SandwichMakerTest.groovy b/groovy/src/test/groovy/mock/example/SandwichMakerTest.groovy
new file mode 100644
index 0000000..fc0ae8f
--- /dev/null
+++ b/groovy/src/test/groovy/mock/example/SandwichMakerTest.groovy
@@ -0,0 +1,23 @@
+package groovy.mock.example
+
+import groovy.mock.interceptor.MockFor
+
+class SandwichMakerTest extends GroovyTestCase {
+
+    void testStuff(){
+
+        def mocker = new MockFor(CheeseSlicer.class)
+
+        mocker.demand.slice { name ->
+            assert name.startsWith("ch") 
+        }
+
+        def sandwichMaker = new SandwichMaker()
+
+        mocker.use(sandwichMaker.cheeseSlicer) { // todo: should also work without giving the object!
+            sandwichMaker.makeFattySandwich()
+        }
+
+    }
+
+}
diff --git a/groovy/src/test/groovy/mock/interceptor/Caller.groovy b/groovy/src/test/groovy/mock/interceptor/Caller.groovy
new file mode 100644
index 0000000..ae9b94f
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/Caller.groovy
@@ -0,0 +1,42 @@
+package groovy.mock.interceptor
+
+/**
+    Helper class for testing.
+    @author Dierk Koenig
+*/
+
+class Caller {
+    int collaborateOne() {
+        return new Collaborator().one()
+    }
+    int collaborateOne(int arg) {
+        return new Collaborator().one( arg )
+    }
+    int collaborateOne(int one, two) {
+        return new Collaborator().one( one, two )
+    }
+    int collaborateTwo() {
+        return new Collaborator().two()
+    }
+    String collaborateJava() {
+        return 'whatever'.toString()
+    }
+
+    String callFoo1() {
+        return new Collaborator().foo
+    }
+    String callFoo2() {
+        return new Collaborator().foo
+    }
+
+    void setBar1() {
+        new Collaborator().bar = "bar1"
+        return
+    }
+
+    void setBar2() {
+        new Collaborator().setBar("bar2")
+        return
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/mock/interceptor/Collaborator.groovy b/groovy/src/test/groovy/mock/interceptor/Collaborator.groovy
new file mode 100644
index 0000000..cba77f3
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/Collaborator.groovy
@@ -0,0 +1,25 @@
+package groovy.mock.interceptor
+
+/**
+    Helper class for testing.
+    @author Dierk Koenig
+*/
+
+class Collaborator {
+
+    String foo = "bar"
+    String bar = "foo"
+    
+    def one() {
+        throw new RuntimeException('Never reach here. Should have been mocked.')
+    }
+    def one(int arg) {
+        throw new RuntimeException('Never reach here. Should have been mocked.')
+    }
+    def one(int one, int two) {
+        throw new RuntimeException('Never reach here. Should have been mocked.')
+    }
+    def two() {
+        throw new RuntimeException('Never reach here. Should have been mocked.')
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/mock/interceptor/IteratorCounter.java b/groovy/src/test/groovy/mock/interceptor/IteratorCounter.java
new file mode 100644
index 0000000..dd2a5c7
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/IteratorCounter.java
@@ -0,0 +1,9 @@
+package groovy.mock.interceptor;

+

+public class IteratorCounter {

+    public int count(java.util.Iterator it) {

+        int count = 0;

+        while (it.hasNext()) count++;

+        return count;

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/mock/interceptor/MockCallSequenceTest.groovy b/groovy/src/test/groovy/mock/interceptor/MockCallSequenceTest.groovy
new file mode 100644
index 0000000..8a3d97c
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/MockCallSequenceTest.groovy
@@ -0,0 +1,173 @@
+package groovy.mock.interceptor
+
+import junit.framework.AssertionFailedError
+
+/*
+    Testing Groovy Mock support for multiple calls to the Collaborator with
+    demanding one or two methods multiple and and various ranges.
+    @author Dierk Koenig
+*/
+
+class MockCallSequenceTest extends GroovyTestCase {
+
+    MockFor mocker
+
+    void setUp() {
+        mocker = new MockFor(Collaborator.class)
+    }
+
+    void testUndemandedCallFailsEarly() {
+        // no demand here
+        mocker.use {
+            def caller = new Caller()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+
+    void testOneDemandedTwoCalledFailsEarly() {
+        mocker.demand.one { 1 }
+        mocker.use {
+            def caller = new Caller()
+            caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testOneDemandedDefaultRange() {
+        mocker.demand.one(1..1) { 1 }
+        mocker.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+        }
+    }
+    void testOneDemandedExactRange() {
+        mocker.demand.one(2..2) { 1 }
+        mocker.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testOneDemandedRealRange() {
+        mocker.demand.one(1..2) { 1 }
+        mocker.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testOneDemandedOptionalRange() {
+        mocker.demand.one(0..2) { 1 }
+        mocker.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testTwoDemandedNoRange() {
+        mocker.demand.one() { 1 }
+        mocker.demand.two() { 2 }
+        mocker.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+        }
+    }
+    void testTwoDemandedFirstRangeExploited() {
+        mocker.demand.one(1..2) { 1 }
+        mocker.demand.two() { 2 }
+        mocker.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+        }
+    }
+    void testTwoDemandedFirstRangeNotExploited() {
+        mocker.demand.one(1..2) { 1 }
+        mocker.demand.two() { 2 }
+        mocker.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+        }
+    }
+    void testTwoDemandedFirstOptionalOmitted() {
+        mocker.demand.one(0..2) { 1 }
+        mocker.demand.two() { 2 }
+        mocker.use {
+            def caller = new Caller()
+            assertEquals 2, caller.collaborateTwo()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+        }
+    }
+    void testMixedDemandedMinimum() {
+        mocker.demand.one(0..1) { 1 }
+        mocker.demand.two() { 2 }
+        mocker.demand.one() { 1 }
+        mocker.demand.two(0..2) { 2 }
+        mocker.demand.one() { 1 }
+        mocker.use {
+            def caller = new Caller()
+            assertEquals 2, caller.collaborateTwo()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testMixedDemandedMaximum() {
+        mocker.demand.one(0..1) { 1 }
+        mocker.demand.two() { 2 }
+        mocker.demand.one() { 1 }
+        mocker.demand.two(0..2) { 2 }
+        mocker.demand.one() { 1 }
+        mocker.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            assertEquals 1, caller.collaborateOne()
+
+            // 2.times( assertEquals(2, caller.collaborateTwo()) ) // todo: why this not possible?
+            assertEquals 2, caller.collaborateTwo()
+            assertEquals 2, caller.collaborateTwo()
+
+            assertEquals 1, caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testMixedDemandedOutOfSequenceFailsEarly() {
+        mocker.demand.one(0..1) { 1 }
+        mocker.demand.two() { 2 }
+        mocker.demand.one() { 1 }
+        mocker.demand.two(0..2) { 2 }
+        mocker.demand.one() { 1 }
+        shouldFail(AssertionFailedError.class) { // fails on verify
+            mocker.use {
+                def caller = new Caller()
+                assertEquals 1, caller.collaborateOne()
+                assertEquals 2, caller.collaborateTwo()
+                shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+            }
+        }
+    }
+    void testRangeDemandedButNotExploitedFailsOnVerify() {
+        mocker.demand.one(2..4) { 1 }
+        shouldFail(AssertionFailedError.class) { // fails on verify
+            mocker.use {
+                def caller = new Caller()
+                assertEquals 1, caller.collaborateOne()
+            }
+        }
+    }
+    void testReversedRangesNotAllowed() {
+        shouldFail(IllegalArgumentException.class) { mocker.demand.one(1..0) { 1 } }
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/mock/interceptor/MockForJavaTest.groovy b/groovy/src/test/groovy/mock/interceptor/MockForJavaTest.groovy
new file mode 100644
index 0000000..1ee91a8
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/MockForJavaTest.groovy
@@ -0,0 +1,37 @@
+package groovy.mock.interceptor

+

+class MockForJavaTest extends GroovyTestCase {

+    void testIterator() {

+        def iteratorContext = new MockFor(Iterator)

+        iteratorContext.demand.hasNext() { true }

+        iteratorContext.demand.hasNext() { true }

+        iteratorContext.demand.hasNext() { false }

+        def iterator = iteratorContext.proxyDelegateInstance()

+        iteratorContext.demand.next() { "foo" }

+        def iterator2 = iteratorContext.proxyDelegateInstance()

+

+        assert new IteratorCounter().count(iterator2) == 2

+        assert iterator2.next() == "foo"

+        iteratorContext.verify(iterator2)

+

+        assert new IteratorCounter().count(iterator) == 2

+        iteratorContext.verify(iterator)

+

+        iteratorContext = new MockFor(Iterator)

+        iteratorContext.demand.hasNext(7..7) { true }

+        iteratorContext.demand.hasNext() { false }

+        def iterator3 = iteratorContext.proxyDelegateInstance()

+        assert new IteratorCounter().count(iterator3) == 7

+        iteratorContext.verify(iterator3)

+    }

+

+    void testString() {

+        def iteratorContext = new MockFor(String)

+        iteratorContext.demand.endsWith(2..2) { String arg -> arg == "foo" }

+        def s = iteratorContext.proxyDelegateInstance()

+        assert !s.endsWith("bar")

+        assert s.endsWith("foo")

+        iteratorContext.verify(s)

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/mock/interceptor/MockNestedCallTest.groovy b/groovy/src/test/groovy/mock/interceptor/MockNestedCallTest.groovy
new file mode 100644
index 0000000..11b9a3c
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/MockNestedCallTest.groovy
@@ -0,0 +1,27 @@
+package groovy.mock.interceptor

+

+class MockNestedCallTest extends GroovyTestCase {

+

+    void testRestore() {

+        def mockTail = new MockFor(Coin)

+        mockTail.demand.flip(0..9) {"tail"}

+

+        def mockHead = new MockFor(Coin)

+        mockHead.demand.flip(0..9) {"head"}

+

+        def c = new Coin()

+        assert c.flip() == "edge"

+        mockTail.use(c) {

+            assert c.flip() == "tail"

+            mockHead.use(c) {

+                assert c.flip() == "head"

+            }

+            assert c.flip() == "tail"

+        }

+        assert c.flip() == "edge"

+    }

+}

+

+class Coin {

+    def flip() { "edge" }

+}

diff --git a/groovy/src/test/groovy/mock/interceptor/MockSingleCallTest.groovy b/groovy/src/test/groovy/mock/interceptor/MockSingleCallTest.groovy
new file mode 100644
index 0000000..2496cb7
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/MockSingleCallTest.groovy
@@ -0,0 +1,128 @@
+package groovy.mock.interceptor
+
+import junit.framework.AssertionFailedError
+
+/**
+    Testing Groovy Mock support for single calls to the Collaborator with
+    no, one, multiple, or arbitrary arguments, exceptions and failures.
+    @author Dierk Koenig
+*/
+
+class MockSingleCallTest extends GroovyTestCase {
+
+    MockFor mocker
+
+    void setUp() {
+        mocker = new MockFor(Collaborator.class)
+    }
+
+    void testMockGetter() {
+       mocker.demand.getFoo { "foo" }
+       mocker.demand.getFoo { "foobar" }
+       mocker.use {
+           assertEquals "foo", new Caller().callFoo1()
+           assertEquals "foobar", new Caller().callFoo2()       
+       }
+    }
+
+    void testMockSetter() {
+
+        def result = null
+        
+        mocker.demand.setBar { result = it }
+        mocker.demand.setBar { result = it }
+
+        mocker.use {
+            new Caller().setBar1()
+            assertEquals result, "bar1"
+            new Caller().setBar2()
+            assertEquals result, "bar2"
+
+        }
+    }
+    
+    void testSingleCallNoArgs() {
+        mocker.demand.one { 1 }
+        mocker.use {
+            assertEquals 1, new Caller().collaborateOne()
+        }
+    }
+    void testSingleCallOneArg() {
+        mocker.demand.one { arg -> return arg }
+        mocker.use {
+            assertEquals 2, new Caller().collaborateOne(2)
+        }
+    }
+    void testSingleCallTwoArgs() {
+        mocker.demand.one { one, two -> return one + two }
+        mocker.use {
+            assertEquals 2, new Caller().collaborateOne(1, 1)
+        }
+    }
+    void testNoSingleCallTwoArgsWhenNoArgDemanded() {
+        mocker.demand.one { 2 }
+        mocker.use {
+            shouldFail {
+                assertEquals 2, new Caller().collaborateOne(1, 1)
+            }
+        }
+    }
+    void testSingleCallTwoArgsWhenArbitraryArgsDemanded() {
+        mocker.demand.one { Object[] arg ->  2 }
+        mocker.use {
+            assertEquals 2, new Caller().collaborateOne(1, 1)
+        }
+    }
+    void testSingleCallTwoArgsWhenDefaultArgsDemanded() {
+        mocker.demand.one { one=null, two=null ->  2 }
+        mocker.use {
+            assertEquals 2, new Caller().collaborateOne(1, 1)
+        }
+    }
+    void testVerifyFailsIfOneDemandedButNoneExcecuted() {
+        mocker.demand.one { 1 }
+        def msg = shouldFail(AssertionFailedError.class) {
+            mocker.use {
+                // no call
+            }
+        }
+        /* This is a fragile test smell! We've changed the message text of the exception, and this test fails due to this assert.
+           If we think this assert is important, we should extend AssertionFailedError and add the properties
+           expectedRange and callCount to it. But I think the test is good enough with just checking for the thrown
+           exception. */
+        // assert msg =~ /0.*1..1.*never called/ 
+    }
+    void testFirstOptionalOmitted() {
+        mocker.demand.one(0..1) { 1 }
+        mocker.use {
+            def caller = new Caller()
+        }
+        // Getting here means no exception, which is what we want to test.  (Fix for GROOVY-2309)
+    }
+    void testSingleCallExceptionDemanded() {
+        mocker.demand.one { throw new IllegalArgumentException() }
+        mocker.use {
+//            shouldFail(IllegalArgumentException.class) {
+            shouldFail { // todo: should fail with IllegalArgumentException instead of GroovyRuntimeException
+                new Caller().collaborateOne()
+            }
+        }
+    }
+    void testSingleCallFailDemanded() {
+        mocker.demand.one { fail 'just kidding' }
+        mocker.use {
+            shouldFail() { new Caller().collaborateOne() }
+        }
+    }
+    void testJavaCall() {
+        mocker = new MockFor(String.class)
+        mocker.demand.toString { 'groovy' }
+        mocker.use {
+            assertEquals 'groovy', new Caller().collaborateJava()
+        }
+    }
+
+}
+
+
+
diff --git a/groovy/src/test/groovy/mock/interceptor/MockWithZeroRangeTest.groovy b/groovy/src/test/groovy/mock/interceptor/MockWithZeroRangeTest.groovy
new file mode 100644
index 0000000..e98c897
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/MockWithZeroRangeTest.groovy
@@ -0,0 +1,32 @@
+package groovy.mock.interceptor
+
+import groovy.mock.interceptor.MockFor
+import junit.framework.AssertionFailedError
+
+class MockForWithZeroRangeTest extends GroovyTestCase {
+    void testMockWithZeroRangeDemandAndNoCall() {
+        MockFor mockForFoo = new MockFor(Foo)
+        mockForFoo.demand.createBar(0..0) {}
+        mockForFoo.use {
+            println 'Foo is not called'
+        }
+        // We should get here and the test should pass.
+    }
+
+    void testMockWithZeroRangeDemandAndOneCall() {
+        MockFor mockForFoo = new MockFor(Foo)
+        mockForFoo.demand.createBar(0..0) {}
+        shouldFail(AssertionFailedError) {
+            mockForFoo.use {
+                new Foo().createBar()
+            }
+        }
+    }
+}
+
+
+class Foo {
+    def createBar() {
+        println 'bar'
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/mock/interceptor/StubCallSequenceTest.groovy b/groovy/src/test/groovy/mock/interceptor/StubCallSequenceTest.groovy
new file mode 100644
index 0000000..1f71fcf
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/StubCallSequenceTest.groovy
@@ -0,0 +1,198 @@
+package groovy.mock.interceptor
+
+import junit.framework.AssertionFailedError
+
+/**
+    Testing Groovy Stub support for multiple calls to the Collaborator with
+    demanding one or two methods multiple and and various ranges.
+    @author Dierk Koenig
+*/
+class StubCallSequenceTest extends GroovyTestCase {
+
+    StubFor stub
+
+    void setUp() {
+        stub = new StubFor(Collaborator.class)
+    }
+
+    void testUndemandedCallFailsEarly() {
+        // no demand here
+        stub.use {
+            def caller = new Caller()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+
+    void testOneDemandedTwoCalledFailsEarly() {
+        stub.demand.one { 1 }
+        stub.use {
+            def caller = new Caller()
+            caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testOneDemandedDefaultRange() {
+        stub.demand.one(1..1) { 1 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+        }
+    }
+    void testOneDemandedExactRange() {
+        stub.demand.one(2..2) { 1 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testOneDemandedRealRange() {
+        stub.demand.one(1..2) { 1 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testOneDemandedOptionalRange() {
+        stub.demand.one(0..2) { 1 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testTwoDemandedNoRange() {
+        stub.demand.one() { 1 }
+        stub.demand.two() { 2 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+        }
+    }
+    void testTwoDemandedFirstRangeExploited() {
+        stub.demand.one(1..2) { 1 }
+        stub.demand.two() { 2 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+        }
+    }
+    void testTwoDemandedFirstRangeNotExploited() {
+        stub.demand.one(1..2) { 1 }
+        stub.demand.two() { 2 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+        }
+    }
+    void testTwoDemandedFirstOptionalOmitted() {
+        stub.demand.one(0..2) { 1 }
+        stub.demand.two() { 2 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 2, caller.collaborateTwo()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+        }
+    }
+    void testMixedDemandedMinimumOutOfSequence() {
+        stub.demand.one(0..1) { 1 }
+        stub.demand.two() { 2 }
+        stub.demand.one() { 1 }
+        stub.demand.two(0..2) { 2 }
+        stub.demand.one() { 1 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 1, caller.collaborateOne()
+
+            assertEquals 2, caller.collaborateTwo()
+            assertEquals 2, caller.collaborateTwo()
+        }
+        stub.expect.verify()
+    }
+    void testMixedDemandedMaximum() {
+        stub.demand.one(0..1) { 1 }
+        stub.demand.two() { 2 }
+        stub.demand.one() { 1 }
+        stub.demand.two(0..2) { 2 }
+        stub.demand.one() { 1 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            assertEquals 1, caller.collaborateOne()
+
+            // 2.times( assertEquals(2, caller.collaborateTwo()) ) // todo: why this not possible?
+            assertEquals 2, caller.collaborateTwo()
+            assertEquals 2, caller.collaborateTwo()
+
+            assertEquals 1, caller.collaborateOne()
+            shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+    }
+    void testMixedDemandedOutOfSequenceFailsEarly() {
+        stub.demand.one(0..1) { 1 }
+        stub.demand.two() { 2 }
+        stub.demand.one() { 1 }
+        stub.demand.two(0..2) { 2 }
+        stub.demand.one() { 1 }
+        shouldFail(AssertionFailedError.class) { // fails on verify
+            stub.use {
+                def caller = new Caller()
+                assertEquals 1, caller.collaborateOne()
+                assertEquals 2, caller.collaborateTwo()
+                shouldFail(AssertionFailedError.class) { caller.collaborateTwo() }
+            }
+        }
+    }
+    void testRangeDemandedOutOfSequenceCalls() {
+        stub.demand.one(0..3) { 1 }
+        stub.demand.two(0..3) { 2 }
+        stub.use {
+            def caller = new Caller()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            assertEquals 1, caller.collaborateOne()
+            assertEquals 2, caller.collaborateTwo()
+            shouldFail(AssertionFailedError.class) { caller.collaborateOne() }
+        }
+        stub.expect.verify()
+    }
+
+    void testUnreachedDemandFailsOnVerify() {
+        stub.demand.one { 1 }
+        // nothing used
+        shouldFail(AssertionFailedError.class) { stub.expect.verify() }
+    }
+
+
+    void testRangeDemandedButNotExploitedFailsOnVerify() {
+        stub.demand.one(2..4) { 1 }
+        shouldFail(AssertionFailedError.class) { // fails on verify
+            stub.use {
+                def caller = new Caller()
+                assertEquals 1, caller.collaborateOne()
+            }
+            stub.expect.verify()
+        }
+    }
+    void testReversedRangesNotAllowed() {
+        shouldFail(IllegalArgumentException.class) { stub.demand.one(1..0) { 1 } }
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/mock/interceptor/StubForJavaTest.groovy b/groovy/src/test/groovy/mock/interceptor/StubForJavaTest.groovy
new file mode 100644
index 0000000..5c39660
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/StubForJavaTest.groovy
@@ -0,0 +1,28 @@
+package groovy.mock.interceptor

+

+class StubForJavaTest extends GroovyTestCase {

+    void testIterator() {

+        def iteratorContext = new StubFor(Iterator)

+        iteratorContext.demand.hasNext() { true }

+        iteratorContext.demand.hasNext() { false }

+        iteratorContext.demand.hasNext() { true }

+        iteratorContext.demand.hasNext() { false }

+        def iterator = iteratorContext.proxyDelegateInstance()

+        def counter = new IteratorCounter()

+        assert counter.count(iterator) == 1

+        assert counter.count(iterator) == 1

+    }

+

+    void testString() {

+        def iteratorContext = new StubFor(String)

+        iteratorContext.demand.startsWith(2..2) { String arg -> arg == "wiz" }

+        iteratorContext.demand.endsWith(2..2) { String arg -> arg == "foo" }

+        def s = iteratorContext.proxyDelegateInstance()

+        assert !s.endsWith("bar")

+        assert s.endsWith("foo")

+        assert !s.startsWith("bar")

+        assert s.startsWith("wiz")

+        iteratorContext.verify(s)

+    }

+

+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/mock/interceptor/StubSingleCallTest.groovy b/groovy/src/test/groovy/mock/interceptor/StubSingleCallTest.groovy
new file mode 100644
index 0000000..397ba88
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/StubSingleCallTest.groovy
@@ -0,0 +1,27 @@
+package groovy.mock.interceptor
+
+/**
+    Testing Groovy Stub support for single calls to the Collaborator
+    @author Dierk Koenig
+*/
+
+class StubSingleCallTest extends GroovyTestCase {
+
+    StubFor stub
+
+    void setUp() {
+    	stub = new StubFor(Collaborator.class)
+    }
+
+    void testFirstOptionalOmitted() {
+    	stub.demand.one(0..1) { 1 }
+    	stub.use {
+            def caller = new Caller()
+        }
+    	stub.expect.verify()
+        // Getting here means no exception, which is what we want to test.  (Fix for GROOVY-2309)
+    }
+}
+
+
+
diff --git a/groovy/src/test/groovy/mock/interceptor/StubTest.groovy b/groovy/src/test/groovy/mock/interceptor/StubTest.groovy
new file mode 100644
index 0000000..fb82947
--- /dev/null
+++ b/groovy/src/test/groovy/mock/interceptor/StubTest.groovy
@@ -0,0 +1,76 @@
+package groovy.mock.interceptor
+
+class StubTest extends GroovyTestCase {
+
+   void testBehaviorWithInstanceCreatedOutsideUseClosure() {
+      def speakerStub = new StubFor(Speaker)
+      speakerStub.demand.startLecture() { "Intercepted!" }
+
+      def speaker1 = new Speaker()
+      assert speaker1.startLecture() == "Starting..."
+
+      speakerStub.use {
+         def speaker2 = new Speaker()
+         assert speaker2.startLecture() == "Intercepted!"
+      }
+
+      speakerStub.demand.startLecture() { "Intercepted!" }
+      speakerStub.use (speaker1) {
+         assert speaker1.startLecture() == "Intercepted!"
+      }
+      assert speaker1.startLecture() == "Starting..."
+   }
+
+   void testWIthOGBOutsideUse() {
+      def ogb = new ObjectGraphBuilder( classNameResolver: 'groovy.mock.interceptor' )
+      def stub = new StubFor( Company )
+      stub.demand.payroll(0..2) { 500 }
+      def acme = ogb.company {
+         employee( name: 'Duke', salary: 42 )
+      }
+      stub.use (acme) {
+         assert acme.payroll() == 500
+         assert acme.payroll() == 500
+      }
+   }
+
+   void testWIthOGBInsideUse() {
+      def ogb = new ObjectGraphBuilder( classNameResolver: 'groovy.mock.interceptor' )
+      def stub = new StubFor( ObjectGraphBuilder )
+      stub.use {
+         def acme = ogb.company {
+            // blows after the next line because property handling
+            // on Company was not demanded
+            employee( name: 'Duke', salary: 42 )
+         }
+
+         def stub2 = new StubFor( Company )
+         stub2.demand.payroll(0..2) { 500 }
+         stub2.use (acme) {
+           assert acme.payroll() == 500
+         }
+      }
+   }
+
+}
+
+class Speaker {
+   String name
+   def startLecture() { "Starting..." }
+}
+
+
+class Company {
+   String name
+   List employees = []
+   def payroll() {
+      def total = 0
+      employees.each { total += it.salary }
+      total
+   }
+}
+
+class Employee {
+   String name
+   Number salary = 0
+}
diff --git a/groovy/src/test/groovy/model/TableModelTest.groovy b/groovy/src/test/groovy/model/TableModelTest.groovy
new file mode 100644
index 0000000..1c4ef67
--- /dev/null
+++ b/groovy/src/test/groovy/model/TableModelTest.groovy
@@ -0,0 +1,37 @@
+package groovy.model
+
+class TableModelTest extends GroovyTestCase {
+    
+    void testTableModel() {
+        def list = [ ['name':'James', 'location':'London'], ['name':'Bob', 'location':'Atlanta']]
+        
+        def listModel = new ValueHolder(list)
+        
+        def model = new DefaultTableModel(listModel)
+        model.addColumn(new DefaultTableColumn("Name", new PropertyModel(model.rowModel, "name")))
+        model.addColumn(new DefaultTableColumn("Location", new PropertyModel(model.rowModel, "location")))
+        
+        assert model.rowCount == 2
+        assert model.columnCount == 2
+        assertValueAt(model, 0, 0, 'James')
+        assertValueAt(model, 0, 1, 'London')
+        assertValueAt(model, 1, 0, 'Bob')
+        assertValueAt(model, 1, 1, 'Atlanta')
+        
+        assert model.getColumnName(0) == 'Name'
+        assert model.getColumnName(1) == 'Location'
+        
+        // let's set some values
+        model.setValueAt('Antigua', 0, 1)
+        assertValueAt(model, 0, 1, 'Antigua')
+        
+        // let's check the real model changed too
+        def james = list.get(0)
+        assert james.location == 'Antigua'
+    }
+    
+    protected void assertValueAt(model, row, col, expected) {
+        def value = model.getValueAt(row, col)
+        assert value == expected , "for row " + row + " col " + col
+    }
+}
diff --git a/groovy/src/test/groovy/operator/BigDecimalOperatorsTest.groovy b/groovy/src/test/groovy/operator/BigDecimalOperatorsTest.groovy
new file mode 100644
index 0000000..5bc5b33
--- /dev/null
+++ b/groovy/src/test/groovy/operator/BigDecimalOperatorsTest.groovy
@@ -0,0 +1,128 @@
+package groovy.operator
+
+import java.math.BigDecimal;
+
+class BigDecimalOperatorsTest extends GroovyTestCase {
+
+    def x, y
+
+    void testPlus() {
+
+        x = 0.1 + 1.1
+        assert x instanceof BigDecimal;
+        assert x == 1.2
+
+        x = 3 + 2.2
+        assert x == 5.2
+        assert x instanceof BigDecimal;
+
+        x = 2.2 + 4
+        assert x instanceof BigDecimal;
+        assert x == 6.2
+
+        y = x + 1
+        assert y instanceof BigDecimal;
+        assert y == 7.2
+
+        def z = y + x + 1 + 2
+        assert z instanceof BigDecimal;
+        assert z == 16.4
+    }
+
+    void testMinus() {
+        x = 1.1-0.01
+        assert x == 1.09
+
+        x = 6 - 2.2
+        assert x == 3.8
+
+        x = 5.8 - 2
+        assert x == 3.8
+
+        y = x - 1
+        assert y == 2.8
+    }
+
+    void testMultiply() {
+        x = 3 * 2.0
+        assert x == 6.0
+
+        x = 3.0 * 2
+        assert x == 6.0
+
+        x = 3.0 * 2.0
+        assert x == 6.0
+
+        y = x * 2
+        assert y == 12.0
+
+        y = 11 * 3.333
+        assert y == 36.663 , "y = " + y
+
+        y = 3.333 * 11
+        assert y == 36.663 , "y = " + y
+    }
+
+    void testDivide() {
+        x = 80.0 / 4
+        assert x == 20.0 , "x = " + x
+
+        x = 80 / 4.0
+        assert x == 20.0 , "x = " + x
+
+        y = x / 2
+        assert y == 10.0 , "y = " + y
+        assert y == 10 , "y = " + y
+
+        y = 34 / 3.000
+        assert y == 11.3333333333 , "y = " + y
+
+        y = 34.00000000000 / 3
+        assert y == 11.33333333333 , "y = " + y
+    }
+    
+    BigDecimal echoX ( BigDecimal x, BigDecimal y) {x}
+    
+    // test for Groovy-1250
+    void testBigDecimalCoerce() {
+        assert echoX(9.95, 1.0) == echoX(9.95, 1)
+    }
+    
+    void testAssign() {
+        BigDecimal foo
+        foo = (byte) 20
+        assert foo.class == BigDecimal.class
+        assert foo == 20
+
+        foo = (short) 20
+        assert foo.class == BigDecimal.class
+        assert foo == 20
+
+        foo = (int) 20
+        assert foo.class == BigDecimal.class
+        assert foo == 20
+
+        foo = (long) 20
+        assert foo.class == BigDecimal.class
+        assert foo == 20
+
+        foo = (float) 0.5f
+        assert foo.class == BigDecimal.class
+        assert foo == 0.5
+
+        foo = (double) 0.5d
+        assert foo.class == BigDecimal.class
+        assert foo == 0.5
+        
+        foo = 10G
+        assert foo.class == BigDecimal.class
+        assert foo == 10
+        
+        double d = 1000
+        d *= d
+        d *= d
+        d *= d
+        assert (long)d != d
+		assert (BigDecimal) d == d
+    }
+}
diff --git a/groovy/src/test/groovy/operator/BigIntegerOperationsTest.groovy b/groovy/src/test/groovy/operator/BigIntegerOperationsTest.groovy
new file mode 100644
index 0000000..713c087
--- /dev/null
+++ b/groovy/src/test/groovy/operator/BigIntegerOperationsTest.groovy
@@ -0,0 +1,41 @@
+package groovy.operator
+
+class BigIntegerOperationsTest extends GroovyTestCase {
+    void testAssign() {
+        BigInteger foo
+        foo = (byte) 20
+        assert foo.class == BigInteger
+        assert foo == 20
+
+        foo = (short) 20
+        assert foo.class == BigInteger
+        assert foo == 20
+
+        foo = (int) 20
+        assert foo.class == BigInteger
+        assert foo == 20
+
+        foo = (long) 20
+        assert foo.class == BigInteger
+        assert foo == 20
+
+        foo = (float) 0.5f
+        assert foo.class == BigInteger
+        assert foo == 0
+
+        foo = (double) 0.5d
+        assert foo.class == BigInteger
+        assert foo == 0
+        
+        foo = 10.5G
+        assert foo.class == BigInteger
+        assert foo == 10
+        
+        double d = 1000
+        d *= d
+        d *= d
+        d *= d
+        assert (long)d != d
+		assert (BigInteger) d == d
+    }
+}
diff --git a/groovy/src/test/groovy/operator/BitwiseOperatorsTest.groovy b/groovy/src/test/groovy/operator/BitwiseOperatorsTest.groovy
new file mode 100644
index 0000000..344ce42
--- /dev/null
+++ b/groovy/src/test/groovy/operator/BitwiseOperatorsTest.groovy
@@ -0,0 +1,292 @@
+package groovy.operator
+
+/** 
+ * Test Bitwise Operations in Classic/New Groovy
+ * 
+ * @author Pilho Kim
+ * @version $Revision: 4996 $
+ */
+class BitwiseOperatorsTest extends GroovyTestCase {
+
+    void testBitwiseShift() {
+        def a = 4
+        def b = -4
+        assert a << 1 == 8
+        assert a << 2 == 16
+        assert a >> 1 == 2
+        assert a >> 2 == 1
+        assert a >>> 1 == 2
+        assert a >>> 2 == 1
+        assert b << 1 == -8
+        assert b << 2 == -16
+        assert b >> 1 == -2
+        assert b >> 2 == -1
+        assert b >>> 1 == 0x7FFFFFFE
+        assert b >>> 2 == 0x3FFFFFFF
+    }
+
+    void testBitwiseShiftEQUAL() {
+        def a = 4
+        a <<= 1
+        assert a == 8
+        a <<= 2
+        assert a == 32
+        a >>= 1
+        assert a == 16
+        a >>= 2
+        assert a == 4
+
+        def b = -4
+        b <<= 1
+        assert b == -8
+        b <<= 2
+        assert b == -32
+        b >>= 1
+        assert b == -16
+        b >>= 2
+        assert b == -4
+
+        b = -4
+        b >>>= 1
+        assert b == 0x7FFFFFFE
+        b = -8
+        b >>>= 2
+        assert b == 0x3FFFFFFE
+    }
+
+    void testBitwiseAnd() {
+
+        def a = 13
+        assert (a & 3) == 1 // 0x0000000D & 0x00000003
+        assert (a & 7) == 5 // 0x0000000D & 0x00000007
+        def b = -13
+        assert (b & 3) == 3 // 0xFFFFFFF3 & 0x00000003
+        assert (b & 7) == 3 // 0xFFFFFFF3 & 0x00000007
+    }
+
+    void testBitwiseAndOperatorPrecedence_FAILS() {if (notYetImplemented()) return
+        // Oprator Precedence Problem
+        // ^, &, | should be prior to ==, <, >, <=, >=
+        def a = 13
+        assert a & 3 == 1 // 0x0000000D & 0x00000003
+        assert a & 7 == 5 // 0x0000000D & 0x00000007
+        def b = -13
+        assert b & 3 == 3 // 0xFFFFFFF3 & 0x00000003
+        assert b & 7 == 3 // 0xFFFFFFF3 & 0x00000007
+    }
+
+    void testBitwiseAndEqual() {
+        def a = 13
+        a &= 3
+        assert a == 1 // 0x0000000D & 0x00000003
+        a &= 4
+        assert a == 0 // 0x00000001 & 0x00000004
+        def b = -13
+        b &= 3
+        assert b == 3 // 0xFFFFFFF3 & 0x00000003
+        b &= 7
+        assert b == 3 // 0x00000003 & 0x00000007
+    }
+
+    void testBitwiseOr() {
+        def a = 13
+        assert (a | 8) == 13 // 0x0000000D | 0x00000008
+        assert (a | 16) == 29 // 0x0000000D | 0x00000010
+        def b = -13
+        assert (b | 8) == -5 // 0xFFFFFFF3 | 0x00000008
+        assert (b | 16) == -13 // 0xFFFFFFF3 | 0x00000010
+    }
+
+    void testBitwiseOrOperatorPrecedence_FAILS() {if (notYetImplemented()) return
+        // Oprator Precedence Problem
+        // ^, &, | should be prior to ==, <, >, <=, >=
+        def a = 13
+        assert a | 8 == 13 // 0x0000000D | 0x00000008
+        assert a | 16 == 29 // 0x0000000D | 0x00000010
+        def b = -13
+        assert b | 8 == -5 // 0xFFFFFFF3 | 0x00000008
+        assert b | 16 == -13 // 0xFFFFFFF3 | 0x00000010
+    }
+
+    void testBitwiseOrEqual() {
+        def a = 13
+        a |= 2
+        assert a == 15 // 0x0000000D | 0x00000002
+        a |= 16
+        assert a == 31 // 0x0000000F | 0x0000001F
+        def b = -13
+        b |= 8
+        assert b == -5 // 0xFFFFFFF3 | 0x00000008
+        b |= 1
+        assert b == -5 // 0xFFFFFFFB | 0x00000001
+    }
+
+    void testBitwiseXor() {
+        def a = 13
+        assert (a ^ 10) == 7 // 0x0000000D ^ 0x0000000A = 0x000000007
+        assert (a ^ 15) == 2 // 0x0000000D ^ 0x0000000F = 0x000000002
+        def b = -13
+        assert (b ^ 10) == -7 // 0xFFFFFFF3 ^ 0x0000000A = 0xFFFFFFF9
+        assert (b ^ 15) == -4 // 0xFFFFFFF3 ^ 0x0000000F = 0xFFFFFFFC
+    }
+    void testBitwiseXorOperatorPrecedence_FAILS() {if (notYetImplemented()) return
+        // Oprator Precedence Problem
+        // ^, &, | should be prior to ==, <, >, <=, >=
+        def a = 13
+        assert a ^ 10 == 7 // 0x0000000D ^ 0x0000000A = 0x000000007
+        assert a ^ 15 == 2 // 0x0000000D ^ 0x0000000F = 0x000000002
+        def b = -13
+        assert b ^ 10 == -7 // 0xFFFFFFF3 ^ 0x0000000A = 0xFFFFFFF9
+        assert b ^ 15 == -4 // 0xFFFFFFF3 ^ 0x0000000F = 0xFFFFFFFC
+    }
+
+    void testBitwiseXorEqual() {
+        def a = 13
+        a ^= 8
+        assert a == 5 // 0x0000000D ^ 0x00000008 = 0x000000005
+        a ^= 16
+        assert a == 21 // 0x00000005 ^ 0x00000010 = 0x000000015
+        def b = -13
+        b ^= 8
+        assert b == -5 // 0xFFFFFFF3 ^ 0x00000008 = 0xFFFFFFFB
+        b ^= 16
+        assert b == -21 // 0xFFFFFFFB ^ 0x00000010 = 0xFFFFFFEB
+    }
+
+    void testBitwiseOrInClosure() {
+        def c1 = {x, y -> return x | y}
+        assert c1(14, 5) == 15 // 0x0000000E | 0x00000005 = 0x0000000F
+        assert c1(0x0D, 0xFE) == 255 // 0x0000000D | 0x000000FE = 0x000000FF
+
+        def c2 = {x, y -> return x | y}
+        assert c2(14, 5) == 15 // 0x0000000E | 0x00000005 = 0x0000000F
+        assert c2(0x0D, 0xFE) == 255 // 0x0000000D | 0x000000FE = 0x000000FF
+    }
+
+    void testAmbiguityOfBitwiseOr() {
+        def c1 = {x, y -> return x | y}
+        assert c1(14, 5) == 15 // 0x0000000E | 0x00000005 = 0x0000000F
+        assert c1(0x0D, 0xFE) == 255 // 0x0000000D | 0x000000FE = 0x000000FF
+
+        def c2 = {x, y -> return x | y}
+        assert c2(14, 5) == 15 // 0x0000000E | 0x00000005 = 0x0000000F
+        assert c2(0x0D, 0xFE) == 255 // 0x0000000D | 0x000000FE = 0x000000FF
+
+        def x = 3
+        def y = 5
+        c1 = {xx -> return y} // -> is a closure delimiter
+        c2 = {return x & y} // & is a bitAnd
+        def c3 = {return x ^ y} // & is a bitXor
+        def c11 = {
+            xx -> return y // -> is a closure delimiter
+        }
+        def c12 = {
+            return (x | y) // | is a bitOr
+        }
+        def c13 = {xx -> return y // -> is a closure delimiter
+        }
+        def c14 = {-> return x | y // last | is a bitOr
+        }
+
+        assert c1(null) == 5
+        assert c2() == 1
+        assert c3() == 6
+        assert c11(null) == 5
+        assert c12() == 7
+        assert c13(null) == 5
+        assert c14() == 7
+
+        x = 0x03
+
+        def d1 = {xx -> return xx} // -> is a closure delimiter
+        def d2 = {return x & x} // & is a bitAnd
+        def d3 = {return x ^ x} // & is a bitXor
+        def d11 = {
+            xx -> return xx // -> is a closure delimiter
+        }
+        def d12 = {
+            return (x | x) // | is a bitOr
+        }
+        def d13 = {xx -> return xx // -> is a closure delimiter
+        }
+        def d14 = {-> return x | x // last | is a bitOr
+        }
+        assert d1(0xF0) == 0xF0
+        assert d2(0xF0) == 0x03
+        assert d3(0xF0) == 0
+        assert d11(0xF0) == 0xF0
+        assert d12(0xF0) == 0x03
+        assert d13(0xF0) == 0xF0
+        assert d14() == 0x03
+    }
+
+    void testBitwiseNegation() {
+        assert ~1 == -2 // ~0x00000001 = 0xFFFFFFFE
+        assert ~-1 == 0 // ~0xFFFFFFFF = 0x00000000
+        assert ~~5 == 5 // ~~0x00000005 = ~0xFFFFFFFA = 0xFFFFFFF5
+        def a = 13
+        assert ~a == -14 // ~0x0000000D = 0xFFFFFFF2
+        assert ~~a == 13 // ~~0x0000000D = ~0xFFFFFFF2 = 0x0000000D
+        assert -~a == 14 // -~0x0000000D = -0xFFFFFFF2 = 0x0000000E
+    }
+
+    void testBitwiseNegationType() {
+        def x = ~7
+        assert x.class == java.lang.Integer
+
+        def y = ~"foo"
+        assert y.class == java.util.regex.Pattern
+
+        def z = ~"${x}"
+        assert z.class == java.util.regex.Pattern
+    }
+
+    void testBitwiseNegationTypeCallFunction() {
+        // integer test
+        assert neg(2).class == java.lang.Integer
+        assert neg(2) instanceof java.lang.Integer
+        assert neg(2) == ~2
+
+        // long test
+        assert neg(2L).class == java.lang.Long
+        assert neg(2L) instanceof java.lang.Long
+        assert neg(2L) == ~2
+
+        // BigInteger test
+        assert neg(new java.math.BigInteger("2")).class == java.math.BigInteger
+        assert neg(new java.math.BigInteger("2")) instanceof java.math.BigInteger
+        assert neg(new java.math.BigInteger("2")) == ~2
+
+        // BigInteger test
+        assert neg(2G).class == java.math.BigInteger
+        assert neg(2G) instanceof java.math.BigInteger
+        assert neg(2G) == ~2
+
+        assert neg("foo").class == java.util.regex.Pattern
+        assert neg("foo") instanceof java.util.regex.Pattern
+    }
+
+    void testCorrectAutoboxing() {
+        // test that the first parameter is boxed correctly, if not then this test
+        // will possibly produce a verify error
+        assert (!true | false) == false
+        assert (true | false) == true
+        assert (!true & false) == false
+        assert (true & false) == false
+        assert (true & !false) == true
+
+    }
+
+    Object neg(n) {
+        if (n instanceof java.lang.Integer) {
+            return ~n
+        }
+        if (n instanceof java.lang.Long) {
+            return ~n
+        }
+        if (n instanceof java.math.BigInteger) {
+            return ~n
+        }
+        return ~n.toString()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/operator/BooleanOperationsTest.groovy b/groovy/src/test/groovy/operator/BooleanOperationsTest.groovy
new file mode 100644
index 0000000..088a768
--- /dev/null
+++ b/groovy/src/test/groovy/operator/BooleanOperationsTest.groovy
@@ -0,0 +1,129 @@
+package groovy.operator
+
+class BooleanOperationsTest extends GroovyTestCase {
+
+    void testComparisons() {
+        assert true
+        assert true != false
+        
+        def x = true
+        
+        assert x
+        assert x == true
+        assert x != false
+        
+        x = false
+        
+        assert x == false
+        assert x != true
+        
+        assert !x
+        
+        def y = false        
+        assert x == y
+        
+        y = true
+        assert x != y
+    }
+    
+    
+    void testIfBranch() {
+        def x = false
+        def r = false
+        
+        if ( x ) {
+            // ignore
+        }
+        else {
+            r = true
+        }
+
+        assert r
+        
+        x = true
+        r = false
+        
+        if ( x ) {
+            r = true
+        }
+        else {
+            // ignore
+        }
+        assert r
+        
+        if ( !x ) {
+            r = false
+        }
+        else {
+            r = true
+        }
+        
+        assert r
+    }
+
+
+	void testBooleanExpression() {
+	    def x = 5
+	    def value = x > 2
+	    assert value
+	    
+	    value = x < 2
+	    assert value == false
+	}
+	
+	
+	void testBooleanOps() {
+	    boolean x = true
+	    boolean y = false
+	    assert (x & x) == true
+	    assert (x & y) == false
+	    assert (y & x) == false
+	    assert (y & y) == false
+
+	    assert (x | x) == true
+	    assert (x | y) == true
+	    assert (y | x) == true
+	    assert (y | y) == false
+
+	    assert (x ^ x) == false
+	    assert (x ^ y) == true
+	    assert (y ^ x) == true
+	    assert (y ^ y) == false
+
+	    assert (!x) == false
+	    assert (!y) == true
+	}
+
+
+	void testBooleanAssignOps() {
+	    boolean z = true
+	    z &= true
+	    assert z == true
+	    z &= false
+	    assert z == false
+
+	    z = true
+	    z |= true
+	    assert z == true
+	    z |= false
+	    assert z == true
+	    z = false
+	    z |= false
+	    assert z == false
+	    z |= true
+	    assert z == true
+
+        z = true
+        z ^= true
+        assert z == false
+        z ^= true
+        assert z == true
+        z ^= false
+        assert z == true
+        z ^= true
+        assert z == false
+        z ^= false
+        assert z == false
+	}
+
+}
diff --git a/groovy/src/test/groovy/operator/DoubleOperationTest.groovy b/groovy/src/test/groovy/operator/DoubleOperationTest.groovy
new file mode 100644
index 0000000..4998a21
--- /dev/null
+++ b/groovy/src/test/groovy/operator/DoubleOperationTest.groovy
@@ -0,0 +1,82 @@
+package groovy.operator
+
+class DoubleOperationTest extends GroovyTestCase {
+
+    def x
+    def y
+    
+    void testPlus() {
+        x = 2.1 + 2.1
+        assert x == 4.2
+        
+        x = 3 + 2.2
+        assert x == 5.2
+        
+        x = 2.2 + 4
+        assert x == 6.2
+        
+        y = x + 1
+        assert y == 7.2       
+        	
+        def z = y + x + 1 + 2
+        assert z == 16.4
+    }
+    
+    void testMinus() {
+        x = 6 - 2.2
+        assert x == 3.8
+        
+        x = 5.8 - 2
+        assert x == 3.8
+        
+        y = x - 1
+		assert y == 2.8        
+    }
+    
+    void testMultiply() {
+        x = 3 * 2.0
+        assert x == 6.0
+        
+        x = 3.0 * 2
+        assert x == 6.0
+        
+        x = 3.0 * 2.0
+        assert x == 6.0
+        y = x * 2
+        assert y == 12.0        
+    }
+    
+    void testDivide() {
+        x = 80.0 / 4
+        assert x == 20.0 , "x = " + x
+        
+        x = 80 / 4.0
+        assert x == 20.0 , "x = " + x
+        
+        y = x / 2
+        assert y == 10.0 , "y = " + y     
+    }
+
+    void testMethodNotFound() {
+    	try {
+    		println( Math.sin("foo", 7) );
+	    	fail("Should catch a MissingMethodException");
+    	} catch (MissingMethodException mme) {
+    	}
+    }
+        
+    void testCoerce() {
+    	def xyz = Math.sin(1.1);
+    	assert xyz instanceof Double;
+    	assert xyz == Math.sin(1.1D);
+    	
+        //Note that (7.3F).doubleValue() != 7.3D
+    	x = Math.sin(7.3F);
+    	assert x instanceof Double;
+    	assert x == Math.sin((7.3F).doubleValue());
+
+    	x = Math.sin(7);
+    	assert x instanceof Double;
+    	assert x == Math.sin(7.0D);
+    }
+}
diff --git a/groovy/src/test/groovy/operator/IntegerOperatorsTest.groovy b/groovy/src/test/groovy/operator/IntegerOperatorsTest.groovy
new file mode 100644
index 0000000..7083cf1
--- /dev/null
+++ b/groovy/src/test/groovy/operator/IntegerOperatorsTest.groovy
@@ -0,0 +1,185 @@
+package groovy.operator
+
+class IntegerOperatorsTest extends GroovyTestCase {
+
+    def x
+    def y
+    def z
+    
+    void testPlus() {
+        x = 2 + 2
+        assert x == 4
+        
+        y = x + 1
+        assert y == 5
+
+        z = y + x + 1 + 2
+        assert z == 12
+    }
+    
+    void testCharacterPlus() {
+        Character c1 = 1
+        Character c2 = 2
+
+        x = c2 + 2
+        assert x == 4
+
+        x = 2 + c2
+        assert x == 4
+
+        x = c2 + c2
+        assert x == 4
+          
+        y = x + c1
+        assert y == 5
+          
+        y = c1 + x
+        assert y == 5
+
+        z = y + x + c1 + 2
+        assert z == 12
+
+        z = y + x + 1 + c2
+        assert z == 12
+
+        z = y + x + c1 + c2
+        assert z == 12
+    }
+    
+    void testMinus() {
+        x = 6 - 2
+        assert x == 4
+        
+        y = x - 1
+        assert y == 3
+    }
+    
+    void testCharacterMinus() {
+        Character c1 = 1
+        Character c2 = 2
+        Character c6 = 6
+
+        x = c6 - 2
+        assert x == 4
+
+        x = 6 - c2
+        assert x == 4
+
+        x = c6 - c2
+        assert x == 4
+        
+        y = x - c1
+        assert y == 3
+    }
+    
+    void testMultiply() {
+        x = 3 * 2
+        assert x == 6
+        
+        y = x * 2
+        assert y == 12        
+    }
+    
+    void testDivide() {
+        x = 80 / 4
+        assert x == 20.0 , "x = " + x
+        
+        y = x / 2
+        assert y == 10.0 , "y = " + y
+    }
+    
+    void testIntegerDivide() {
+        x = 52.intdiv(3)
+        assert x == 17 , "x = " + x
+        
+        y = x.intdiv(2)
+        assert y == 8 , "y = " + y 
+        
+        y = 11
+        y = y.intdiv(3)
+        assert y == 3       
+    }
+    
+    void testMod() {
+        x = 100 % 3
+
+        assert x == 1
+
+        y = 11
+        y %= 3
+        assert y == 2
+    }
+    
+    void testAnd() {
+        x = 1 & 3
+
+        assert x == 1
+
+        x = 1.and(3)
+
+        assert x == 1
+    }
+     
+     void testOr() {
+         x = 1 | 3
+
+         assert x == 3
+
+         x = 1 | 4
+
+         assert x == 5
+
+         x = 1.or(3)
+
+         assert x == 3
+
+         x = 1.or(4)
+
+         assert x ==5
+    }
+    
+    void testShiftOperators() {
+
+        x = 8 >> 1
+        assert x == 4
+        assert x instanceof Integer
+
+        x = 8 << 2
+        assert x == 32
+        assert x instanceof Integer
+
+        x = 8L << 2
+        assert x == 32
+        assert x instanceof Long
+
+        x = -16 >> 4
+        assert x == -1
+
+        x = -16 >>> 4
+        assert x == 0xFFFFFFF
+
+        //Ensure that the type of the right operand (shift distance) is ignored when calculating the
+        //result.  This is how java works, and for these operators, it makes sense to keep that behavior.
+        x = Integer.MAX_VALUE << 1L
+        assert x == -2
+        assert x instanceof Integer
+
+        x = new Long(Integer.MAX_VALUE).longValue() << 1
+        assert x == 0xfffffffe
+        assert x instanceof Long
+
+        //The left operand (shift value) must be an integral type
+        try {
+            x = 8.0F >> 2
+            fail("Should catch UnsupportedOperationException");
+        } catch (UnsupportedOperationException uoe) {
+        }
+
+        //The right operand (shift distance) must be an integral type
+        try {
+            x = 8 >> 2.0
+            fail("Should catch UnsupportedOperationException");
+        } catch (UnsupportedOperationException uoe) {
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/operator/MyColor.groovy b/groovy/src/test/groovy/operator/MyColor.groovy
new file mode 100644
index 0000000..d340a9b
--- /dev/null
+++ b/groovy/src/test/groovy/operator/MyColor.groovy
@@ -0,0 +1,23 @@
+package groovy.operator
+
+import java.awt.Color
+
+class MyColor {
+    Color delegate
+
+    MyColor(int r, int g, int b) {
+        delegate = new Color(r, g, b)
+    }
+
+    MyColor negative() {
+        return new MyColor(delegate.red.intdiv(2), delegate.green.intdiv(2), delegate.blue.intdiv(2))
+    }
+
+    MyColor positive() {
+        return new MyColor(2 * delegate.red - 1, 2 * delegate.green - 1, 2 * delegate.blue - 1)
+    }
+
+    String toString() {
+        return delegate.toString()
+    }
+}
diff --git a/groovy/src/test/groovy/operator/MyColorCategory.groovy b/groovy/src/test/groovy/operator/MyColorCategory.groovy
new file mode 100644
index 0000000..06a45bd
--- /dev/null
+++ b/groovy/src/test/groovy/operator/MyColorCategory.groovy
@@ -0,0 +1,7 @@
+package groovy.operator
+
+class MyColorCategory {
+    static MyColor bitwiseNegate(MyColor self) {
+        return new MyColor(256 - self.delegate.red, 256 - self.delegate.green, 256 - self.delegate.blue)
+    }
+}
diff --git a/groovy/src/test/groovy/operator/MyColorOperatorOverloadingTest.groovy b/groovy/src/test/groovy/operator/MyColorOperatorOverloadingTest.groovy
new file mode 100644
index 0000000..ec65960
--- /dev/null
+++ b/groovy/src/test/groovy/operator/MyColorOperatorOverloadingTest.groovy
@@ -0,0 +1,16 @@
+package groovy.operator
+
+import static java.awt.Color.*
+
+class MyColorOperatorOverloadingTest extends GroovyTestCase {
+    void testAll() {
+        def c = new MyColor(128, 128, 128)
+        assert c.delegate == GRAY
+        def c2 = -c
+        assert c2.delegate == DARK_GRAY
+        assert (+c).delegate == WHITE
+        use(MyColorCategory) {
+            assert (~c2).delegate == LIGHT_GRAY
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/operator/NegateListsTest.groovy b/groovy/src/test/groovy/operator/NegateListsTest.groovy
new file mode 100644
index 0000000..6a8d0c1
--- /dev/null
+++ b/groovy/src/test/groovy/operator/NegateListsTest.groovy
@@ -0,0 +1,79 @@
+package groovy.operator
+
+/** 
+ * Test to negate lists in Classic Groovy.
+ * Test to check whether a given function is even/odd on a given domain.
+ * 
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+class NegateListsTest extends GroovyTestCase {
+
+    void testNegateList() {
+        assert -[1, 2, 3] == [-1, -2, -3]
+
+        def x = [1, 2, 3]
+        assert -x == [-1, -2, -3]
+        assert x == -[-1, -2, -3]
+        assert -(-x) == x
+
+        def y = [-1, -2, -3]
+        assert -x == y
+        assert x == -y
+    }
+
+    void testBitwiseNegateList() {
+        assert ~[1, 2, 3] == [-2, -3, -4]
+
+        def x = [1, 2, 3]
+        assert ~x == [-2, -3, -4]
+        assert x == ~[-2, -3, -4]
+        assert ~~x == x
+        assert ~(~x) == x
+
+        def y = [-2, -3, -4]
+        assert ~x == [-2, -3, -4]
+        assert x == ~y
+    }
+
+    void testEvenFunction() {
+        def PI = Math.PI
+
+        /////////////////////////////////////////////////////////////////////
+        // A case of partition having 10 subintervals.
+        // x = [0.0*PI/2, 0.1*PI/2, 0.2*PI/2, 0.3*PI/2, 0.4*PI/2, 0.5*PI/2, 
+        //               0.6*PI/2, 0.7*PI/2, 0.8*PI/2, 0.9*PI/2, 1.0*PI/2]
+
+        /////////////////////////////////////////////////////////////////////
+        // Generate a domain of function used om testing.
+        def n = 1000    // the number of partitions for the interval 0..2/PI
+        def x = []
+        for (i in 0..n) {
+            x << i*PI/n
+        }
+        // println x
+
+        def cos = { Math.cos(it) }
+        assertTrue(isEvenFn(cos, x))
+
+        def sin = { Math.sin(it) }
+        assertTrue(isOddFn(sin, x))
+
+        def tan = { Math.tan(it) }
+        assertTrue(isOddFn(tan, x))
+    }
+
+    boolean isEvenFn(f, domain) {
+        // println domain.collect(f)
+        // println( (-domain).collect(f) )
+        println( (domain.collect(f)) == ((-domain).collect(f)) )
+        return (domain.collect(f)) == ((-domain).collect(f))
+    }
+
+    boolean isOddFn(f, domain) {
+        // println domain.collect(f)
+        // println(  (-domain).collect(f) )
+        println( domain.collect(f) == -((-domain).collect(f)) )
+        return domain.collect(f) == -((-domain).collect(f))
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/operator/PowerOperatorsTest.groovy b/groovy/src/test/groovy/operator/PowerOperatorsTest.groovy
new file mode 100644
index 0000000..26e5601
--- /dev/null
+++ b/groovy/src/test/groovy/operator/PowerOperatorsTest.groovy
@@ -0,0 +1,74 @@
+package groovy.operator
+
+/** 
+ * Test Math Power Operation in Classic/New Groovy
+ * 
+ * @author Pilho Kim
+ * @version $Revision: 4996 $
+ */
+class PowerOperatorsTest extends GroovyTestCase {
+
+    void testConstantPowerOperation() {
+        assert 2**5 == 32
+        assert -2**5 == -32
+        assert 3**4 == 81
+        assert -3**4 == -81
+        assert 3**-4 == 3.power(-4)
+        assert -3**-4 == -3.power(-4)
+        assert 7**2 - 7*3 + 2 == 30         //  49 - 21 + 2 = 30
+        assert -7**2 - 7*3 + 2 == -68       // -49 - 21 + 2 = -68
+        assert -(7**2) - 7*3 + 2 == -68     // -49 - 21 + 2 = -68
+        assert (-7)**2 - 7*3 + 2 == 30     //  49 - 21 + 2 = 30
+    }
+
+    void testPowerOperation() {
+        def x = 9
+        --x
+        assert x == 8
+        println(--x)
+        assert x == 7
+        println(--x)
+        assert x == 6
+        println((--x)**3)
+        assert x == 5
+        assert (--x)**3 == 64
+        assert (-x**3) == -64
+        assert x == 4
+        assert (++x)**3 == 125
+        assert x == 5
+        assert (x++)**3 == 125
+        assert x == 6
+        println((x++)**3)
+        assert x == 7
+        println(x)
+        println("${x**2}")
+        println("${-x**2}")
+        assert x == 7
+        println("${(--x)**2}")
+        assert x == 6
+        assert (--x)**2 + x*2 - 1 == 34      // 5**2 + 5*2 - 1 = 34
+        assert x == 5
+        assert (x--)**2 + x*2 - 1 == 32      // 5**2 + 4*2 - 1 = 32
+        assert x == 4
+    }
+
+    void testConstantPowerAssignmentOperation() {
+        def x = 5
+        x **= 2
+        assert x == 25
+        assert x**2 == 625
+        assert -x**2 != 625
+        assert -x**2 == -625
+    }
+
+    void testPowerAssignmentOperation() {
+        def x = 5
+        def y = 2
+        x **= y
+        assert x == 25
+        assert x**y == 625
+        assert x**-1 == 1/25
+        assert x**-y == 1/625
+        assert x**-y == x**(-y)
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/operator/SpreadListOperatorTest.groovy b/groovy/src/test/groovy/operator/SpreadListOperatorTest.groovy
new file mode 100644
index 0000000..a0ea9b5
--- /dev/null
+++ b/groovy/src/test/groovy/operator/SpreadListOperatorTest.groovy
@@ -0,0 +1,64 @@
+package groovy.operator
+
+/**
+ * @version $Revision: 4996 $
+ *
+ * <code>[2, 3].toSpreadList() equals to *[2, 3]</code> <br><br>
+ *
+ * For an example, <pre>
+ *        assert [1, *[2, 3], 4] == [1, 2, 3, 4]
+ * </pre>
+ *
+ * @author Pilho Kim
+ * @author Jochen Theodorou
+ */
+
+class SpreadListOperatorTest extends GroovyTestCase {
+
+    void testSpreadingInList() {
+        println([1, *[222, 333], 456])
+
+        assert [1, *[222, 333], 456] == [1, 222, 333, 456]
+
+        def y = [1,2,3]
+
+        assert [*y] == y
+    }
+
+    void testSpreadingRange() {
+        def r = 1..10
+
+        assert [*r] == r
+        assert [*1..10] == r
+
+    }
+
+    void testSpreadingInMethodParameters() {
+        assert sum(1, *[2, 3], 4) == 10
+        assert sum(*[10, 20, 30, 40]) == 100
+
+        def z = [11, 22, 33]
+
+        assert sum(1, *z) == 67
+
+        assert sum(*z, 2) == 68
+
+        assert sum(*z, 44) == 110
+
+        def x = ["foo", "Bar-"]
+
+        assert sum(*x, *x) == "fooBar-fooBar-"
+    }
+
+    def sum(a, b, c, d) {
+        return a + b + c + d
+    }
+
+    void testSpreadingInClosureParameters() {
+        def twice = {it*2}
+        assert twice(3) == 6
+        assert twice("abcd") == 'abcdabcd'
+
+        assert twice(*[11]) == 22
+    }
+}
diff --git a/groovy/src/test/groovy/operator/SpreadMapOperatorTest.groovy b/groovy/src/test/groovy/operator/SpreadMapOperatorTest.groovy
new file mode 100644
index 0000000..fedac6d
--- /dev/null
+++ b/groovy/src/test/groovy/operator/SpreadMapOperatorTest.groovy
@@ -0,0 +1,98 @@
+package groovy.operator
+
+/**
+ * Test the spread map operator "*:".
+ *
+ *   For an example,
+ *            m = ['a':11, 'aa':22, 'aaa':33]
+ *            z = ['c':100, *:m]
+ *
+ *            m = ['a':11, 'aa':22, 'aaa':33]
+ *            w = ['c':100]
+ *            m.each {w[it.key] = it.value }
+ *
+ *            assert z == w
+ *
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+
+public class SpreadMapOperatorTest extends GroovyTestCase {
+    def f(m) {
+        println m.c
+    }
+
+    def func(m, i, j, k) {
+        // The first argument m is a map.
+        println m
+        println i
+        println j
+        println k
+    }
+
+    def fn() {
+        return [ 1:'ein', 2:'zwei', 3:'drei' ]
+    }
+
+    void testSpreadMap() {
+        try {
+            def m = ["a":100, "b":200]
+            def x = ['tt':55, *:m]
+            println x.size()
+            println x
+            x = ['tt':55, 'yy':77]
+            println x
+            x = [*:m, *:m]
+            println x
+            assert x == m
+
+            x = [*:x, *:fn(), 100:'hundred']
+            println x
+            println(x.getClass())
+            assert x instanceof Map
+
+            def y = [1:1, 2:2, *:[3:3, 4:4, *:[5:5], 6:6], 7:7]
+            println y
+            println(y.getClass())
+            assert y == [1:1, 2:2, 3:3, 4:4, 5:5, 6:6, 7:7]
+        }
+        catch (Exception e) {
+            e.printStackTrace()
+        }
+    }
+
+    void testSpreadMapVsWithClosure() {
+        def m = ['a':11, 'aa':22, 'aaa':33]
+        def z = ['c':100, *:m]
+ 
+        def w = ['c':100]
+        m.each { w[it.key] = it.value }
+
+        println z 
+        println w 
+        assert z == w
+
+        def z2 = [*:m, 'c':100]
+        def w2 = m
+        w2['c'] = 100
+        println z2 
+        println w2 
+        assert z2 == w2
+        assert z == z2
+        assert w == w2
+    }
+
+    void testSpreadMapFunctionCall() {
+             def m = ['a':10, 'b':20, 'c':30]
+             f(*:m)                 // Call with only one spread map argument
+             f(*:m, 'e':50)      // Call with one spread map argument and one named argument
+             f('e':100, *:m)     // Call with one named argument and one spread map argument
+
+             func('e':100, 1, 2, 3, *:m)       // Call with one named argument, three usual arguments,  and one spread map argument
+
+             def l = [4, 5]
+             func('e':100, *l, *:m, 6)       // Call with one named argument, one spread list argument, one spread map argument, and  one usual argument
+             func(7, 'e':100, *l, *:m)       // Call with one usual argument, one named argument, one spread list argument, and one spread map argument 
+    }
+}
+
diff --git a/groovy/src/test/groovy/operator/StringOperatorsTest.groovy b/groovy/src/test/groovy/operator/StringOperatorsTest.groovy
new file mode 100644
index 0000000..9b71bb6
--- /dev/null
+++ b/groovy/src/test/groovy/operator/StringOperatorsTest.groovy
@@ -0,0 +1,43 @@
+package groovy.operator
+
+class StringOperatorsTest extends GroovyTestCase {
+
+    def x
+    def y
+    
+    void testPlus() {
+        x = "hello " + "there"
+        assert x == "hello there"
+        
+        x = "hello " + 2
+        assert x == "hello 2"
+        
+        x = "hello " + 1.2
+        assert x == "hello 1.2"
+        
+        y = x + 1
+        assert y == "hello 1.21"        
+    }
+
+	void testLongPlus() {
+	    x = "hello" + " " + "there" + " nice" + " day"
+	    
+	    assert x == "hello there nice day"
+	}
+	
+    void testMinus() {
+		x = "the quick brown fox" - "quick "
+		
+		assert x == "the brown fox"
+		
+		y = x - "brown "
+		
+		assert y == "the fox"
+    }
+    
+    void testOperationsOnConstantString() {
+        assert "hello".size() == 5
+
+        assert "the quick brown".substring(4).substring(0,5) == "quick"
+    }
+}
diff --git a/groovy/src/test/groovy/operator/TernaryOperatorsTest.groovy b/groovy/src/test/groovy/operator/TernaryOperatorsTest.groovy
new file mode 100644
index 0000000..67eecd6
--- /dev/null
+++ b/groovy/src/test/groovy/operator/TernaryOperatorsTest.groovy
@@ -0,0 +1,64 @@
+package groovy.operator
+
+class TernaryOperatorsTest extends GroovyTestCase {
+
+    void testSimpleUse() {
+        def y = 5
+
+        def x = (y > 1) ? "worked" : "failed"
+        assert x == "worked"
+
+
+        x = (y < 4) ? "failed" : "worked"
+        assert x == "worked"
+    }
+
+    void testUseInParameterCalling() {
+        def z = 123
+        assertCalledWithFoo(z > 100 ? "foo" : "bar")
+        assertCalledWithFoo(z < 100 ? "bar" : "foo")
+       }
+
+    def assertCalledWithFoo(param) {
+        println "called with param ${param}"
+        assert param == "foo"
+    }
+    
+    void testWithBoolean(){
+        def a = 1
+        def x = a!=null ? a!=2 : a!=1
+        assert x == true
+        def y = a!=1 ? a!=2 : a!=1
+        assert y == false
+    }
+    
+    void testElvisOperator() {
+        def a = 1
+        def x = a?:2
+        assert x==a
+        
+        a = null
+        x = a?:2
+        assert x==2
+        
+        def list = ['a','b','c']
+        def index = 0
+        def ret = list[index++]?:"something else"
+        assert index==1
+        assert ret=='a'
+    }
+    
+    void testForType() {
+        boolean b = false
+        int anInt = b ? 100 : 100 / 3
+        assert anInt.class == Integer
+    }
+    
+    void testBytecodeRegisters() {
+        // this code will blow up if the true and false parts
+        // are not handled correctly in regards to the registers.
+        def i = 1
+        def c= { false? { i } : it == i }
+        assert true
+    }
+}   
diff --git a/groovy/src/test/groovy/operator/UnaryMinusNumberTests.groovy b/groovy/src/test/groovy/operator/UnaryMinusNumberTests.groovy
new file mode 100644
index 0000000..58625a7
--- /dev/null
+++ b/groovy/src/test/groovy/operator/UnaryMinusNumberTests.groovy
@@ -0,0 +1,27 @@
+package groovy.operator
+
+class UnaryMinusNumberTests extends GroovyTestCase {
+
+    void testNegateInteger() {
+        def a = -1
+        assert a == -1
+    }
+
+    void testNegateIntegerExpression() {
+        def a = -1
+        a = -a
+        assert a == 1
+    }
+
+    void testNegateDouble() {
+        def a = -1.0
+        assert a == -1.0
+    }
+
+    void testNegateDoubleExpression() {
+        def a = -1.0
+        a = -a
+        assert a == 1.0
+    }
+
+}
diff --git a/groovy/src/test/groovy/operator/UnaryMinusOperatorTest.groovy b/groovy/src/test/groovy/operator/UnaryMinusOperatorTest.groovy
new file mode 100644
index 0000000..978d4a8
--- /dev/null
+++ b/groovy/src/test/groovy/operator/UnaryMinusOperatorTest.groovy
@@ -0,0 +1,31 @@
+package groovy.operator
+
+class UnaryMinusOperatorTest extends GroovyTestCase {
+
+    void testUnaryMinus() {
+        def value = -1
+        
+        assert value == -1
+        
+        def x = value + 2
+        assert x == 1
+        
+        def y = -value
+        assert y == 1
+    }   
+    
+    void testBug() {
+        def a = 1
+        def b = -a
+        
+        assert b == -1
+    }
+    
+    void testShellBug() {
+        assertScript("""
+def a = 1
+def b = -a
+assert b == -1            
+""")
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/runtime/metaclass/groovy/bugs/CustomMetaClassTestMetaClass.groovy b/groovy/src/test/groovy/runtime/metaclass/groovy/bugs/CustomMetaClassTestMetaClass.groovy
new file mode 100644
index 0000000..d3c072e
--- /dev/null
+++ b/groovy/src/test/groovy/runtime/metaclass/groovy/bugs/CustomMetaClassTestMetaClass.groovy
@@ -0,0 +1,7 @@
+package groovy.runtime.metaclass.groovy.bugs
+
+class CustomMetaClassTestMetaClass extends groovy.lang.DelegatingMetaClass {
+  CustomMetaClassTestMetaClass( final MetaClass metaClazz ){
+     super( metaClazz )
+  }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/script/AtomTestScript.groovy b/groovy/src/test/groovy/script/AtomTestScript.groovy
new file mode 100644
index 0000000..38af581
--- /dev/null
+++ b/groovy/src/test/groovy/script/AtomTestScript.groovy
@@ -0,0 +1,71 @@
+package groovy.script
+
+import groovy.xml.MarkupBuilder;
+
+/** note you could use Expando classes if you don't mind being dynamically typed 
+f = new Expando()
+
+f.author = new Expando(name:'Ted Leung',url:'http://www.sauria.com/blog', email:'twl@sauria.com')
+
+f.entries = [ new Expando(title:'one',summary:'first post'), new Expando(title:'two',summary:'the second post'), new Expando(title:'three', summary:'post the third'), new Expando(title:'four',summary:'the ponderous fourth post') ]
+*/
+
+f = new Feed()
+
+f.author = new Person(name:'Ted Leung',url:'http://www.sauria.com/blog', email:'twl@sauria.com')
+
+f.entries = [ new Entry(title:'one',summary:'first post'), new Entry(title:'two',summary:'the second post'), new Entry(title:'three', summary:'post the third'), new Entry(title:'four',summary:'the ponderous fourth post') ]
+
+f.entries.each { println it.title }
+println f.author.name
+
+xml = new MarkupBuilder()
+
+atom = xml.atom() {
+  title("Ted Leung off the air")
+  link("http://www.sauria.com/noblog")
+  author() {
+    person() {
+      name(f.author.name)
+      url(f.author.url)
+      email(f.author.email)
+    }
+  }
+
+  for (e in f.entries) {
+    entry() {
+      title(e.title)
+      summary(e.summary)
+    }
+  }
+}
+
+
+class Feed {
+    String title
+    String link
+    Person author
+    String tagline
+    String generator
+    String copyright
+    String modified
+    List entries
+}
+
+class Entry {
+    String title
+    String link
+    String id
+    String summary
+    String content
+    Person author
+    String created
+    String issued
+    String modified
+}
+
+class Person {
+    String name
+    String url
+    String email
+}
diff --git a/groovy/src/test/groovy/script/CallAnotherScript.groovy b/groovy/src/test/groovy/script/CallAnotherScript.groovy
new file mode 100644
index 0000000..b7d9317
--- /dev/null
+++ b/groovy/src/test/groovy/script/CallAnotherScript.groovy
@@ -0,0 +1,8 @@
+import java.io.File
+
+println("About to call another script")
+
+script = new GroovyShell()
+script.run(new File("src/test/groovy/script/HelloWorld.groovy"), [])
+
+println("Done")
diff --git a/groovy/src/test/groovy/script/ClassWithScript.groovy b/groovy/src/test/groovy/script/ClassWithScript.groovy
new file mode 100644
index 0000000..60798ba
--- /dev/null
+++ b/groovy/src/test/groovy/script/ClassWithScript.groovy
@@ -0,0 +1,3 @@
+class X {}
+x = new X()
+println(x)
\ No newline at end of file
diff --git a/groovy/src/test/groovy/script/EvalInScript.groovy b/groovy/src/test/groovy/script/EvalInScript.groovy
new file mode 100644
index 0000000..4dd3940
--- /dev/null
+++ b/groovy/src/test/groovy/script/EvalInScript.groovy
@@ -0,0 +1,14 @@
+import java.io.File
+
+a = 1
+evaluate("a = 3")
+assert a == 3
+
+println("Done and now set a to ${a}")
+
+
+println("About to call another script")
+
+evaluate(new File("src/test/groovy/script/HelloWorld.groovy"))
+
+println("Done")
diff --git a/groovy/src/test/groovy/script/HelloWorld.groovy b/groovy/src/test/groovy/script/HelloWorld.groovy
new file mode 100644
index 0000000..4f813ab
--- /dev/null
+++ b/groovy/src/test/groovy/script/HelloWorld.groovy
@@ -0,0 +1,4 @@
+
+println "Hello world"
+
+
diff --git a/groovy/src/test/groovy/script/HelloWorld2.groovy b/groovy/src/test/groovy/script/HelloWorld2.groovy
new file mode 100644
index 0000000..9056ef0
--- /dev/null
+++ b/groovy/src/test/groovy/script/HelloWorld2.groovy
@@ -0,0 +1,4 @@
+
+x = ['James', 'Bob', 'Brian']
+x.each { println("hello " + it) }
+
diff --git a/groovy/src/test/groovy/script/MapFromList.groovy b/groovy/src/test/groovy/script/MapFromList.groovy
new file mode 100644
index 0000000..3cd94c8
--- /dev/null
+++ b/groovy/src/test/groovy/script/MapFromList.groovy
@@ -0,0 +1,25 @@
+package groovy.script
+
+class MapFromList {
+    void talk(a) {
+        println("hello "+a)
+    }
+
+    void doit(args) {
+        def i = 1
+        def l = [:]
+        args.each { 
+	    talk(it)
+	    l.put(it,i++)
+	    }
+        l.each {
+           println(it)
+        }
+    }
+
+    static void main(args) {
+        def a = ['tom','dick','harry']
+        def t = new MapFromList()
+        t.doit(a)
+    }
+}
diff --git a/groovy/src/test/groovy/script/MarkupTestScript.groovy b/groovy/src/test/groovy/script/MarkupTestScript.groovy
new file mode 100644
index 0000000..8b45448
--- /dev/null
+++ b/groovy/src/test/groovy/script/MarkupTestScript.groovy
@@ -0,0 +1,22 @@
+import groovy.xml.MarkupBuilder;
+
+class Bean {
+	String b
+};
+
+t = new Bean()
+t.b = "hello"
+println t.b
+println "test: ${t.b}"
+
+xml = new MarkupBuilder()
+root = xml.foo {
+	bar {
+		// works
+		baz("test")
+		// fails
+		baz(t.b)
+		// fails
+		baz("${t.b}")
+	}
+} 
diff --git a/groovy/src/test/groovy/script/MethodTestScript.groovy b/groovy/src/test/groovy/script/MethodTestScript.groovy
new file mode 100644
index 0000000..2bdf017
--- /dev/null
+++ b/groovy/src/test/groovy/script/MethodTestScript.groovy
@@ -0,0 +1 @@
+GString.methods.each { println it.name }
diff --git a/groovy/src/test/groovy/script/PackageScript.groovy b/groovy/src/test/groovy/script/PackageScript.groovy
new file mode 100644
index 0000000..1e57436
--- /dev/null
+++ b/groovy/src/test/groovy/script/PackageScript.groovy
@@ -0,0 +1,3 @@
+package groovy.script
+
+println("Hello world")
diff --git a/groovy/src/test/groovy/script/ScriptTest.groovy b/groovy/src/test/groovy/script/ScriptTest.groovy
new file mode 100644
index 0000000..14709b7
--- /dev/null
+++ b/groovy/src/test/groovy/script/ScriptTest.groovy
@@ -0,0 +1,36 @@
+package groovy.script
+
+class ScriptTest extends GroovyTestCase {
+    void testScripts() {
+        def file = new File("src/test/groovy/script")
+        file.eachFile {
+            def name = it.name
+            if (name.endsWith('.groovy')) {
+                if (name.startsWith('ScriptTest')) {
+                    //
+                }
+				else {                        
+                	runScript(it) 
+				}
+            } 
+        }
+    }
+    
+    protected def runScript(file) {
+        println("Running script: " + file)
+        
+        def shell = new GroovyShell()
+        def args = ['a', 'b', 'c']
+        
+        shell.run(file, args)
+
+        /** @todo this doesn't work when ran in an IDE?
+        try {
+	        shell.run(file, args)
+        } 
+        catch (Exception e) {
+            println("Caught: " + e)
+        }
+         */
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/script/ScriptWithFunctions.groovy b/groovy/src/test/groovy/script/ScriptWithFunctions.groovy
new file mode 100644
index 0000000..bb04b78
--- /dev/null
+++ b/groovy/src/test/groovy/script/ScriptWithFunctions.groovy
@@ -0,0 +1,11 @@
+def foo(list, value) {
+    println "Calling function foo() with param ${value}"
+    list << value
+}
+
+x = []
+foo(x, 1)
+foo(x, 2)
+assert x == [1, 2]
+
+println "Creating list ${x}"
\ No newline at end of file
diff --git a/groovy/src/test/groovy/script/ShowArgs.groovy b/groovy/src/test/groovy/script/ShowArgs.groovy
new file mode 100644
index 0000000..b3d0578
--- /dev/null
+++ b/groovy/src/test/groovy/script/ShowArgs.groovy
@@ -0,0 +1,5 @@
+
+for (a in args) {
+    println("Argument: " + a)
+}
+
diff --git a/groovy/src/test/groovy/script/UseClosureInScript.groovy b/groovy/src/test/groovy/script/UseClosureInScript.groovy
new file mode 100644
index 0000000..9769195
--- /dev/null
+++ b/groovy/src/test/groovy/script/UseClosureInScript.groovy
@@ -0,0 +1,4 @@
+a = 1
+[1].each { 
+    a = it 
+}
diff --git a/groovy/src/test/groovy/security/RunAllGroovyScriptsSuite.java b/groovy/src/test/groovy/security/RunAllGroovyScriptsSuite.java
new file mode 100644
index 0000000..144f7c3
--- /dev/null
+++ b/groovy/src/test/groovy/security/RunAllGroovyScriptsSuite.java
@@ -0,0 +1,66 @@
+/*
+ * Created on Apr 7, 2004
+ *
+ * To change the template for this generated file go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+package groovy.security;
+
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Run all the .groovy scripts found under the src/test tree with a security manager active.
+ * Not currently part of the build because it adds about 4 minutes to the build process.
+ *
+ * @author Steve Goetze
+ */
+public class RunAllGroovyScriptsSuite extends SecurityTestSupport {
+
+    /**
+     * Find all Groovy script test cases in the source tree and execute them with a security policy in effect.
+     * The complete filename of the groovy source file is used as the codebase so that the proper grants can be
+     * made for each script.
+     */
+    protected void executeTests(File dir, TestResult result) throws Exception {
+        File[] files = dir.listFiles();
+        List traverseList = new ArrayList();
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            if (file.isDirectory()) {
+                traverseList.add(file);
+            } else {
+                String name = file.getName();
+                if (name.endsWith("Test.groovy") || name.endsWith("Bug.groovy")) {
+                    //if (name.endsWith("IanMaceysBug.groovy")) {
+                    Class clazz = parseClass(file);
+                    if (TestCase.class.isAssignableFrom(clazz)) {
+                        TestSuite suite = new TestSuite(clazz);
+                        suite.run(result);
+                    }
+                }
+            }
+        }
+        for (Iterator iter = traverseList.iterator(); iter.hasNext();) {
+            executeTests((File) iter.next(), result);
+        }
+    }
+
+    public void testGroovyScripts() throws Exception {
+        if (!isSecurityAvailable()) {
+            return;
+        }
+        TestResult result = new TestResult();
+        executeTests(new File("src/test"), result);
+        if (!result.wasSuccessful()) {
+            new SecurityTestResultPrinter(System.out).print(result);
+            fail("At least one groovy testcase did not run under the secure groovy environment.  Results in the testcase output");
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/security/RunOneGroovyScript.java b/groovy/src/test/groovy/security/RunOneGroovyScript.java
new file mode 100644
index 0000000..717924d
--- /dev/null
+++ b/groovy/src/test/groovy/security/RunOneGroovyScript.java
@@ -0,0 +1,34 @@
+package groovy.security;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+import java.io.File;
+
+/**
+ * Test case for running a single groovy script parsed from a .groovy file.
+ */
+public class RunOneGroovyScript extends SecurityTestSupport {
+
+    protected static String file;
+
+    public static void main(String[] args) {
+        if (args.length > 0) {
+            file = args[0];
+        }
+        TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        return new TestSuite(RunOneGroovyScript.class);
+    }
+
+    public void testScript() {
+        String fileName = System.getProperty("script", file);
+        if (fileName == null) {
+            throw new RuntimeException("No filename given in the 'script' system property so cannot run a Groovy script");
+        }
+        assertExecute(new File(fileName), null);
+    }
+}
diff --git a/groovy/src/test/groovy/security/SecurityTest.java b/groovy/src/test/groovy/security/SecurityTest.java
new file mode 100644
index 0000000..8fdc256
--- /dev/null
+++ b/groovy/src/test/groovy/security/SecurityTest.java
@@ -0,0 +1,120 @@
+package groovy.security;
+
+import groovy.lang.GroovyCodeSource;
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+import org.codehaus.groovy.control.CompilationFailedException;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.security.Security;
+import java.util.PropertyPermission;
+
+/**
+ * Test the effects of enabling security in Groovy.  Some tests below check for proper framework
+ * behavior (e.g. ensuring that GroovyCodeSources may only be created for which proper permissions exist).
+ * Other tests run .groovy scripts under a secure environment and ensure that the proper permissions
+ * are required for success.
+ * <p/>
+ * Todo: find out why the marked tests are environment specific and why security tests are not
+ * running on the build server.
+ *
+ * @author Steve Goetze
+ */
+public class SecurityTest extends SecurityTestSupport {
+
+    public static void main(String[] args) {
+        TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        return new TestSuite(SecurityTest.class);
+    }
+
+    public void testForbiddenProperty() {
+        String script = "System.getProperty(\"user.home\")";
+        assertExecute(script, null, new PropertyPermission("user.home", "read"));
+    }
+
+    public void testForbiddenPackage() {
+        String script = "import sun.net.*; s = new NetworkClient()";
+        assertExecute(script, "/groovy/security/testForbiddenPackage", new RuntimePermission("accessClassInPackage.sun.*"));
+    }
+
+    public void testForbiddenCodebase() {
+        assertExecute(new File("src/test/groovy/security/forbiddenCodeBase.gvy"), new GroovyCodeSourcePermission("/groovy/security/forbiddenCodeBase"));
+    }
+
+    public void testForbiddenCodebaseWithActions() {
+        assertExecute(new File("src/test/groovy/security/forbiddenCodeBase.gvy"), new GroovyCodeSourcePermission("/groovy/security/forbiddenCodeBase", "unused actions string"));
+    }
+
+    //Check that the Security package.access control works.
+    public void testPackageAccess() {
+        String script = "new javax.print.PrintException();";
+        Security.setProperty("package.access", "javax.print");
+        //This should throw an ACE because its codeBase does not allow access to javax.print
+        assertExecute(script, "/groovy/security/javax/print/deny", new RuntimePermission("accessClassInPackage.javax.print"));
+        //This should not throw an ACE because groovy.policy grants the codeBase access to javax.print
+        assertExecute(script, "/groovy/security/javax/print/allow", null);
+    }
+
+    public void testBadScriptNameBug() {
+        assertExecute(new File("src/test/groovy/bugs/BadScriptNameBug.groovy"), null);
+    }
+
+    public void testClosureListenerTest() {
+        //if (System.getProperty("java.version").startsWith("1.5") && notYetImplemented()) return;
+        if (System.getProperty("java.version").startsWith("1.5")) return;
+        assertExecute(new File("src/test/groovy/ClosureListenerTest.groovy"), null);
+    }
+
+    public void testClosureMethodTest() {
+        assertExecute(new File("src/test/groovy/ClosureMethodTest.groovy"), null);
+    }
+
+    public void testGroovyMethodsTest_FAILS() {
+        if (notYetImplemented()) return;
+        assertExecute(new File("src/test/groovy/GroovyMethodsTest.groovy"), null);
+    }
+
+    public void testClosureWithDefaultParamTest() {
+        assertExecute(new File("src/test/groovy/ClosureWithDefaultParamTest.groovy"), null);
+    }
+
+    public void testGroovy303_Bug() {
+        assertExecute(new File("src/test/groovy/bugs/Groovy303_Bug.groovy"), null);
+    }
+
+    public void testScriptTest() {
+        assertExecute(new File("src/test/groovy/script/ScriptTest.groovy"), null);
+    }
+
+    //In addition to requiring several permissions, this test is an example of the case
+    //where the groovy class loader is required at script invocation time as well as
+    //during compilation.
+    public void testSqlCompleteWithoutDataSourceTest() {
+        assertExecute(new File("src/test/groovy/sql/SqlCompleteWithoutDataSourceTest.groovy"), null);
+    }
+
+    //Test to prevent scripts from invoking the groovy compiler.  This is done by restricting access
+    //to the org.codehaus.groovy packages.
+    public void testMetaClassTest() {
+        //Security.setProperty("package.access", "org.codehaus.groovy");
+        //assertExecute(new File("src/test/org/codehaus/groovy/classgen/MetaClassTest.groovy"), new RuntimePermission("accessClassInPackage.org.codehaus.groovy"));
+    }
+
+    //Mailing list post by Richard Hensley reporting a CodeSource bug.  A GroovyCodeSource created
+    //with a URL was causing an NPE.
+    public void testCodeSource() throws IOException, CompilationFailedException {
+        URL script = loader.getResource("groovy/ArrayTest.groovy");
+        try {
+            new GroovyCodeSource(script);
+        } catch (RuntimeException re) {
+            assertEquals("Could not construct a GroovyCodeSource from a null URL", re.getMessage());
+        }
+    }
+
+}
diff --git a/groovy/src/test/groovy/security/SecurityTestSupport.java b/groovy/src/test/groovy/security/SecurityTestSupport.java
new file mode 100644
index 0000000..ee03578
--- /dev/null
+++ b/groovy/src/test/groovy/security/SecurityTestSupport.java
@@ -0,0 +1,271 @@
+package groovy.security;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyCodeSource;
+import groovy.lang.Script;
+import groovy.util.GroovyTestCase;
+import junit.framework.TestCase;
+import junit.framework.TestFailure;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import junit.textui.ResultPrinter;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+import java.security.*;
+import java.util.Enumeration;
+
+/**
+ * @author Steve Goetze
+ */
+public class SecurityTestSupport extends GroovyTestCase {
+    private static final String POLICY_FILE = "security/groovy.policy";
+    private static int counter = 0;
+    private static boolean securityDisabled;
+    private static boolean securityAvailable;
+    private static boolean securityChecked = false;
+
+    static {
+        if (System.getProperty("groovy.security.disabled") != null) {
+            securityAvailable = false;
+            securityDisabled = true;
+        } else {
+            securityDisabled = false;
+            if (new File(POLICY_FILE).exists()) {
+                securityAvailable = true;
+                System.setProperty("java.security.policy", "=security/groovy.policy");
+            } else {
+                securityAvailable = false;
+            }
+        }
+    }
+
+    public static boolean isSecurityAvailable() {
+        return securityAvailable;
+    }
+
+    public static boolean isSecurityDisabled() {
+        return securityDisabled;
+    }
+
+    public static void resetSecurityPolicy(String policyFileURL) {
+        System.setProperty("java.security.policy", policyFileURL);
+        Policy.getPolicy().refresh();
+    }
+
+    protected class SecurityTestResultPrinter extends ResultPrinter {
+
+        public SecurityTestResultPrinter(PrintStream stream) {
+            super(stream);
+        }
+
+        public void print(TestResult result) {
+            getWriter().println("Security testing on a groovy test failed:");
+            printErrors(result);
+            printFailures(result);
+            printFooter(result);
+        }
+    }
+
+    protected GroovyClassLoader loader = (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+        public Object run() {
+            return new GroovyClassLoader(SecurityTestSupport.class.getClassLoader());
+        }
+    });
+
+    private SecurityManager securityManager;
+    private ClassLoader currentClassLoader;
+
+    public SecurityTestSupport() {
+    }
+
+    /*
+      * Check SecuritySupport to see if security is properly configured.  If not, fail the first
+      * test that runs.  All remaining tests will run, but not do any security checking.
+      */
+    private boolean checkSecurity() {
+        if (!securityChecked) {
+            securityChecked = true;
+            if (!isSecurityAvailable()) {
+                fail("Security is not available - skipping security tests.  Ensure that "
+                        + POLICY_FILE + " is available from the current execution directory.");
+            }
+        }
+        return isSecurityAvailable();
+    }
+
+    //Prepare for each security test.  First, check to see if groovy.lib can be determined via
+    //a call to checkSecurity().  If not, fail() the first test.  Establish a security manager
+    //and make the GroovyClassLoader the initiating class loader (ala GroovyShell) to compile AND
+    //invoke the test scripts.  This handles cases where multiple .groovy scripts are involved in a
+    //test case: a.groovy depends on b.groovy; a.groovy is parsed (and in the process the gcl
+    //loads b.groovy via findClass).  Note that b.groovy is only available in the groovy class loader.
+    //See
+    protected void setUp() {
+        if (checkSecurity()) {
+            securityManager = System.getSecurityManager();
+            if (securityManager == null) {
+                System.setSecurityManager(new SecurityManager());
+            }
+        }
+        currentClassLoader = Thread.currentThread().getContextClassLoader();
+        AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                Thread.currentThread().setContextClassLoader(loader);
+                return null;
+            }
+        });
+    }
+
+    protected void tearDown() {
+        AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                System.setSecurityManager(securityManager);
+                Thread.currentThread().setContextClassLoader(currentClassLoader);
+                return null;
+            }
+        });
+    }
+
+    protected synchronized String generateClassName() {
+        return "testSecurity" + (++counter);
+    }
+
+    /*
+      * Execute the groovy script contained in file.  If missingPermission
+      * is non-null, then this invocation expects an AccessControlException with missingPermission
+      * as the reason.  If missingPermission is null, the script is expected to execute successfully.
+      */
+    protected Class parseClass(File file) {
+        GroovyCodeSource gcs = null;
+        try {
+            gcs = new GroovyCodeSource(file);
+        } catch (FileNotFoundException fnfe) {
+            fail(fnfe.toString());
+        }
+        return parseClass(gcs);
+    }
+
+    /*
+	 * Parse the Groovy code contained in the GroovyCodeSource as a privileged operation (i.e. do not
+	 * require the code source to have specific compile time permissions) and return the resulting class.
+	 */
+    protected Class parseClass(final GroovyCodeSource gcs) {
+        Class clazz = null;
+        try {
+            clazz = loader.parseClass(gcs);
+        } catch (Exception e) {
+            fail(e.toString());
+        }
+        return clazz;
+    }
+
+    /*
+      * Parse the script contained in the GroovyCodeSource as a privileged operation (i.e. do not
+      * require the code source to have specific compile time permissions).  If the class produced is a
+      * TestCase, run the test in a suite and evaluate against the missingPermission.
+      * Otherwise, run the class as a groovy script and evaluate against the missingPermission.
+      */
+    private void parseAndExecute(final GroovyCodeSource gcs, Permission missingPermission) {
+        Class clazz = null;
+        try {
+            clazz = loader.parseClass(gcs);
+        } catch (Exception e) {
+            fail(e.toString());
+        }
+        if (TestCase.class.isAssignableFrom(clazz)) {
+            executeTest(clazz, missingPermission);
+        } else {
+            executeScript(clazz, missingPermission);
+        }
+    }
+
+    protected void executeTest(Class test, Permission missingPermission) {
+        TestSuite suite = new TestSuite();
+        suite.addTestSuite(test);
+        TestResult result = new TestResult();
+        suite.run(result);
+        if (result.wasSuccessful()) {
+            if (missingPermission == null) {
+                return;
+            } else {
+                fail("Security test expected an AccessControlException on " + missingPermission + ", but did not receive one");
+            }
+        } else {
+            if (missingPermission == null) {
+                new SecurityTestResultPrinter(System.out).print(result);
+                fail("Security test was expected to run successfully, but failed (results on System.out)");
+            } else {
+                //There may be more than 1 failure:  iterate to ensure that they all match the missingPermission.
+                boolean otherFailure = false;
+                for (Enumeration e = result.errors(); e.hasMoreElements();) {
+                    TestFailure failure = (TestFailure) e.nextElement();
+                    if (failure.thrownException() instanceof AccessControlException) {
+                        AccessControlException ace = (AccessControlException) failure.thrownException();
+                        if (missingPermission.implies(ace.getPermission())) {
+                            continue;
+                        }
+                    }
+                    otherFailure = true;
+                }
+                if (otherFailure) {
+                    new SecurityTestResultPrinter(System.out).print(result);
+                    fail("Security test expected an AccessControlException on " + missingPermission + ", but failed for other reasons (results on System.out)");
+                }
+            }
+        }
+    }
+
+    protected void executeScript(Class scriptClass, Permission missingPermission) {
+        try {
+            Script script = InvokerHelper.createScript(scriptClass, new Binding());
+            script.run();
+            //InvokerHelper.runScript(scriptClass, null);
+        } catch (AccessControlException ace) {
+            if (missingPermission != null && missingPermission.implies(ace.getPermission())) {
+                return;
+            } else {
+                fail(ace.toString());
+            }
+        }
+        if (missingPermission != null) {
+            fail("Should catch an AccessControlException");
+        }
+    }
+
+    /*
+      * Execute the groovy script contained in file.  If missingPermission
+      * is non-null, then this invocation expects an AccessControlException with missingPermission
+      * as the reason.  If missingPermission is null, the script is expected to execute successfully.
+      */
+    protected void assertExecute(File file, Permission missingPermission) {
+        if (!isSecurityAvailable()) {
+            return;
+        }
+        GroovyCodeSource gcs = null;
+        try {
+            gcs = new GroovyCodeSource(file);
+        } catch (FileNotFoundException fnfe) {
+            fail(fnfe.toString());
+        }
+        parseAndExecute(gcs, missingPermission);
+    }
+
+    /*
+      * Execute the script represented by scriptStr using the supplied codebase.  If missingPermission
+      * is non-null, then this invocation expects an AccessControlException with missingPermission
+      * as the reason.  If missingPermission is null, the script is expected to execute successfully.
+      */
+    protected void assertExecute(String scriptStr, String codeBase, Permission missingPermission) {
+        if (!isSecurityAvailable()) {
+            return;
+        }
+        if (codeBase == null) {
+            codeBase = "/groovy/security/test";
+        }
+        parseAndExecute(new GroovyCodeSource(scriptStr, generateClassName(), codeBase), missingPermission);
+    }
+}
diff --git a/groovy/src/test/groovy/security/SignedJarTest.java b/groovy/src/test/groovy/security/SignedJarTest.java
new file mode 100644
index 0000000..638c87d
--- /dev/null
+++ b/groovy/src/test/groovy/security/SignedJarTest.java
@@ -0,0 +1,46 @@
+package groovy.security;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+
+/**
+ * Read a .groovy file from a signed jar and verify that a policy file grant with a signedBy field
+ * works.  The following steps were used to create and manage the keys used to sign and read the jar:
+ * <ol>
+ * <li>keytool -genkey -alias groovy -keypass keypass -keystore groovystore -storepass storepass -validity 7000
+ * <li>keytool -export -keystore groovystore -alias groovy -file GroovyDev.cer
+ * <li>keytool -import -alias groovy -file GroovyDev.cer -keystore groovykeys
+ * </ol>
+ * Once the keys are constructed, creat the jar and sign:
+ * <ol>
+ * <li>jar -cvf Groovy.jar groovy
+ * <li>jarsigner -keystore groovystore -signedjar GroovyJarTest.jar Groovy.jar groovy
+ * </ol>
+ * Add the keystore to the policy file and write the grant:
+ * <ol>
+ * <li>keystore "file:${user.dir}/src/test/groovy/security/groovykeys";
+ * </ol>
+ */
+public class SignedJarTest extends SecurityTestSupport {
+
+    public static void main(String[] args) {
+        TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        return new TestSuite(SignedJarTest.class);
+    }
+
+    public void testReadSignedJar() throws Exception {
+        if (!isSecurityAvailable() || (notYetImplemented())) return;
+
+        //spg 2006-02-09 The GroovyClassLoader code that checked jar files
+        //for source files was removed last July.  This test will not function
+        //without that capability.
+        Class c = loader.loadClass("groovy.security.JarTest");  // ClassNotFoundException !
+        executeTest(c, null);
+
+    }
+}
diff --git a/groovy/src/test/groovy/security/forbiddenCodeBase.gvy b/groovy/src/test/groovy/security/forbiddenCodeBase.gvy
new file mode 100644
index 0000000..d5e0141
--- /dev/null
+++ b/groovy/src/test/groovy/security/forbiddenCodeBase.gvy
@@ -0,0 +1,6 @@
+/*
+ * Groovy script to be read as a file to test the file based codesource features of groovy security.
+ * The file extension of .gvy is used to prevent this script from being treated as a groovy script by maven.
+ */
+ 
+new GroovyShell().evaluate("1+2", "forbiddenCodeBaseTest", "/groovy/security/forbiddenCodeBase");
diff --git a/groovy/src/test/groovy/servlet/AbstractHttpServletTest.groovy b/groovy/src/test/groovy/servlet/AbstractHttpServletTest.groovy
new file mode 100644
index 0000000..5ab8740
--- /dev/null
+++ b/groovy/src/test/groovy/servlet/AbstractHttpServletTest.groovy
@@ -0,0 +1,243 @@
+package groovy.servlet
+
+import groovy.servlet.AbstractHttpServlet
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.ServletContext
+import javax.servlet.ServletConfig
+import java.net.URL
+
+/**
+* This test case tests the AbstractHttpServlet class. It uses a test
+* specific subclass called ConcreteHttpServlet to test the abstract
+* class in isolation from any implementations. 
+* 
+* @author Hamlet D'Arcy
+*/ 
+class AbstractHttpServletTest extends GroovyTestCase {
+
+	def servlet; 
+	
+	void setUp() {
+		super.setUp()
+		servlet = new ConcreteHttpServlet()
+	}
+	
+	/**
+	* getScriptUri() concatenates the servlet path and path info 
+	* attributes if attributes exist on the http request. 
+	*/
+	void testGetScriptUri_AllAttributesExist() {
+
+		//just return whatever attributes were requested
+		def request = {attribute -> attribute} 
+
+		assert servlet.getScriptUri(request as HttpServletRequest) ==
+			AbstractHttpServlet.INC_SERVLET_PATH + AbstractHttpServlet.INC_PATH_INFO
+	}
+
+	/**
+	* getScriptUri() returns the servlet path if the http request
+	* contains path but no path info attribute.  
+	*/
+	void testGetScriptUri_NoPathInfoAttribute() {
+
+		//just return whatever attributes were requested, except for path info attribute
+		def request = {attribute -> 
+			if (attribute == AbstractHttpServlet.INC_PATH_INFO) {
+				return null 
+			}
+			attribute
+		} 
+
+		assert servlet.getScriptUri(request as HttpServletRequest) ==
+			AbstractHttpServlet.INC_SERVLET_PATH
+	}
+	
+	/**
+	* Tests getScriptUri when no attributes exist, but servletPath and 
+	* pathInfo methods return data. 
+	*/
+	void testGetScriptUri_NoAttributesPathInfoExists() {
+		def request = [ 
+			getAttribute: {null}, 
+		    getServletPath: {"servletPath"}, 
+		    getPathInfo: {"pathInfo"}] as HttpServletRequest
+
+		def servlet = new ConcreteHttpServlet()
+		assert servlet.getScriptUri(request) == "servletPathpathInfo"
+		
+	}
+
+	/**
+	* Tests getScriptUri when no attributes exist, no path info exists, 
+	* but servletPath returns data. 
+	*/
+	void testGetScriptUri_NoAttributesPathInfoMissing() {
+		def request = [ 
+			getAttribute: {null}, 
+		    getServletPath: {"servletPath"}, 
+		    getPathInfo: {null}] as HttpServletRequest
+
+		def servlet = new ConcreteHttpServlet()
+		assert servlet.getScriptUri(request) == "servletPath"
+		
+	}
+	
+	/**
+	* Tests getting URIs as files. 
+	*/ 
+	void testGetScriptURIasFile() {
+
+		def request = [ 
+		    getAttribute: {null}, 
+		    getServletPath: {"servletPath"}, 
+		    getPathInfo: {"pathInfo"}] as HttpServletRequest
+
+		def servletContext = [
+		    getRealPath: { arg-> "realPath" + arg}] as ServletContext
+
+		def servletConfig = [
+		    getServletContext: {servletContext}, 
+		    getInitParameter: {null}] as ServletConfig
+
+		servlet.init(servletConfig)
+		def file = servlet.getScriptUriAsFile(request)
+		assert file.getName() == "realPathservletPathpathInfo"		
+	}
+	
+	/**
+	* Tests that exception is thrown when resource is not found. 
+	*/ 
+	void testGetResourceConnection_MissingResource() {
+		def servletContext = [
+		    getRealPath: {arg-> "realPath" + arg}, 
+		    getResource: {arg -> null} ] as ServletContext
+
+		def servletConfig = [
+		    getServletContext: {servletContext}, 
+		    getInitParameter: {null}] as ServletConfig
+
+		//servlet config is used to find resources
+		servlet.init(servletConfig)    
+
+		shouldFail(groovy.util.ResourceException) {
+		    servlet.getResourceConnection("someresource")
+		}
+	}
+
+	/**
+	* Tests finding resource. 
+	*/ 
+	public void testGetResourceConnection_FoundInCurrentDir() {
+		def urlStub = new java.net.URL("http://foo")
+		def servletContext = [
+		    getRealPath: { arg-> "realPath" + arg}, 
+		    getResource: {arg -> 
+				if (arg == "/someresource") return urlStub
+				else return null
+			} ] as ServletContext
+
+		def servletConfig = [
+		    getServletContext: {servletContext}, 
+		    getInitParameter: {null}] as ServletConfig
+
+		//servlet config is used to find resources
+		servlet.init(servletConfig)    
+
+		def connection = servlet.getResourceConnection("someresource")
+
+		assert connection.getURL() == urlStub		
+	}
+
+	/**
+	* Tests finding resource in web-inf directory. 
+	*/ 
+	public void testGetResourceConnection_FoundInWebInf() {
+		def urlStub = new java.net.URL("http://foo")
+		def servletContext = [
+		    getRealPath: { arg-> "realPath" + arg}, 
+		    getResource: {arg -> 
+				if (arg == "/WEB-INF/groovy/someresource") return urlStub
+				else return null
+			} ] as ServletContext
+
+		def servletConfig = [
+		    getServletContext: {servletContext}, 
+		    getInitParameter: {null}] as ServletConfig
+
+		//servlet config is used to find resources
+		servlet.init(servletConfig)    
+
+		def connection = servlet.getResourceConnection("someresource")
+
+		assert connection.getURL() == urlStub		
+	}
+	
+	/**
+	* Tests regex style resource replacement for first occurence. 
+	*/ 
+	public void testGetResourceConnection_Replace1stFooWithBar() {
+		def servletContext = [
+		    getRealPath: {arg -> "realPath" + arg}, 
+		    getResource: {arg -> 
+		      if (arg.startsWith("//")) arg=arg.substring(2)
+		      new URL("http://" + arg)}
+		] as ServletContext
+
+		def servletConfig = [
+		    getServletContext: {servletContext}, 
+		    getInitParameter: {arg ->
+		        //replace first occurence of foo resources with bar resources
+		        if (arg == "resource.name.regex") return "foo"
+		        else if (arg == "resource.name.replacement") return "bar" 
+		        else if (arg == "resource.name.replace.all") return "false"
+		        else  return null
+		    }] as ServletConfig
+
+		//servlet config is used to find resources
+		servlet.init(servletConfig)    
+
+		//replace first foo with bar in resources
+		def connection = servlet.getResourceConnection("/somefoo/foo")
+		//expecting http://somebar/foo
+		def actual = connection.getURL().toExternalForm() 
+		def expected = new URL("http://somebar/foo").toExternalForm()
+		assert actual == expected		
+	}
+
+	/**
+	* Tests regex style resource replacement for all occurences. 
+	*/ 
+	public void testGetResourceConnection_ReplaceAllFooWithBar() {
+		def servletContext = [
+		    getRealPath: {arg -> "realPath" + arg}, 
+		    getResource: {arg -> 
+		      if (arg.startsWith("//")) arg=arg.substring(2)
+		      new URL("http://" + arg)
+		}] as ServletContext
+
+		def servletConfig = [
+		    getServletContext: {servletContext}, 
+		    getInitParameter: {arg ->
+		        //replace all occurences of foo resources with bar resources
+		        if (arg == "resource.name.regex") return "foo"
+		        else if (arg == "resource.name.replacement") return "bar" 
+		        else if (arg == "resource.name.replace.all") return "true"
+		        else  return null
+		    }] as ServletConfig
+
+		//servlet config is used to find resources
+		servlet.init(servletConfig)    
+
+		//replace first foo with bar in resources
+		def connection = servlet.getResourceConnection("/somefoo/foo")
+		//expecting http://somebar/foo
+		def actual = connection.getURL().toExternalForm() 
+		def expected = new URL("http://somebar/bar").toExternalForm()
+		assert actual == expected		
+	}
+}
+
+//test specific subclass
+class ConcreteHttpServlet extends AbstractHttpServlet {}
+
diff --git a/groovy/src/test/groovy/servlet/GroovyServletTest.java b/groovy/src/test/groovy/servlet/GroovyServletTest.java
new file mode 100644
index 0000000..c8e2409
--- /dev/null
+++ b/groovy/src/test/groovy/servlet/GroovyServletTest.java
@@ -0,0 +1,50 @@
+package groovy.servlet;
+
+import org.jmock.Mock;
+import org.jmock.MockObjectTestCase;
+
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+
+public class GroovyServletTest extends MockObjectTestCase {
+
+    private ServletConfig config;
+    private ServletContext context;
+    private GroovyServlet servlet;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        //this.config = (ServletConfig) mock(ServletConfig.class).proxy();
+        //this.context = (ServletContext) mock(ServletContext.class).proxy();
+        this.servlet = new GroovyServlet();
+        //servlet.init(config);
+    }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testRequestGetCommandOK() {
+        Mock requestMock = mock(HttpServletRequest.class);
+        requestMock.expects(once()).method("getParameter").with(eq("command")).will(returnValue("SELECT..."));
+        HttpServletRequest request = (HttpServletRequest) requestMock.proxy();
+        String command = request.getParameter("command");
+        assertEquals("SELECT...", command);
+    }
+
+    //    public void testService() {
+    //        Mock requestMock = mock(HttpServletRequest.class);
+    //        Mock responseMock = mock(HttpServletResponse.class);
+    //        
+    //        HttpServletRequest request = (HttpServletRequest) requestMock.proxy();
+    //        HttpServletResponse response = (HttpServletResponse) responseMock.proxy();
+    //        try {
+    //            servlet.service(request, response);
+    //        } catch (Throwable t) {
+    //            t.printStackTrace();
+    //            fail(t.getMessage());
+    //        }
+    //    }
+
+}
diff --git a/groovy/src/test/groovy/servlet/ServletBindingTest.groovy b/groovy/src/test/groovy/servlet/ServletBindingTest.groovy
new file mode 100644
index 0000000..bff8d0f
--- /dev/null
+++ b/groovy/src/test/groovy/servlet/ServletBindingTest.groovy
@@ -0,0 +1,176 @@
+package groovy.servlet
+
+import javax.servlet.http.HttpSession
+import javax.servlet.http.HttpServletRequest
+import javax.servlet.http.HttpServletResponse
+import javax.servlet.ServletContext
+import javax.servlet.ServletOutputStream
+
+/**
+* This test case tests the ServletBinding class. 
+* 
+* @author Hamlet D'Arcy
+*/ 
+class ServletBindingTest extends GroovyTestCase {
+
+	def session = {} as HttpSession
+	def response = {} as HttpServletResponse
+	def context = {} as ServletContext
+	
+	void setUp() {
+		super.setUp()
+	}
+	
+	def makeDefaultBinding = { request ->
+		new ServletBinding(
+		    request as HttpServletRequest, 
+		    response as HttpServletResponse, 
+		    context as ServletContext
+		)
+	}
+	
+	def makeDefaultRequest  = {
+		[ getSession: {session}, 
+		getParameterNames: {new Vector().elements()}, 
+		getHeaderNames: {new Vector().elements()} ] as HttpServletRequest
+	}
+	
+	/**
+	* Tests that the constructor binds the correct default variables. 
+	*/
+	void testConstructor_VariableBindings() {
+		def request = makeDefaultRequest()
+		def binding = makeDefaultBinding(request)
+		
+		assert request == binding.getVariable("request")
+		assert response == binding.getVariable("response")
+		assert context == binding.getVariable("context")
+		assert context == binding.getVariable("application")
+		assert session == binding.getVariable("session")
+		assertTrue binding.getVariable("params").isEmpty()
+		assertTrue binding.getVariable("headers").isEmpty()
+	}
+	
+	/**
+	* Tests that the constructor binds request parameter names correctly. 
+	*/ 
+	void testConstructor_ParameterNameBindings() {
+
+		def parmNames = new Vector()
+		parmNames.add("name1")
+		parmNames.add("name2")
+
+		def request = [ 
+		    getSession: {session}, 
+			getHeaderNames: {new Vector().elements()}, 
+		    getParameterNames: {parmNames.elements()}, 
+			getParameterValues: { 
+		        //prepend string parm to known value to simulate attribute map
+		        String[] arr = new String[1]; 
+		        arr[0] = "value_for_" + it
+		        return arr} ] as HttpServletRequest
+
+		def binding = makeDefaultBinding(request)
+
+		def variables = binding.getVariable("params")
+		assert 2 == variables.size()
+		assert "value_for_name1" == variables.get("name1")
+		assert "value_for_name2" == variables.get("name2")	
+	}
+	
+	/**
+	* Tests that the constructor binds request header values correctly. 
+	*/ 
+	void testConstructor_HeaderBindings() {
+		def headerNames = new Vector()
+		headerNames.add("name1")
+		headerNames.add("name2")
+
+		def request = [ 
+		    getSession: {session}, 
+		    getParameterNames: {new Vector().elements()}, 
+		    getHeaderNames: {headerNames.elements()}, 
+		    getHeader: { 
+		        //prepend string parm to known value to simulate attribute map
+		        "value_for_" + it
+		        } ] as HttpServletRequest
+
+		def binding = makeDefaultBinding(request)
+
+		def variables = binding.getVariable("headers")
+		assert 2 == variables.size()
+		assert "value_for_name1" == variables.get("name1")
+		assert "value_for_name2" == variables.get("name2")		
+	}
+	
+	/**
+	* Tests the argument contract on getVariable. 
+	*/ 
+	void testGetVariable_Contract() {
+		
+		def request = makeDefaultRequest()
+		def binding = makeDefaultBinding(request)
+		
+		shouldFail(IllegalArgumentException) { binding.getVariable(null) }
+		shouldFail(IllegalArgumentException) { binding.getVariable("") }
+	}
+
+	/**
+	* Tests that getVariable works for the special key names. 
+	*/ 
+	void testGetVariable_ImplicitKeyNames() {
+		
+		def writer = new PrintWriter(new StringWriter())
+		def outputStream = new OutputStreamStub()
+
+		def response = [
+		    getWriter: {writer}, 
+		    getOutputStream: {outputStream} ] as HttpServletResponse
+
+		def request = makeDefaultRequest()
+
+		def binding = new ServletBinding(
+		    request as HttpServletRequest, 
+		    response as HttpServletResponse, 
+		    context as ServletContext
+		)
+
+		assert writer == binding.getVariable("out")
+		assert binding.getVariable("html") instanceof groovy.xml.MarkupBuilder
+		assert outputStream == binding.getVariable("sout")
+	}
+	
+	/**
+	* Tests the contract on setVarible(). 
+	*/ 
+	void testSetVariable_Contract() {
+		
+		def request = makeDefaultRequest()
+		def binding = makeDefaultBinding(request)
+		
+		shouldFail(IllegalArgumentException) { binding.setVariable(null, null) }
+		shouldFail(IllegalArgumentException) { binding.setVariable("", null) }
+		shouldFail(IllegalArgumentException) { binding.setVariable("out", null) }
+		shouldFail(IllegalArgumentException) { binding.setVariable("sout", null) }
+		shouldFail(IllegalArgumentException) { binding.setVariable("html", null) }		
+	}
+	
+	/**
+	* Tests setVariable. 
+	*/ 
+	void testSetVariable() {
+		def request = makeDefaultRequest()
+		def binding = makeDefaultBinding(request)
+		
+		binding.setVariable("var_name", "var_value")
+		def variables = binding.getVariables()
+		assert "var_value" == variables.get("var_name")
+	}
+}
+
+/**
+* Test specific sub class to stub out the ServletOutputStream class. 
+*/ 
+class OutputStreamStub extends ServletOutputStream {
+    void write(int x) {   }
+}
diff --git a/groovy/src/test/groovy/sql/GroovyRowResultTest.groovy b/groovy/src/test/groovy/sql/GroovyRowResultTest.groovy
new file mode 100644
index 0000000..1f27a9d
--- /dev/null
+++ b/groovy/src/test/groovy/sql/GroovyRowResultTest.groovy
@@ -0,0 +1,116 @@
+package groovy.sql

+

+import java.util.LinkedHashMap

+

+class GroovyRowResultTest extends GroovyTestCase {

+

+    void testMap() {

+        def row = createRow();

+        def row2 = createRow();

+        

+        /**

+         * Test for implementing Map

+         */ 

+        assert row instanceof Map, "GroovyRowResult doesn't implement Map interface"

+        

+        /**

+         * Test for put and accessing the new property

+         */ 

+        row.put("john","Doe")

+        assert row.john=="Doe"

+        assert row["john"]=="Doe"

+        assert row['john']=='Doe'

+        assert row.containsKey("john")

+        assert !row2.containsKey("john")

+        assert row.containsValue("Doe")

+        assert !row2.containsKey("Doe")

+

+        /**

+         * Test for equality (1) and size

+         */ 

+        assert row!=row2, "rows unexpectedly equal"

+        assert row.size()==7

+        assert row2.size()==6

+        

+        /**

+         * Test for remove, equality (2) and isEmpty (1)

+         */ 

+        row.remove("john")        

+        assert row==row2, "rows different after remove"

+        assert !row.isEmpty(), "row empty after remove"

+        

+        /**

+         * Test for clear, equality (3) and isEmpty (2)

+         */ 

+        row.clear()

+        row2.clear()

+        assert row==row2, "rows different after clear"

+        assert row.isEmpty(), "row not empty after clear"

+    }

+    

+    void testProperties() {

+        def row = createRow()

+        assert row.miXed == "quick"

+        assert row.lower == "brown"

+        assert row.upper == "fox"

+        assert row.UPPER == "fox"

+        

+        try {

+            assert row.LOWER == "brown"

+            assert false

+        } catch (MissingPropertyException mpe) {

+        } catch (Exception e) {

+            assert false

+        }

+

+        try {

+            println row.foo

+            assert false

+        } catch (MissingPropertyException mpe) {

+        } catch (Exception e) {

+            assert false

+        }

+

+        /**

+         * This is for GROOVY-1296

+         */

+        assert row.nullMixed==null

+        assert row[1]==null

+        assert row.nulllower==null

+        assert row[3]==null

+        assert row.NULLUPPER==null

+        assert row[5]==null

+        

+    } 

+    

+    void testOrder() {

+        def row = createRow()

+        assert row[0] == "quick" 

+        assert row[1] == null 

+        assert row[2] == "brown" 

+        assert row[3] == null 

+        assert row[4] == "fox" 

+        assert row[5] == null 

+        assert row[27] == null 

+        assert row[-1] == null 

+        assert row[-2] == "fox" 

+    }

+

+    protected def createRow() {

+        def map = new LinkedHashMap()

+        assert map != null , "failed to load LinkedHashMap class"

+

+        map.put("miXed", "quick")

+        map.put("nullMixed", null)

+        map.put("lower", "brown")

+        map.put("nulllower", null)

+        map.put("UPPER", "fox")

+        map.put("NULLUPPER", null)

+

+        def row = new GroovyRowResult(map)

+        assert row != null , "failed to load GroovyRowResult class"

+

+        return row

+    }

+    

+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/sql/Person.groovy b/groovy/src/test/groovy/sql/Person.groovy
new file mode 100644
index 0000000..ddcff90
--- /dev/null
+++ b/groovy/src/test/groovy/sql/Person.groovy
@@ -0,0 +1,8 @@
+package groovy.sql
+
+class Person {
+
+    def firstName
+    def lastName
+    def age
+}
diff --git a/groovy/src/test/groovy/sql/PersonTest.groovy b/groovy/src/test/groovy/sql/PersonTest.groovy
new file mode 100644
index 0000000..88e47a9
--- /dev/null
+++ b/groovy/src/test/groovy/sql/PersonTest.groovy
@@ -0,0 +1,61 @@
+package groovy.sql
+
+import javax.sql.DataSource
+
+class PersonTest extends GroovyTestCase {
+
+    def type
+    
+    void testFoo() {
+        def persons = createDataSet()
+		
+        def blogs = persons.findAll { it.lastName == "Bloggs" }
+		
+        assertSql(blogs, "select * from person where lastName = ?", ['Bloggs'])
+    }
+
+    void testWhereWithAndClause() {
+        def persons = createDataSet()
+        def blogs = persons.findAll { it.lastName == "Bloggs" }
+        def bigBlogs = blogs.findAll { it.size > 100 }
+        assertSql(bigBlogs, "select * from person where lastName = ? and size > ?", ['Bloggs', 100])
+    }
+
+    void testWhereClosureWithAnd() {
+        def persons = createDataSet()
+        def blogs = persons.findAll { it.size < 10 && it.lastName == "Bloggs" }
+        assertSql(blogs, "select * from person where (size < ? and lastName = ?)", [10, 'Bloggs'])
+    }
+ 
+    protected def compareFn(value) {
+        value > 1 && value < 10
+    }
+    
+    protected def assertSql(dataSet, expectedSql, expectedParams) {
+        def sql = dataSet.sql
+        def params = dataSet.parameters
+        assert sql == expectedSql
+        assert params == expectedParams
+    }
+    
+    protected DataSource createDataSource() {
+        def ds = new org.hsqldb.jdbc.jdbcDataSource()
+        ds.database = "jdbc:hsqldb:mem:foo" + getMethodName()
+        ds.user = 'sa'
+        ds.password = ''
+        return ds
+    }
+    
+    protected def createDataSet() {
+        def type = Person
+
+        assert type != null , "failed to load Person class"
+
+        def dataSource = createDataSource()
+        def sql = new Sql(dataSource)
+
+        return sql.dataSet(type)
+    }
+
+
+}
diff --git a/groovy/src/test/groovy/sql/SqlCompleteTest.groovy b/groovy/src/test/groovy/sql/SqlCompleteTest.groovy
new file mode 100644
index 0000000..a8e8aa5
--- /dev/null
+++ b/groovy/src/test/groovy/sql/SqlCompleteTest.groovy
@@ -0,0 +1,143 @@
+package groovy.sql
+
+class SqlCompleteTest extends TestHelper {
+
+    void testSqlQuery() {
+        def sql = createSql()
+        def results = [:]
+        sql.eachRow("select * from PERSON") { results.put(it.firstname, it.lastname) }
+        def expected = ["James":"Strachan", "Bob":"Mcwhirter", "Sam":"Pullara"]
+        assert results == expected
+    }
+    
+    void testSqlQueryWithWhereClause() {
+        def sql = createSql()
+        def foo = "drink"
+        def results = []
+        sql.eachRow("select * from FOOD where type=${foo}") { results.add(it.name) }
+        def expected = ["beer", "coffee"]
+        assert results == expected
+    }
+    
+    void testSqlQueryWithWhereClauseWith2Arguments() {
+        def sql = createSql()
+        def foo = "cheese"
+        def bar = "edam"
+        def results = []
+        sql.eachRow("select * from FOOD where type=${foo} and name != ${bar}") { results.add(it.name) }
+        def expected = ["brie", "cheddar"]
+        assert results == expected
+    }
+
+    void testSqlQueryWith2ParametersUsingQuestionMarkNotation() {
+        def sql = createSql()
+        def results = []
+        sql.eachRow("select * from FOOD where type=? and name != ?", ["cheese", "edam"]) { results.add(it.name) }
+        def expected = ["brie", "cheddar"]
+        assert results == expected
+    }
+
+    void testDataSet() {
+        def sql = createSql()
+        def results = []
+        def people = sql.dataSet("PERSON")
+        people.each { results.add(it.firstname) }
+        def expected = ["James", "Bob", "Sam"]
+        assert results == expected
+    }
+    
+    void testDataSetWithClosurePredicate() {
+        def sql = createSql()
+        def results = []
+        def food = sql.dataSet("FOOD")
+        food.findAll { it.type == "cheese" }.each { results.add(it.name) }
+        def expected = ["edam", "brie", "cheddar"]
+        assert results == expected
+    }
+    
+    void testUpdatingDataSet() {
+        def sql = createSql()
+        def results = []
+        def features = sql.dataSet("FEATURE")
+        features.each {
+            /** @todo HSQLDB doesn't yet support ResultSet updating
+            if (it.id == 1) {
+                it.name = it.name + " Rocks!"
+                println("Changing name to ${it.name}")
+            }
+            */
+            results.add(it.name)
+        }
+        def expected = ["GDO", "GPath", "GroovyMarkup"]
+        assert results == expected
+    }
+    
+    void testGStringToSqlConversion(){
+       def foo = 'loincloth'
+       def bar = 'wasteband'
+       def sql = createSql()
+       def expected = "A narrow ? supported by a ?!!"
+       def gstring = "A narrow ${foo} supported by a ${bar}!!"
+       def result = sql.asSql(gstring, gstring.values.toList())
+       assert result == expected
+    }
+    
+    void testExecuteUpdate(){
+        def foo='food-drink'
+        def food = 'food'
+        def drink = 'drink'
+        def bar='guinness'
+        def sql = createSql();
+        def expected = 0
+        def result = sql.executeUpdate("update FOOD set type=? where name=?",[foo,bar]);
+        assert result == expected
+        expected  = 1
+        result = sql.executeUpdate("insert into FOOD (type,name) values (${food},${bar})");
+    	assert result == expected
+        result = sql.executeUpdate("insert into FOOD (type,name) values (${drink},${bar})");
+    	assert result == expected
+        result = sql.executeUpdate("insert into FOOD (type,name) values ('drink','guinness')");
+    	assert result == expected
+        expected = 3
+        result = sql.executeUpdate("update FOOD set type=? where name=?",[foo,bar]);
+        assert result == expected
+    }
+    
+    void testFirstRow() {
+      def sql = createSql();
+      def row = sql.firstRow("select * from FOOD where type=? and name=?", ["cheese", "edam"])
+      assert row.type == "cheese"
+    }
+    
+    /** When no results, firstRow should return null */
+    void testFirstRowNoResults() {
+      def sql = createSql();
+      def row = sql.firstRow("select * from FOOD where type=?", ["nothing"])
+      assert row == null
+    }
+    
+    void testDataSetWithRows(){
+      def sql = createSql()
+      def dataSet = new DataSet(sql,"FOOD")
+      def rows = dataSet.rows()
+
+      //Expected names of the food items
+      def expected = ["edam", "brie", "cheddar", "beer", "coffee"]
+         
+      //Checking to make sure I got one item back
+      assert rows.size() == 5
+      def results = []
+      rows.each{results.add(it.name)}
+
+      //Checking to make sure the results retrieved match the expected results
+      assert results == expected
+    }
+        
+    void testDataSetWithFirstRow(){
+      def sql = createSql()
+      def dataSet = new DataSet(sql,"FOOD")
+      def result = dataSet.firstRow()
+      assert result!=null
+      assert result["name"]=="edam"
+    }
+}
diff --git a/groovy/src/test/groovy/sql/SqlCompleteWithoutDataSourceTest.groovy b/groovy/src/test/groovy/sql/SqlCompleteWithoutDataSourceTest.groovy
new file mode 100644
index 0000000..2fbd11a
--- /dev/null
+++ b/groovy/src/test/groovy/sql/SqlCompleteWithoutDataSourceTest.groovy
@@ -0,0 +1,16 @@
+package groovy.sql
+
+import java.sql.DriverManager
+
+/**
+ * Tests the use of the Sql class using just a Connection 
+ * rather than a DataSource
+ */
+class SqlCompleteWithoutDataSourceTest extends SqlCompleteTest {
+    
+    protected def newSql(String uri) {
+        def driver = org.hsqldb.jdbcDriver
+        println("Loading driver ${driver}")
+        return new Sql(DriverManager.getConnection(uri))
+    }
+}
diff --git a/groovy/src/test/groovy/sql/SqlRowsTest.groovy b/groovy/src/test/groovy/sql/SqlRowsTest.groovy
new file mode 100644
index 0000000..7380323
--- /dev/null
+++ b/groovy/src/test/groovy/sql/SqlRowsTest.groovy
@@ -0,0 +1,77 @@
+package groovy.sql
+
+class SqlRowsTest extends TestHelper {
+
+    void testFirstRowWithPropertyName() {
+        def sql = createSql()
+
+        def results = sql.firstRow("select firstname, lastname from PERSON where id=1").firstname
+        def expected = "James"
+        assert results == expected
+    }
+
+    void testFirstRowWithPropertyNameAndParams() {
+        def sql = createSql()
+
+        def results = sql.firstRow("select firstname, lastname from PERSON where id=?", [1]).lastname
+        def expected = "Strachan"
+        assert results == expected
+    }
+
+    void testFirstRowWithPropertyNumber() {
+        def sql = createSql()
+
+        def results = sql.firstRow("select firstname, lastname from PERSON where id=1")[0]
+        def expected = "James"
+        assert results == expected
+    }
+    
+    void testFirstRowWithPropertyNumberAndParams() {
+        def sql = createSql()
+
+        def results = sql.firstRow("select firstname, lastname from PERSON where id=?", [1])[0]
+        def expected = "James"
+        assert results == expected
+    }
+    
+    void testAllRowsWithPropertyNumber() {
+        def sql = createSql()
+
+        def results = sql.rows("select firstname, lastname from PERSON where id=1 or id=2 order by id")
+        assert results[0][0] == "James"
+        assert results[0][1] == "Strachan"
+        assert results[1][0] == "Bob"
+        assert results[1][1] == "Mcwhirter"
+    }
+
+    void testAllRowsWithPropertyNumberAndParams() {
+        def sql = createSql()
+
+        def results = sql.rows("select firstname, lastname from PERSON where id=? or id=? order by id", [1,2])
+        assert results[0][0] == "James"
+        assert results[0][1] == "Strachan"
+        assert results[1][0] == "Bob"
+        assert results[1][1] == "Mcwhirter"
+    }
+
+    void testAllRowsWithPropertyName() {
+        def sql = createSql()
+
+        def results = sql.rows("select firstname, lastname from PERSON where id=1 or id=2 order by id")
+        assert results[0].firstname == "James"
+        assert results[0].lastname == "Strachan"
+        assert results[1].firstname == "Bob"
+        assert results[1].lastname == "Mcwhirter"
+    }
+
+    void testAllRowsWithPropertyNameAndParams() {
+        def sql = createSql()
+
+        def results = sql.rows("select firstname, lastname from PERSON where id=? or id=? order by id", [1,2])
+        assert results[0].firstname == "James"
+        assert results[0].lastname == "Strachan"
+        assert results[1].firstname == "Bob"
+        assert results[1].lastname == "Mcwhirter"
+    }
+
+}
diff --git a/groovy/src/test/groovy/sql/SqlTest.groovy b/groovy/src/test/groovy/sql/SqlTest.groovy
new file mode 100644
index 0000000..6ddb259
--- /dev/null
+++ b/groovy/src/test/groovy/sql/SqlTest.groovy
@@ -0,0 +1,118 @@
+package groovy.sql
+
+/**
+ * This is more of a sample program than a unit test and is here as an easy
+ * to read demo of GDO. The actual full unit test case is in SqlCompleteTest
+ */
+class SqlTest extends GroovyTestCase {
+
+    private sql
+
+    void setUp() {
+        sql = createSql()
+    }
+
+    void testSqlQuery() {
+        sql.eachRow("select * from PERSON") { println("Hello ${it.firstname} ${it.lastname}") }
+    }
+
+    void testQueryUsingColumnIndex() {
+        def answer = null
+        sql.eachRow("select count(*) from PERSON") { answer = it[0] }
+        println "Found the count of ${answer}"
+        assert answer == 3
+    }
+
+    void testQueryUsingNegativeColumnIndex() {
+        def first = null
+        def last = null
+        sql.eachRow("select firstname, lastname from PERSON where firstname='James'") { row ->
+            first = row[-2]
+            last = row[-1]
+        }
+        println "Found name ${first} ${last}"
+        assert first == "James"
+        assert last == "Strachan"
+    }
+
+    void testSqlQueryWithWhereClause() {
+        def foo = "drink"
+        sql.eachRow("select * from FOOD where type=${foo}") { println("Drink ${it.name}") }
+    }
+
+    void testSqlQueryWithWhereClauseWith2Arguments() {
+        def foo = "cheese"
+        def bar = "edam"
+        sql.eachRow("select * from FOOD where type=${foo} and name != ${bar}") { println("Found cheese ${it.name}") }
+    }
+
+    void testSqlQueryWithIncorrectlyQuotedDynamicExpressions() {
+        def foo = "cheese"
+        def bar = "edam"
+        sql.eachRow("select * from FOOD where type='${foo}' and name != '${bar}'") { println("Found cheese ${it.name}") }
+    }
+
+    void testDataSet() {
+        def people = sql.dataSet("PERSON")
+        people.each { println("Hey ${it.firstname}") }
+    }
+
+    void testDataSetWithClosurePredicate() {
+        def food = sql.dataSet("FOOD")
+        food.findAll { it.type == "cheese" }.each { println("Cheese ${it.name}") }
+    }
+
+    void testExecuteUpdate(){
+        def foo='food-drink'
+        def bar='guinness'
+        def nRows = sql.executeUpdate("update FOOD set type=? where name=?",[foo,bar]);
+        if (nRows == 0){
+            sql.executeUpdate("insert into FOOD (type,name) values (${foo},${bar})");
+        }
+    }
+
+    void testExecuteInsert() {
+        def foo = 'food-drink'
+        def bar = 'guiness'
+        if (sql.dataSource.connection.metaData.supportsGetGeneratedKeys()) {
+            def keys = sql.executeInsert('insert into FOOD (type,name) values (?,?)', [foo,bar])
+            assert 1 == keys.size()
+        } else {
+            def count = sql.executeUpdate('insert into FOOD (type,name) values (?,?)', [foo,bar])
+            assert 1 == count
+        }
+    }
+
+    void testMetaData() {
+      sql.eachRow('select * from PERSON') {
+	       assert it[0] != null
+	       assert it.getMetaData() != null
+	  }
+    }
+
+    protected def createSql() {
+        def ds = new org.hsqldb.jdbc.jdbcDataSource()
+        ds.database = "jdbc:hsqldb:mem:foo" + getMethodName()
+        ds.user = 'sa'
+        ds.password = ''
+        def sql = new Sql(ds)
+
+        sql.execute("create table PERSON ( firstname varchar, lastname varchar )")
+        sql.execute("create table FOOD ( type varchar, name varchar)")
+
+        // now let's populate the datasets
+        def people = sql.dataSet("PERSON")
+        people.add( firstname:"James", lastname:"Strachan" )
+        people.add( firstname:"Bob", lastname:"Mcwhirter" )
+        people.add( firstname:"Sam", lastname:"Pullara" )
+
+        def food = sql.dataSet("FOOD")
+        food.add( type:"cheese", name:"edam" )
+        food.add( type:"cheese", name:"brie" )
+        food.add( type:"cheese", name:"cheddar" )
+        food.add( type:"drink", name:"beer" )
+        food.add( type:"drink", name:"coffee" )
+
+        return sql
+    }
+}
diff --git a/groovy/src/test/groovy/sql/SqlWithBuilderTest.groovy b/groovy/src/test/groovy/sql/SqlWithBuilderTest.groovy
new file mode 100644
index 0000000..07175c4
--- /dev/null
+++ b/groovy/src/test/groovy/sql/SqlWithBuilderTest.groovy
@@ -0,0 +1,26 @@
+package groovy.sql
+
+import groovy.xml.MarkupBuilder 
+
+/**
+ * @author Brian McCallister
+ * @version $Revision$
+ */
+class SqlWithBuilderTest extends TestHelper {
+
+    void testSqlQuery() {
+         def sql = createSql()
+         println "Created ${sql}"
+        
+         def doc = new MarkupBuilder()
+        
+         doc.people {
+             sql.eachRow("select * from PERSON") {
+                 doc.person(first: it.firstname, last: it.lastname, location: it.location_name)
+             }
+         }
+        
+         sql.close()
+    }
+}
+
diff --git a/groovy/src/test/groovy/sql/SqlWithTypedResultsTest.groovy b/groovy/src/test/groovy/sql/SqlWithTypedResultsTest.groovy
new file mode 100644
index 0000000..1724416
--- /dev/null
+++ b/groovy/src/test/groovy/sql/SqlWithTypedResultsTest.groovy
@@ -0,0 +1,37 @@
+package groovy.sql
+
+import groovy.xml.MarkupBuilder 
+
+/**
+ * @author Thomas Heller
+ * @version $Revision$
+ */
+class SqlWithTypedResultsTest extends TestHelper {
+
+    void testSqlQuery() {
+         def sql = createEmptySql()
+         
+         sql.execute("create table groovytest ( anint integer, astring varchar )");
+
+         def groovytest = sql.dataSet("groovytest")
+         groovytest.add( anint:1, astring:"Groovy" )
+         groovytest.add( anint:2, astring:"rocks" )
+
+         // this line messes up things:
+         /** @todo this fails
+         Integer id
+		 */
+         Integer id = 0
+		 
+         sql.eachRow("SELECT * FROM groovytest ORDER BY anint") { 
+         	println "found ${it.astring} for id ${it.anint}"
+         	
+         	id = it.anint 
+         }
+
+         assert id == 2
+        
+         sql.close()
+    }
+}
+
diff --git a/groovy/src/test/groovy/sql/TestHelper.groovy b/groovy/src/test/groovy/sql/TestHelper.groovy
new file mode 100644
index 0000000..188356f
--- /dev/null
+++ b/groovy/src/test/groovy/sql/TestHelper.groovy
@@ -0,0 +1,65 @@
+package groovy.sql
+
+class TestHelper extends GroovyTestCase {
+
+    static def counter = 1
+    
+    static Sql makeSql() {
+        def foo = new TestHelper()
+        return foo.createSql()
+    }
+    
+    
+    protected def createEmptySql() {
+        return newSql(getURI())
+    }
+    
+    protected def createSql() {
+        def sql = newSql(getURI())
+        
+        try {
+           sql.execute("drop table PERSON")
+           sql.execute("drop table FOOD")
+           sql.execute("drop table FEATURE")
+        } catch(Exception e){}
+        
+        sql.execute("create table PERSON ( firstname varchar, lastname varchar, id integer, location_id integer, location_name varchar )")     
+        sql.execute("create table FOOD ( type varchar, name varchar)")
+        sql.execute("create table FEATURE ( id integer, name varchar)")
+        
+        // now lets populate the datasets
+        def people = sql.dataSet("PERSON")
+        people.add( firstname:"James", lastname:"Strachan", id:1, location_id:10, location_name:'London' )
+        people.add( firstname:"Bob", lastname:"Mcwhirter", id:2, location_id:20, location_name:'Atlanta' )
+        people.add( firstname:"Sam", lastname:"Pullara", id:3, location_id:30, location_name:'California' )
+        
+        def food = sql.dataSet("FOOD")
+        food.add( type:"cheese", name:"edam" )
+        food.add( type:"cheese", name:"brie" )
+        food.add( type:"cheese", name:"cheddar" )
+        food.add( type:"drink", name:"beer" )
+        food.add( type:"drink", name:"coffee" )
+        
+        def features = sql.dataSet("FEATURE")
+        features.add( id:1, name:'GDO' )
+        features.add( id:2, name:'GPath' )
+        features.add( id:3, name:'GroovyMarkup' )
+        return sql
+    }
+    
+    protected def getURI() {
+		def answer = "jdbc:hsqldb:mem:foo"
+		def name = getMethodName()
+		if (name == null) {name = ""}
+		name += counter++
+		return answer + name
+    }
+    
+    protected def newSql(String uri) {
+	    def ds = new org.hsqldb.jdbc.jdbcDataSource()
+        ds.database = uri
+        ds.user = 'sa'
+        ds.password = ''
+	    return new Sql(ds)
+    }
+}
diff --git a/groovy/src/test/groovy/swing/SwingBuilderBindingsTest.groovy b/groovy/src/test/groovy/swing/SwingBuilderBindingsTest.groovy
new file mode 100644
index 0000000..563ec3a
--- /dev/null
+++ b/groovy/src/test/groovy/swing/SwingBuilderBindingsTest.groovy
@@ -0,0 +1,239 @@
+/*

+ * $Id$

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ *

+ */

+

+package groovy.swing

+

+import javax.swing.DefaultBoundedRangeModel

+import javax.swing.DefaultButtonModel

+import javax.swing.JFrame

+import javax.swing.text.PlainDocument

+

+public class SwingBuilderBindingsTest extends GroovyTestCase {

+

+    private Boolean isHeadless

+

+    private Boolean isHeadless() {

+        if (isHeadless != null)

+          return isHeadless;

+

+        try {

+            new JFrame("testing")

+            isHeadless = false

+        } catch (java.awt.HeadlessException he) {

+            isHeadless = true

+        }

+        return isHeadless

+    }

+

+    public void testSliderValueBinding() {

+        if (isHeadless()) return

+        SwingBuilder swing = new SwingBuilder()

+

+        swing.actions() {

+            slider(id:'sl')

+            textField(id:'txt', text:bind(source:sl, sourceProperty:'value', id:'binding'))

+            slider(id:'slReverse', value:bind(source:sl, sourceProperty:'value', id:'bindingReverse'))

+            // need to use a second slider for reverse test, because string->int autobox, not so happy

+        }

+

+        swing.sl.value = 10

+        assert swing.txt.text == '10'

+        swing.sl.value = 95

+        assert swing.txt.text == '95'

+

+        swing.binding.rebind()

+        swing.sl.value = 42

+        assert swing.txt.text == '42'

+        swing.binding.unbind()

+        swing.sl.value = 13

+        assert swing.txt.text == '42'

+        swing.binding.bind()

+        assert swing.txt.text == '42'

+        swing.binding.update()

+        assert swing.txt.text == '13'

+

+        swing.sl.model = new DefaultBoundedRangeModel(30, 1, 20, 40)

+        assert swing.txt.text == '30'

+

+        // first make sure we've been fireing

+        assert swing.slReverse.value == 30

+        swing.slReverse.value = 21

+        swing.bindingReverse.reverseUpdate()

+        assert swing.sl.value == 21

+    }

+

+    public void testTextFieldTextBinding() {

+        if (isHeadless()) return

+        SwingBuilder swing = new SwingBuilder()

+

+        swing.actions() {

+            textField('Bind', id:'txts')

+            textField(id:'txt', text:bind(source:txts, sourceProperty:'text', id:'binding'))

+        }

+

+        assert swing.txt.text == 'Bind'

+        swing.txts.text = 'Text'

+        assert swing.txt.text == 'Text'

+        swing.txts.text = 'Easily'

+        assert swing.txt.text == 'Easily'

+

+        swing.binding.rebind()

+        swing.txts.text = 'With'

+        assert swing.txt.text == 'With'

+        swing.binding.unbind()

+        swing.txts.text = 'bind()'

+        assert swing.txt.text == 'With'

+        swing.binding.bind()

+        assert swing.txt.text == 'With'

+        swing.binding.update()

+        assert swing.txt.text == 'bind()'

+

+        swing.txt.text = 'reversal'

+        swing.binding.reverseUpdate()

+        assert swing.txts.text == 'reversal'

+

+        PlainDocument doc = new PlainDocument()

+        doc.insertString(0, '{}', null)

+        swing.txts.document = doc

+        assert swing.txt.text == '{}'

+    }

+

+    public void testCheckboxSelectedBinding() {

+        if (isHeadless()) return

+        SwingBuilder swing = new SwingBuilder()

+

+        swing.actions() {

+            checkBox(id:'cb')

+            textField(id:'txt', enabled:bind(source:cb, sourceProperty:'selected', id:'binding'))

+        }

+

+        assert swing.txt.enabled == swing.cb.selected

+        swing.cb.selected = !swing.cb.selected

+        assert swing.txt.enabled == swing.cb.selected

+        swing.cb.selected = !swing.cb.selected

+        assert swing.txt.enabled == swing.cb.selected

+

+        swing.binding.rebind()

+        swing.cb.selected = !swing.cb.selected

+        assert swing.txt.enabled == swing.cb.selected

+        swing.binding.unbind()

+        swing.cb.selected = !swing.cb.selected

+        assert swing.txt.enabled != swing.cb.selected

+        swing.binding.bind()

+        assert swing.txt.enabled != swing.cb.selected

+        swing.binding.update()

+        assert swing.txt.enabled == swing.cb.selected

+

+        DefaultButtonModel md = new DefaultButtonModel()

+        md.enabled = !swing.txt.enabled

+        swing.cb.model = md

+        assert swing.txt.enabled == swing.cb.selected

+

+        swing.txt.enabled = !swing.txt.enabled

+        swing.binding.reverseUpdate()

+        assert swing.txt.enabled == swing.cb.selected

+

+    }

+

+    public void testEventBinding() {

+        if (isHeadless()) return

+        SwingBuilder swing = new SwingBuilder()

+

+        swing.actions() {

+            button('Button!', id:'b')

+            textField(id:'txt', text:bind(source:b, sourceEvent:'actionPerformed', sourceValue:{b.text}))

+        }

+

+        assert swing.txt.text == 'Button!'

+        swing.b.text = 'Pressed!'

+        // not pressed yet...

+        assert swing.txt.text == 'Button!'

+        swing.b.doClick()

+        //ok, now it's pressed

+        assert swing.txt.text == 'Pressed!'

+    }

+

+    public void testPropertyBinding() {

+        if (isHeadless()) return

+        SwingBuilder swing = new SwingBuilder()

+

+        swing.actions() {

+            checkBox('Button!', id:'cb')

+            textField(id:'txt', enabled:bind(source:cb, sourceProperty:'enabled', id:'binding'))

+        }

+        assert swing.txt.enabled == swing.cb.enabled

+        swing.cb.enabled = !swing.cb.enabled

+        assert swing.txt.enabled == swing.cb.enabled

+        swing.cb.enabled = !swing.cb.enabled

+        assert swing.txt.enabled == swing.cb.enabled

+

+        swing.binding.rebind()

+        swing.cb.enabled = !swing.cb.enabled

+        assert swing.txt.enabled == swing.cb.enabled

+        swing.binding.unbind()

+        swing.cb.enabled = !swing.cb.enabled

+        assert swing.txt.enabled != swing.cb.enabled

+        swing.binding.bind()

+        assert swing.txt.enabled != swing.cb.enabled

+        swing.binding.update()

+        assert swing.txt.enabled == swing.cb.enabled

+

+        swing.txt.enabled = !swing.txt.enabled

+        swing.binding.reverseUpdate()

+        assert swing.txt.enabled == swing.cb.enabled

+

+        DefaultButtonModel md = new DefaultButtonModel()

+        md.enabled = !swing.txt.enabled

+        swing.cb.model = md

+        assert swing.txt.enabled == swing.cb.enabled

+    }

+

+

+    public void testModel() {

+        if (isHeadless()) return

+        SwingBuilder swing = new SwingBuilder()

+

+        def bean = new org.codehaus.groovy.runtime.DummyBean()

+

+        swing.model(bean, id:'dummyBean')

+

+        // test initial binding

+        swing.textField(id:'textField', text:swing.dummyBean.name)

+        assert swing.textField.text == bean.name

+

+        // test no live update by default

+        bean.name = 'Jochen'

+        assert swing.textField.text != bean.name

+

+        // test for update on bean change

+        bean = new org.codehaus.groovy.runtime.DummyBean()

+        bean.name = 'Alex'

+        swing.dummyBean.setModel(bean)

+        assert swing.textField.text == bean.name

+

+

+        //test for auto-update

+        swing.model(bean, id:'boundDummyBean', bind:true)

+        swing.textField(id:'boundTextField', text:swing.boundDummyBean.name)

+        assert swing.boundTextField.text == bean.name

+        bean.name = 'Danno'

+        assert swing.boundTextField.text == bean.name

+

+        // old model binding could be listening...

+        assert swing.textField.text != bean.name

+    }

+}

diff --git a/groovy/src/test/groovy/swing/SwingBuilderTest.groovy b/groovy/src/test/groovy/swing/SwingBuilderTest.groovy
new file mode 100644
index 0000000..5026135
--- /dev/null
+++ b/groovy/src/test/groovy/swing/SwingBuilderTest.groovy
@@ -0,0 +1,1497 @@
+/*
+ * $Id:  $
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package groovy.swing
+
+import javax.swing.JPopupMenu.Separator as JPopupMenu_Separator
+import javax.swing.JToolBar.Separator as JToolBar_Separator
+
+import groovy.ui.Console
+import java.awt.*
+import java.awt.event.InputEvent
+import java.awt.event.KeyEvent
+import java.text.SimpleDateFormat
+import javax.swing.*
+import javax.swing.border.TitledBorder
+import javax.swing.plaf.metal.MetalLookAndFeel
+import javax.swing.text.DateFormatter
+import javax.swing.text.NumberFormatter
+
+class SwingBuilderTest extends GroovyTestCase {
+
+    private static boolean headless
+
+    static {
+        try {
+            new JFrame("testing")
+            headless = false
+        } catch (java.awt.HeadlessException he) {
+            headless = true
+        }
+    }
+
+    void testNamedWidgetCreation() {
+        if (headless) return
+
+        def topLevelWidgets = [
+            frame: [JFrame.class, true],
+            dialog: [JDialog.class, true],
+            window: [JWindow.class, false],
+            fileChooser: [JFileChooser.class, false],
+            optionPane: [JOptionPane.class, false]
+        ]
+        def swing = new SwingBuilder()
+        topLevelWidgets.each{ name, widgetInfo ->
+            if (widgetInfo[1])
+                swing."$name"(id:"${name}Id".toString(), title:"This is my $name")
+            else
+                swing."$name"(id:"${name}Id".toString())
+            def widget = swing."${name}Id"
+            assert widget.class == widgetInfo[0]
+            if (widgetInfo[1]) assert widget.title == "This is my $name"
+        }
+    }
+
+    void testLayoutCreation() {
+        if (headless) return
+
+        def layouts = [
+            borderLayout: BorderLayout.class,
+            cardLayout: CardLayout.class,
+            flowLayout: FlowLayout.class,
+            gridBagLayout: GridBagLayout.class,
+            gridLayout: GridLayout.class,
+//            overlayLayout: OverlayLayout.class,
+            springLayout: SpringLayout.class,
+//            boxLayout: BoxLayout.class
+        ]
+        def swing = new SwingBuilder()
+        layouts.each{ name, expectedLayoutClass ->
+            def frame = swing.frame(){
+               "$name"()
+            }
+            assert frame.contentPane.layout.class == expectedLayoutClass
+        }
+    }
+
+    void testWidgetCreation() {
+        if (headless) return
+
+        def widgets = [
+            button: JButton.class,
+            checkBox: JCheckBox.class,
+            checkBoxMenuItem: JCheckBoxMenuItem.class,
+            colorChooser: JColorChooser.class,
+            comboBox: JComboBox.class,
+            desktopPane: JDesktopPane.class,
+            editorPane: JEditorPane.class,
+            formattedTextField: JFormattedTextField.class,
+            internalFrame: JInternalFrame.class,
+            label: JLabel.class,
+            layeredPane: JLayeredPane.class,
+            list: JList.class,
+            menu: JMenu.class,
+            menuBar: JMenuBar.class,
+            menuItem: JMenuItem.class,
+            panel: JPanel.class,
+            passwordField: JPasswordField.class,
+            popupMenu: JPopupMenu.class,
+            progressBar: JProgressBar.class,
+            radioButton: JRadioButton.class,
+            radioButtonMenuItem: JRadioButtonMenuItem.class,
+            scrollBar: JScrollBar.class,
+            scrollPane: JScrollPane.class,
+            separator: JSeparator.class,
+            slider: JSlider.class,
+            spinner: JSpinner.class,
+            splitPane: JSplitPane.class,
+            tabbedPane: JTabbedPane.class,
+            table: JTable.class,
+            textArea: JTextArea.class,
+            textPane: JTextPane.class,
+            textField: JTextField.class,
+            toggleButton: JToggleButton.class,
+            toolBar: JToolBar.class,
+            tree: JTree.class,
+            viewport: JViewport.class,
+        ]
+        def swing = new SwingBuilder()
+        widgets.each{ name, expectedLayoutClass ->
+            def frame = swing.frame(){
+               "$name"(id:"${name}Id".toString())
+            }
+            assert swing."${name}Id".class == expectedLayoutClass
+        }
+    }
+
+    void testButtonGroupOnlyForButtons() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def buttonGroup = swing.buttonGroup()
+        shouldFail(MissingPropertyException) {
+            swing.label(buttonGroup:buttonGroup)
+        }
+    }
+
+    void testWidget() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def label = swing.label("By Value:")
+        def widgetByValue = swing.widget(label)
+        assert widgetByValue != null
+        def widgetByLabel = swing.widget(widget: label)
+        assert widgetByLabel != null
+    }
+
+    void testTableColumn() {
+        if (headless) return
+
+        // TODO is this required?
+        def swing = new SwingBuilder()
+        swing.table{
+            tableColumn()
+        }
+    }
+
+    void testSplitPane() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def buttonGroup = swing.buttonGroup()
+        def frame = swing.frame(){
+            splitPane(id:'hsplit', orientation: JSplitPane.HORIZONTAL_SPLIT) {
+                button(id:'left', buttonGroup:buttonGroup)
+                button(id:'right', buttonGroup:buttonGroup)
+            }
+            splitPane(id:'vsplit', orientation: JSplitPane.VERTICAL_SPLIT) {
+                button(id:'top')
+                button(id:'bottom')
+            }
+        }
+        assert swing.hsplit.leftComponent == swing.left
+        assert swing.hsplit.rightComponent == swing.right
+        assert swing.vsplit.topComponent == swing.top
+        assert swing.vsplit.bottomComponent == swing.bottom
+    }
+
+    void testNestedWindows() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        swing.window (id:'root') {
+            window(id:'child1')
+            frame(id:'child2') {
+                window(id:'child2_1')
+            }
+        }
+        swing.window (id:'root2')
+
+        //assert swing.root.owner == null;
+        assert swing.child1.owner == swing.root
+        assert swing.child2.owner == null // it's a frame, frames have no owners
+        assert swing.child2_1.owner == swing.child2
+
+        assert swing.root2.owner != swing.root
+        assert swing.root2.owner != swing.child1
+        assert swing.root2.owner != swing.child2
+        assert swing.root2.owner != swing.child2_1
+
+        swing.panel {
+            swing.frame()
+            swing.window()
+            swing.dialog()
+        }
+    }
+
+    void testFrames() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        swing.frame(id:'frame') {
+            button('test', id:'button', defaultButton:true)
+        }
+        assert swing.frame.rootPane.defaultButton == swing.button
+        assert swing.button.defaultButton
+    }
+
+    void testDialogs() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        swing.dialog(id:'d1')
+        swing.frame(id:'f') { dialog(id:'fd') }
+        swing.dialog(id:'d') { dialog(id:'dd') }
+        swing.dialog(id:'d2')
+
+        //assert swing.d1.owner == null
+        assert swing.fd.owner == swing.f
+        assert swing.dd.owner == swing.d
+
+        assert swing.d2.owner != swing.dd
+        assert swing.d2.owner != swing.fd
+        assert swing.d2.owner != swing.d
+        assert swing.d2.owner != swing.f
+        assert swing.d2.owner != swing.d1
+    }
+
+    void testWindows() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        swing.window()
+        swing.frame{ window() }
+        swing.dialog{ window() }
+    }
+
+    void testNodeCreation() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def frame = swing.frame(){
+            // 4 valid parameter combinations
+            button()
+            button('Text')
+            button(label:'Label')
+            button(label:'Label', 'Text')
+        }
+        shouldFail(){
+            frame = swing.frame(){
+                // invalid parameter
+                button(new Date())
+            }
+        }
+    }
+
+    void testSetMnemonic() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        swing.panel(layout:new BorderLayout()) {
+            label(id:'label0', text:'Name0', mnemonic:48)
+            label(id:'label1', text:'Name1', mnemonic:'N')
+        }
+        int expected0 = '0'
+        int expected1 = 'N'
+        assert swing.label0.displayedMnemonic == expected0
+        assert swing.label1.displayedMnemonic == expected1
+        swing.menuBar{
+            menu{
+                menuItem {
+                    action(id:'actionId', name:'About', mnemonic:'A')
+                }
+            }
+        }
+        int expected2 = 'A'
+        int actual = swing.actionId.getValue(Action.MNEMONIC_KEY)
+        assert  actual == expected2
+    }
+
+    void testBuilderProperties() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        assert swing.class.name == 'groovy.swing.SwingBuilder'
+    }
+
+    void testFormattedTextField() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def dummy = new Date()
+        def field = swing.formattedTextField(value:dummy)
+        assert field.value == dummy
+        assert field.formatter.class == DateFormatter.class
+        def dummyFormatter = new SimpleDateFormat()
+        field = swing.formattedTextField(format:dummyFormatter)
+        assert field.formatter.class == DateFormatter.class
+        field = swing.formattedTextField()
+        field.value = 3
+        assert field.formatter.class == NumberFormatter.class
+    }
+
+    void testTabbedPane() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        swing.tabbedPane(id:'tp') {
+            panel(id:'p1', name:'Title 1')
+            panel(id:'p2')
+            panel(id:'p3', title:'Title 3')
+            panel(id:'p4', title:'Title 4', name:'Name 4')
+            panel(id:'p5', title:'Title 5', tabIcon: imageIcon(Console.ICON_PATH, id:'i5'))
+            panel(id:'p6', title:'Title 6', tabDisabledIcon: imageIcon(Console.ICON_PATH, id:'i6'))
+            panel(id:'p7', title:'Title 7', tabToolTip:'tip 7')
+            panel(id:'p8', title:'Title 8', tabBackground:Color.GREEN)
+            panel(id:'p9', title:'Title 9', tabForeground:Color.GREEN)
+            panel(id:'pA', title:'Title A', tabEnabled:false)
+            panel(id:'pB', title:'Title B', tabMnemonic: 'T');
+            panel(id:'pC', title:'Title C', tabDisplayedMnemonicIndex: 2);
+        }
+
+        assert swing.tp.tabCount == 12 
+        assert swing.tp.indexOfComponent(swing.p1) == 0
+        assert swing.tp.indexOfComponent(swing.p2) == 1
+        assert swing.tp.indexOfComponent(swing.p3) == 2
+        assert swing.tp.indexOfComponent(swing.p4) == 3
+        assert swing.tp.indexOfTab('Title 1') == 0
+        assert swing.tp.indexOfTab('Title 3') == 2
+        assert swing.tp.indexOfTab('Title 4') == 3
+        assert swing.tp.getIconAt(4) == swing.i5
+        assert swing.tp.getDisabledIconAt(5) == swing.i6
+        assert swing.tp.getToolTipTextAt(6) == 'tip 7'
+        assert swing.tp.getBackgroundAt(7) == Color.GREEN
+        assert swing.tp.getForegroundAt(8) == Color.GREEN
+        assert swing.tp.isEnabledAt(9) == false
+        assert swing.tp.getMnemonicAt(10) == 0x54
+        assert swing.tp.getDisplayedMnemonicIndexAt(11) == 2
+
+        swing.tabbedPane(id:'tp', selectedComponent:swing.p2) {
+            panel(p1, name:'Title 1')
+            panel(p2)
+            panel(p3)
+        }
+        assert swing.tp.selectedIndex == 1
+        assert swing.tp.selectedComponent == swing.p2
+
+        swing.tabbedPane(id:'tp', selectedIndex:1) {
+            panel(p1, name:'Title 1')
+            panel(p2)
+            panel(p3)
+        }
+        assert swing.tp.selectedIndex == 1
+        assert swing.tp.selectedComponent == swing.p2
+
+    }
+
+    void testComboBox() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        Object[] objects = ['a','b']
+        def list = ['c', 'd', 'e']
+        def vector = new Vector(['f', 'g', 'h', 'i'])
+        assert swing.comboBox(items:objects).itemCount == 2
+        assert swing.comboBox(items:list).itemCount == 3
+        assert swing.comboBox(items:vector).itemCount == 4
+    }
+
+    void testMisplacedActionsAreIgnored() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        // labels don't support actions; should be ignored
+        swing.label{
+            action(id:'actionId', Name:'about', mnemonic:'A', closure:{x->x})
+            map()
+        }
+        swing.panel{
+            borderLayout{
+                // layouts don't support actions, will be ignored
+                action(id:'actionId')
+            }
+        }
+    }
+
+    void testBoxLayout() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def message = shouldFail{
+            swing.boxLayout()
+        }
+        assert message.contains('Must be nested inside a Container')
+        // default is X_AXIS
+        swing.panel(id:'panel'){
+            boxLayout(id:'layout1')
+        }
+        // can also set explicit axis
+        swing.frame(id:'frame'){
+            boxLayout(id:'layout2', axis:BoxLayout.Y_AXIS)
+        }
+    }
+
+    void testKeystrokesWithinActions() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        swing.panel{
+            button(id:'buttonId'){
+                action(id:'action1', keyStroke:'ctrl W')
+                action(id:'action2',
+                    keyStroke:KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.ALT_MASK))
+            }
+        }
+        def component = swing.buttonId
+        def expected1 = swing.action1.toString()
+        def expected2 = swing.action2.toString()
+        def keys = component.actionMap.allKeys().toList()
+        assert keys.contains(expected1)
+        assert keys.contains(expected2)
+        def inputMap = component.inputMap
+        def values = inputMap.allKeys().toList().collect{ inputMap.get(it) }
+        assert values.contains(expected1)
+        assert values.contains(expected2)
+    }
+
+    void testSetAccelerator() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def help = swing.action(accelerator:'F1')
+        def about = swing.action(accelerator:KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, InputEvent.CTRL_MASK))
+        assert help.getValue(Action.ACCELERATOR_KEY).toString()
+                             .indexOf(KeyEvent.getKeyText(KeyEvent.VK_F1)) > -1
+        def aboutKeyStroke = about.getValue(Action.ACCELERATOR_KEY)
+        assert aboutKeyStroke.keyCode == KeyEvent.VK_SPACE
+        assert(aboutKeyStroke.modifiers & InputEvent.CTRL_MASK) != 0
+    }
+
+    Action verifyAccel(Action action, int mustHave = 0) {
+        int mods = action.getValue(Action.ACCELERATOR_KEY).modifiers
+        assert mods != 0
+        assert (mods & mustHave) == mustHave
+        // don't assert (modd % musthave) != 0 because mustHave may be the platform shortcut modifer
+        return action
+    }
+
+    void testSetAcceleratorShortcuts() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        char q = 'Q'
+        swing.actions {
+            verifyAccel(action(accelerator: shortcut(q)))
+            verifyAccel(action(accelerator: shortcut(q, InputEvent.SHIFT_DOWN_MASK)), InputEvent.SHIFT_DOWN_MASK)
+            verifyAccel(action(accelerator: shortcut(KeyEvent.VK_NUMPAD5)))
+            verifyAccel(action(accelerator: shortcut(KeyEvent.VK_NUMPAD5, InputEvent.SHIFT_DOWN_MASK)), InputEvent.SHIFT_DOWN_MASK)
+            verifyAccel(action(accelerator: shortcut('DELETE')))
+            verifyAccel(action(accelerator: shortcut('DELETE', InputEvent.SHIFT_DOWN_MASK)), InputEvent.SHIFT_DOWN_MASK)
+        }
+    }
+
+    void testBorderLayoutConstraints() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        swing.internalFrame(id:'frameId',
+                border:BorderFactory.createTitledBorder(BorderFactory.createLoweredBevelBorder())) {
+            swing.frameId.contentPane.layout = new BorderLayout()
+            vbox(id:'vboxId', constraints:BorderLayout.NORTH)
+            hbox(id:'hboxId', constraints:BorderLayout.WEST)
+            rigidArea(id:'area1', constraints:BorderLayout.EAST, size:[3,4] as Dimension)
+            rigidArea(id:'area2', constraints:BorderLayout.SOUTH, width:30, height:40)
+            scrollPane(id:'scrollId', constraints:BorderLayout.CENTER,
+                border:BorderFactory.createRaisedBevelBorder()) {
+                glue()
+                vglue()
+                hglue()
+                vstrut()
+                vstrut(height:8)
+                hstrut()
+                hstrut(width:8)
+                rigidArea(id:'area3')
+                viewport()
+            }
+        }
+        assert swing.vboxId.parent == swing.frameId.contentPane
+        assert swing.hboxId.parent == swing.frameId.contentPane
+        assert swing.scrollId.parent == swing.frameId.contentPane
+    }
+
+    void testPropertyColumn() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def msg = shouldFail{
+            swing.propertyColumn()
+        }
+        assert msg.contains('propertyColumn must be a child of a tableModel')
+        msg = shouldFail{
+            swing.table{
+                tableModel(){
+                    propertyColumn()
+                }
+            }
+        }
+        assert msg.contains("Must specify a property for a propertyColumn"): \
+            "Instead found message: " + msg
+        swing.table{
+            tableModel(id: 'model'){
+                propertyColumn(propertyName:'p')
+                propertyColumn(propertyName:'ph', header: 'header')
+                propertyColumn(propertyName:'pt', type: String)
+                propertyColumn(propertyName:'pth', type: String, header: 'header')
+                propertyColumn(propertyName:'pe', editable:false)
+                propertyColumn(propertyName:'peh', editable:false, header: 'header')
+                propertyColumn(propertyName:'pet', editable:false, type: String, )
+                propertyColumn(propertyName:'peth', editable:false, type: String, header: 'header')
+            }
+        }
+        swing.model.columnList.each { col ->
+            def propName = col.valueModel.property
+            assert (col.headerValue == 'header') ^ !propName.contains('h')
+            assert (col.type == String) ^ !propName.contains('t')
+            assert col.valueModel.editable ^ propName.contains('e')
+        }
+    }
+
+    void testClosureColumn() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def msg = shouldFail{
+            swing.closureColumn()
+        }
+        assert msg.contains('closureColumn must be a child of a tableModel')
+        msg = shouldFail{
+            swing.table{
+                tableModel(){
+                    closureColumn()
+                }
+            }
+        }
+        assert msg.contains("Must specify 'read' Closure property for a closureColumn"): \
+            "Instead found message: " + msg
+        def closure = { x -> x }
+        def table = swing.table {
+            tableModel(){
+                closureColumn(read:closure, write:closure, header:'header')
+            }
+            tableModel(model:new groovy.model.ValueHolder('foo')){
+                closureColumn(read:closure, type:String.class)
+            }
+            tableModel(list:['a','b']){
+                closureColumn(read:closure, type:String.class)
+            }
+        }
+
+        assert table.columnModel.class.name == 'groovy.model.DefaultTableModel$MyTableColumnModel'
+    }
+
+    void testTableModelChange() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def table = swing.table {
+            tableModel {
+                propertyColumn(propertyName:'p')
+                propertyColumn(propertyName:'ph', header: 'header')
+                propertyColumn(propertyName:'pt', type: String)
+            }
+        }
+
+        def sorter = new groovy.inspect.swingui.TableSorter(table.model)
+        table.model = sorter
+
+        //GROOVY-2111 - resetting the model w/ a pass-through cleared the columns
+        assert table.columnModel.columnCount == 3
+    }
+
+    void testTableModelValues() {
+        if (headless) return
+
+        def squares  = [
+            [ val: 1, square:  1 ],
+            [ val: 2, square:  4 ],
+            [ val: 3, square:  9 ],
+            [ val: 4, square: 16 ]
+        ]
+
+        def swing = new SwingBuilder()
+        def frame = swing.frame(title: 'Tabelle',
+                        windowClosing: { System.exit(0) } ) {
+            scrollPane {
+                table(id:'table') {
+                    tableModel(list: squares) {
+                        propertyColumn(header: "Wert", propertyName: "val")
+                        closureColumn(header: "Quadrat", read: { it.square })
+                    }
+                }
+            }
+        }
+
+        squares.eachWithIndex {it, i ->
+            assert swing.table.getValueAt(i, 0) == it.val
+            assert swing.table.getValueAt(i, 1) == it.square
+        }
+    }
+
+    void testSetConstraints() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        swing.panel(layout:new BorderLayout()) {
+            label(text:'Name', constraints:BorderLayout.CENTER)
+        }
+    }
+
+    void testSetToolTipText() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        swing.panel(layout:new BorderLayout()) {
+            label(id:'labelId', text:'Name', toolTipText:'This is the name field')
+        }
+
+    }
+
+    void testTableLayout() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def msg = shouldFailWithCause(RuntimeException){
+            def frame = swing.frame(){
+                tr()
+            }
+        }
+        assert msg == "'tr' must be within a 'tableLayout'"
+        msg = shouldFailWithCause(RuntimeException){
+            def frame = swing.frame(){
+                tableLayout(){
+                    td()
+                }
+            }
+        }
+        assert msg == "'td' must be within a 'tr'"
+        swing.frame(id:'frame'){
+            tableLayout(){
+                tr {
+                    td {
+                        label(id:'label')
+                    }
+                }
+            }
+        }
+
+        assert swing.label.parent
+        assert swing.label.parent.parent
+        assert swing.label.parent.parent.parent
+        assert swing.label.parent.parent.parent.parent
+        assert swing.frame == swing.label.parent.parent.parent.parent.parent
+    }
+
+    void testAttributeOrdering() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+
+        def frame = swing.frame(
+            size:[500,500],
+            locationRelativeTo:null
+        )
+        def locationFirst = frame.location
+
+        frame = swing.frame(
+            locationRelativeTo:null,
+            size:[500,500]
+        )
+        def locationLast = frame.location
+
+        // setLocationReativeTo(null) places the component in the center of
+        // the screen, relative to it's size, so centering it after sizing it
+        // should result in a 250,250 offset from centering it before sizing it
+        assert locationFirst != locationLast
+    }
+
+    void testWidgetPassthroughConstraints() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def foo = swing.button('North')
+        def frame = swing.frame {
+            borderLayout()
+            widget(foo, constraints: BorderLayout.NORTH)
+            // a failed test throws MissingPropertyException by now
+        }
+    }
+
+    void testGROOVY1837ReuseAction() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+
+        def testAction = swing.action(name:'test', mnemonic:'A', accelerator:'ctrl R')
+        assert testAction.getValue(Action.MNEMONIC_KEY) != null
+        assert testAction.getValue(Action.ACCELERATOR_KEY) != null
+
+        swing.action(testAction)
+        assert testAction.getValue(Action.MNEMONIC_KEY) != null
+        assert testAction.getValue(Action.ACCELERATOR_KEY) != null
+    }
+
+    void testSeparators() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        swing.frame {
+            menu("test") {
+                separator(id:"menuSep")
+            }
+            toolBar {
+                separator(id:"tbSep")
+            }
+            separator(id:"sep")
+        }
+        assert swing.menuSep instanceof JPopupMenu_Separator
+        assert swing.tbSep instanceof JToolBar_Separator
+        assert swing.sep instanceof JSeparator
+    }
+
+    void testCollectionNodes() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        def collection = swing.actions {
+            action(id:'test')
+        }
+        assert collection.contains(swing.test)
+    }
+
+    void testFactoryCornerCases() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+        assert swing.bogusWidget() == null
+
+        swing.registerFactory("nullWidget",
+            [newInstance:{builder, name, value, props -> null}] as AbstractFactory)
+        assert swing.nullWidget() == null
+    }
+
+    void testFactoryLogging() {
+        def logger = java.util.logging.Logger.getLogger(SwingBuilder.class.name)
+        def oldLevel = logger.getLevel()
+        logger.setLevel(java.util.logging.Level.FINE)
+        def swing = new SwingBuilder()
+        swing.label()
+        logger.setLevel(oldLevel)
+    }
+
+    void testEnhancedValueArguments() {
+        if (headless) return
+
+        def swing = new SwingBuilder()
+
+        def anAction = swing.action(name:"test action")
+        def icon = new javax.swing.plaf.metal.MetalComboBoxIcon()
+        def richActionItems = [
+            'button',
+            'checkBox',
+            'radioButton',
+            'toggleButton',
+            'menuItem',
+            'checkBoxMenuItem',
+            'radioButtonMenuItem'
+        ]
+
+        richActionItems.each {name ->
+            swing."$name"(anAction, id:"${name}Action".toString())
+            swing."$name"(icon, id:"${name}Icon".toString())
+            swing."$name"("string", id:"${name}String".toString())
+            swing."$name"(swing."${name}Action", id:"${name}Self".toString())
+
+            assert swing."${name}Action"
+            assert swing."${name}Icon"
+            assert swing."${name}String".text == 'string'
+            assert swing."${name}Self" == swing."${name}Action"
+            shouldFail {
+                swing."$name"(['bad'])
+            }
+        }
+
+        // elements that take no value argument
+        def noValueItems = [
+            "actions",
+            "boxLayout",
+            "comboBox",
+            "formattedTextField",
+            "glue",
+            "hbox",
+            "hglue",
+            "hstrut",
+            "map",
+            "rigidArea",
+            "separator",
+            "vbox",
+            "vglue",
+            "vstrut",
+        ]
+
+        noValueItems.each {name ->
+            //println name
+            shouldFail {
+                swing.frame {
+                    "$name"(swing."$name"(), id:"${name}Self".toString())
+                }
+            }
+        }
+
+         // elements that only take their own type as a value argument
+        def selfItems = [
+            "action",
+            "borderLayout",
+            "boundedRangeModel",
+            "box",
+            "buttonGroup",
+            "cardLayout",
+            //"closureColumn",
+            "colorChooser",
+            //"container",
+            "desktopPane",
+            "dialog",
+            "fileChooser",
+            "flowLayout",
+            "frame",
+            "gbc",
+            "gridBagConstraints",
+            "gridBagLayout",
+            "gridLayout",
+            "internalFrame",
+            "layeredPane",
+            "list",
+            "menuBar",
+            "optionPane",
+            //"overlayLayout",
+            "panel",
+            "popupMenu",
+            "progressBar",
+            //"propertyColumn",
+            "scrollBar",
+            "scrollPane",
+            "slider",
+            "spinner",
+            "spinnerDateModel",
+            "spinnerListModel",
+            "spinnerNumberModel",
+            "splitPane",
+            "springLayout",
+            "tabbedPane",
+            "table",
+            "tableColumn",
+            "tableLayout",
+            "tableModel",
+            //"td",
+            "toolBar",
+            //"tr",
+            "tree",
+            "viewport",
+            //"widget",
+            "window",
+        ]
+        selfItems.each {name ->
+            //println name
+            swing.frame {
+                "$name"(swing."$name"(), id:"${name}Self".toString())
+            }
+
+            shouldFail {
+                swing.frame {
+                    swing."$name"(icon)
+                }
+            }
+        }
+
+         // elements take their own type as a value argument or a stringa s a text property
+        def textItems = [
+            "editorPane",
+            "label",
+            "menu",
+            "passwordField",
+            "textArea",
+            "textField",
+            "textPane",
+        ]
+        textItems.each {name ->
+            swing.frame {
+                "$name"(swing."$name"(), id:"${name}Self".toString())
+                "$name"('text', id:"${name}Text".toString())
+            }
+            assert swing."${name}Text".text == 'text'
+
+            shouldFail {
+                swing.frame {
+                    swing."$name"(icon)
+                }
+            }
+        }
+
+        // leftovers...
+        swing.frame {
+            action(action:anAction)
+            box(axis:BoxLayout.Y_AXIS)
+            hstrut(5)
+            vstrut(5)
+            tableModel(tableModel:tableModel())
+            container(id:'c', panel()) {
+                widget(id:'w', label("label"))
+                bean("anything")
+            }
+            container(container:panel()) {
+                widget(widget:label("label"))
+                bean(bean:"anything")
+            }
+        }
+        assert swing.w.parent == swing.c
+        shouldFail {
+            swing.actions(property:'fails')
+        }
+        shouldFail {
+            swing.widget()
+        }
+        shouldFail {
+            swing.widget(label('label')) {
+                label('No Content For You!')
+            }
+        }
+        shouldFail {
+            swing.container()
+        }
+        shouldFail {
+            swing.bean()
+        }
+        shouldFail {
+            swing.bean("anything") {
+                label('Nothing')
+            }
+        }
+    }
+
+    boolean instancePass
+
+    public void markPassed() {
+        instancePass = true
+    }
+
+    public void testEDT() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        boolean pass = false
+        swing.edt { pass = SwingUtilities.isEventDispatchThread() }
+        assert pass
+
+        pass = false
+        swing.edt { swing.edt { pass = SwingUtilities.isEventDispatchThread() } }
+        assert pass
+
+        instancePass = false
+        swing.edt this.&markPassed
+        assert instancePass
+    }
+
+    public void testDoLater() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        boolean pass = false
+        swing.doLater {sleep 100; pass = true }
+        assert !pass
+        sleep 200
+        assert pass
+
+        // doLater in the EDT is still a do later
+        pass = false
+        swing.edt { swing.doLater {sleep 100; pass = true } }
+        assert !pass
+        sleep 200
+        assert pass
+
+        instancePass = false
+        swing.doLater this.&markPassed
+        sleep 50
+        assert instancePass
+    }
+
+    public void testDoOutside() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        boolean pass = false
+        swing.doOutside {sleep 100; pass = true }
+        assert !pass
+        sleep 200
+        assert pass
+
+        pass = false
+        swing.edt {
+            swing.doOutside {sleep 100; pass = true }
+            assert !pass
+            sleep 200
+            assert pass
+        }
+
+        instancePass = false
+        swing.doOutside this.&markPassed
+        sleep 50
+        assert instancePass
+    }
+
+    public void testDispose() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        swing.frame(id:'frame').pack()
+        swing.dialog(id:'dialog').pack()
+        swing.window(id:'window').pack()
+
+        //TODO check bind and model
+        assert swing.frame.isDisplayable()
+        assert swing.dialog.isDisplayable()
+        assert swing.window.isDisplayable()
+        swing.dispose()
+
+        //TODO check bind and model
+        assert !swing.frame.isDisplayable()
+        assert !swing.dialog.isDisplayable()
+        assert !swing.window.isDisplayable()
+
+    }
+
+    public void testPackAndShow() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        swing.frame(id:'frame', pack:true)
+        swing.dialog(id:'dialog', pack:true)
+        swing.window(id:'window', pack:true)
+
+        assert swing.frame.isDisplayable()
+        assert swing.dialog.isDisplayable()
+        assert swing.window.isDisplayable()
+        swing.dispose()
+
+        swing.frame(id:'frame', show:true)
+        swing.dialog(id:'dialog', show:true)
+        swing.window(id:'window', show:true)
+
+        assert swing.frame.visible
+        assert swing.dialog.visible
+        assert swing.window.visible
+        swing.dispose()
+
+        swing.frame(id:'frame', pack:true, show:true)
+        swing.dialog(id:'dialog', pack:true, show:true)
+        swing.window(id:'window', pack:true, show:true)
+
+        assert swing.frame.visible
+        assert swing.dialog.visible
+        assert swing.window.visible
+        swing.dispose()
+    }
+
+    public void testContainment() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        def topLevel = [
+            "window",
+            "frame",
+            "dialog",
+            "internalFrame",
+        ]
+
+        def containers = [
+            "hbox",
+            "box",
+            "desktopPane",
+            "layeredPane",
+            "panel",
+            "popupMenu",
+            //"scrollPane",
+            "splitPane",
+            "tabbedPane",
+            "toolBar",
+            "viewport",
+        ]
+
+        def components = [
+            "comboBox",
+            "formattedTextField",
+            "glue",
+            "hbox",
+            "hglue",
+            "hstrut",
+            "rigidArea",
+            "separator",
+            "vbox",
+            "vglue",
+            "vstrut",
+            "box",
+            "colorChooser",
+            "desktopPane",
+            "fileChooser",
+            "internalFrame",
+            "layeredPane",
+            "list",
+            "menu",
+            //"menuBar",
+            "optionPane",
+            "panel",
+            //"popupMenu",
+            "progressBar",
+            "scrollBar",
+            "scrollPane",
+            "slider",
+            "spinner",
+            "splitPane",
+            "tabbedPane",
+            "table",
+            "toolBar",
+            "tree",
+            "viewport",
+            "editorPane",
+            "label",
+            "passwordField",
+            "textArea",
+            "textField",
+            "textPane",
+        ]
+
+
+        topLevel.each {parentWidget ->
+            components.each { childWidget ->
+                //println "$parentWidget / $childWidget"
+                def child
+                def parent = swing."$parentWidget" { child = "$childWidget"() }
+                assert parent.contentPane == child.parent
+            }
+        }
+
+        containers.each {parentWidget ->
+            components.each { childWidget ->
+                //println "$parentWidget / $childWidget"
+                def child
+                def parent = swing."$parentWidget" { child = "$childWidget"() }
+                assert parent == child.parent
+            }
+        }
+
+        components.each { childWidget ->
+            //println "scrollPane / $childWidget"
+
+            def child
+            def parent = swing.scrollPane { child = "$childWidget"() }
+            if (childWidget == 'viewport') {
+                assert parent.viewport == child
+            } else {
+                assert parent.viewport == child.parent
+            }
+        }
+    }
+
+    public void testMenus() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        def frame = swing.frame {
+            menuBar(id:'bar') {
+                menu('menu', id:'menu') {
+                    menuItem('item', id:'item')
+                    checkBoxMenuItem('check', id:'check')
+                    radioButtonMenuItem('radio', id:'radio')
+                    separator(id:'sep')
+                    menu('subMenu', id:'subMenu') {
+                        menuItem('item', id:'subitem')
+                        checkBoxMenuItem('check', id:'subcheck')
+                        radioButtonMenuItem('radio', id:'subradio')
+                        separator(id:'subsep')
+                        menu('subSubMenu', id:'subSubMenu')
+                    }
+                }
+            }
+        }
+
+        assert frame.JMenuBar == swing.bar
+        assert swing.bar.menuCount == 1
+        assert swing.bar.getMenu(0) == swing.menu
+
+        assert swing.menu.itemCount == 5
+        assert swing.menu.getItem(0) == swing.item
+        assert swing.menu.getItem(1) == swing.check
+        assert swing.menu.getItem(2) == swing.radio
+        assert swing.menu.getItem(3) == null // not a menu item
+        assert swing.menu.getMenuComponent(3) == swing.sep
+        assert swing.menu.getItem(4) == swing.subMenu
+        shouldFail { swing.menu.getItem(5) }
+
+        assert swing.subMenu.itemCount == 5
+        assert swing.subMenu.getItem(0) == swing.subitem
+        assert swing.subMenu.getItem(1) == swing.subcheck
+        assert swing.subMenu.getItem(2) == swing.subradio
+        assert swing.subMenu.getItem(3) == null // not a menu item
+        assert swing.subMenu.getMenuComponent(3) == swing.subsep
+        assert swing.subMenu.getItem(4) == swing.subSubMenu
+    }
+
+    public void testLookAndFeel() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        def oldLAF = UIManager.getLookAndFeel();
+        try {
+            // test LAFs guaranteed to be everywhere
+            swing.lookAndFeel('metal')
+            swing.lookAndFeel('system')
+            swing.lookAndFeel('crossPlatform')
+
+            // test alternate invocations...
+            swing.lookAndFeel(new javax.swing.plaf.metal.MetalLookAndFeel())
+            shouldFail() {
+                swing.lookAndFeel(this)
+            }
+            swing.lookAndFeel('javax.swing.plaf.metal.MetalLookAndFeel')
+            shouldFail() {
+                swing.lookAndFeel('BogusLookAndFeel')
+            }
+
+            // test Metal Themeing and aux attributes
+            swing.lookAndFeel('metal', theme: 'steel', boldFonts: false)
+            shouldFail {
+                swing.lookAndFeel('metal', theme: 'steel', boldFonts: false, bogusAttribute: 'bad bad bad')
+            }
+
+            // test setup via attribute alone
+            swing.lookAndFeel(lookAndFeel:'metal', theme: 'steel', boldFonts: false)
+
+            // test Look and Feel Closure
+            swing.lookAndFeel('metal') { laf ->
+                assert laf instanceof MetalLookAndFeel
+            }
+            swing.lookAndFeel('metal', boldFonts: true) { laf ->
+                assert laf instanceof MetalLookAndFeel
+            }
+            swing.lookAndFeel(lookAndFeel:'metal', boldFonts: true) { laf ->
+                assert laf instanceof MetalLookAndFeel
+            }
+            shouldFail {
+                swing.lookAndFeel() {laf ->
+                    println "shouldn't get here"
+                }
+            }
+
+        } finally {
+            UIManager.setLookAndFeel(oldLAF)
+        }
+   }
+
+    public void testBorders() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        // classic smoke test, try every valid combination and look for smoke...
+        swing.frame {
+            lineBorder(color:Color.BLACK, parent:true)
+            lineBorder(color:Color.BLACK, thickness:4, parent:true)
+            lineBorder(color:Color.BLACK, roundedCorners:true, parent:true)
+            lineBorder(color:Color.BLACK, thickness:4, roundedCorners:true, parent:true)
+            raisedBevelBorder(parent:true)
+            raisedBevelBorder(highlight:Color.GREEN, shadow:Color.PINK, parent:true)
+            raisedBevelBorder(highlightOuter:Color.GREEN, highlightInner:Color.RED, shadowOuter:Color.PINK, shadowInner:Color.BLUE, parent:true)
+            loweredBevelBorder(parent:true)
+            loweredBevelBorder(highlight:Color.GREEN, shadow:Color.PINK, parent:true)
+            loweredBevelBorder(highlightOuter:Color.GREEN, highlightInner:Color.RED, shadowOuter:Color.PINK, shadowInner:Color.BLUE, parent:true)
+            etchedBorder(parent:true)
+            etchedBorder(highlight:Color.GREEN, shadow:Color.PINK, parent:true)
+            loweredEtchedBorder(parent:true)
+            loweredEtchedBorder(highlight:Color.GREEN, shadow:Color.PINK, parent:true)
+            raisedEtchedBorder(parent:true)
+            raisedEtchedBorder(highlight:Color.GREEN, shadow:Color.PINK, parent:true)
+            titledBorder("Title 1", parent:true)
+            titledBorder(title:"Title 2", parent:true)
+            titledBorder("Title 3", position:'bottom', parent:true)
+            titledBorder(title:"Title 4", position:'aboveBottom', parent:true)
+            titledBorder("Title 5", position:TitledBorder.ABOVE_TOP, parent:true)
+            titledBorder(title:"Title 6", position:TitledBorder.BOTTOM, parent:true)
+            titledBorder("Title 7", justification:'right', parent:true)
+            titledBorder(title:"Title 8", justification:'acenter', parent:true)
+            titledBorder("Title 9", justification:TitledBorder.TRAILING, parent:true)
+            titledBorder(title:"Title A", justification:TitledBorder.LEADING, parent:true)
+            titledBorder("Title B", border:lineBorder(color:Color.RED, thickness:6), parent:true)
+            titledBorder(title:"Title C", border:lineBorder(color:Color.BLUE, thickness:6), parent:true)
+            titledBorder("Title D", color:Color.CYAN, parent:true)
+            titledBorder(title:"Title E", border:lineBorder(color:Color.BLUE, thickness:6), parent:true)
+            emptyBorder(6, parent:true)
+            emptyBorder([3,5,6,9], parent:true)
+            emptyBorder(top:6, left:5, bottom:6, right:9, parent:true)
+            compoundBorder([titledBorder("single")], parent:true)
+            compoundBorder([titledBorder("outer"), titledBorder("inner")], parent:true)
+            compoundBorder(outer:titledBorder("outer"), inner:titledBorder("inner"), parent:true)
+            compoundBorder([titledBorder("outer"), titledBorder("middle"), titledBorder("inner")], parent:true)
+            matteBorder(Color.MAGENTA, size:7, parent:true)
+            matteBorder(7, color:Color.MAGENTA, parent:true)
+            matteBorder(javax.swing.plaf.metal.MetalIconFactory.getCheckBoxIcon(), size:9, parent:true)
+            matteBorder(9, icon:javax.swing.plaf.metal.MetalIconFactory.getCheckBoxIcon(), parent:true)
+
+            lineBorder(color:Color.BLACK)
+            lineBorder(color:Color.BLACK, thickness:4)
+            lineBorder(color:Color.BLACK, roundedCorners:true)
+            lineBorder(color:Color.BLACK, thickness:4, roundedCorners:true)
+            raisedBevelBorder()
+            raisedBevelBorder(highlight:Color.GREEN, shadow:Color.PINK)
+            raisedBevelBorder(highlightOuter:Color.GREEN, highlightInner:Color.RED, shadowOuter:Color.PINK, shadowInner:Color.BLUE)
+            loweredBevelBorder()
+            loweredBevelBorder(highlight:Color.GREEN, shadow:Color.PINK)
+            loweredBevelBorder(highlightOuter:Color.GREEN, highlightInner:Color.RED, shadowOuter:Color.PINK, shadowInner:Color.BLUE)
+            etchedBorder()
+            etchedBorder(highlight:Color.GREEN, shadow:Color.PINK)
+            loweredEtchedBorder()
+            loweredEtchedBorder(highlight:Color.GREEN, shadow:Color.PINK)
+            raisedEtchedBorder()
+            raisedEtchedBorder(highlight:Color.GREEN, shadow:Color.PINK)
+            titledBorder("Title 1")
+            titledBorder(title:"Title 2")
+            titledBorder("Title 3", position:'bottom')
+            titledBorder(title:"Title 4", position:'aboveBottom')
+            titledBorder("Title 5", position:TitledBorder.ABOVE_TOP)
+            titledBorder(title:"Title 6", position:TitledBorder.BOTTOM)
+            titledBorder("Title 7", justification:'right')
+            titledBorder(title:"Title 8", justification:'acenter')
+            titledBorder("Title 9", justification:TitledBorder.TRAILING)
+            titledBorder(title:"Title A", justification:TitledBorder.LEADING)
+            titledBorder("Title B", border:lineBorder(color:Color.RED, thickness:6))
+            titledBorder(title:"Title C", border:lineBorder(color:Color.BLUE, thickness:6))
+            titledBorder("Title D", color:Color.CYAN)
+            titledBorder(title:"Title E", border:lineBorder(color:Color.BLUE, thickness:6))
+            emptyBorder(6)
+            emptyBorder([3,5,6,9])
+            emptyBorder(top:6, left:5, bottom:6, right:9)
+            compoundBorder([titledBorder("single")])
+            compoundBorder([titledBorder("outer"), titledBorder("inner")])
+            compoundBorder(outer:titledBorder("outer"), inner:titledBorder("inner"))
+            compoundBorder([titledBorder("outer"), titledBorder("middle"), titledBorder("inner")])
+            matteBorder(Color.MAGENTA, size:7)
+            matteBorder(7, color:Color.MAGENTA)
+            matteBorder(javax.swing.plaf.metal.MetalIconFactory.getCheckBoxIcon(), size:9)
+            matteBorder(9, icon:javax.swing.plaf.metal.MetalIconFactory.getCheckBoxIcon())
+        }
+    }
+
+    public void testBorderAttachment() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        swing.frame(id:'frame') {
+           raisedBevelBorder()
+        }
+        assert swing.frame.contentPane.border == null
+
+        swing.frame(id:'frame') {
+           raisedBevelBorder(parent:true)
+        }
+        assert swing.frame.contentPane.border != null
+
+        swing.panel(id:'panel') {
+           raisedBevelBorder()
+        }
+        assert swing.panel.border == null
+
+        swing.panel(id:'panel') {
+           raisedBevelBorder(parent:true)
+        }
+        assert swing.panel.border != null
+    }
+
+    public void testImageIcon() {
+        if (headless) return
+        def swing = new SwingBuilder()
+
+        String baseDir = new File("src/main").absolutePath
+
+        String resource = Console.ICON_PATH
+        String path = baseDir + resource
+        File file = new File(path)
+        String relativeResource = file.name
+        URL url = file.toURL()
+
+        swing.imageIcon(path, id:'ii')
+        assert swing.ii != null
+
+        swing.imageIcon(file:path, id:'ii')
+        assert swing.ii != null
+
+        swing.imageIcon(path, description:'<none>', id:'ii')
+        assert swing.ii != null
+        assert swing.ii.description == '<none>'
+
+        swing.imageIcon(file:path, description:'<none>', id:'ii')
+        assert swing.ii != null
+        assert swing.ii.description == '<none>'
+
+
+        swing.imageIcon(url, id:'ii')
+        assert swing.ii != null
+
+        swing.imageIcon(url:url, id:'ii')
+        assert swing.ii != null
+
+        swing.imageIcon(url, description:'<none>', id:'ii')
+        assert swing.ii != null
+        assert swing.ii.description == '<none>'
+
+        swing.imageIcon(url:url, description:'<none>', id:'ii')
+        assert swing.ii != null
+        assert swing.ii.description == '<none>'
+
+
+        swing.imageIcon(resource, id:'ii')
+        assert swing.ii != null
+
+        swing.imageIcon(resource:resource, id:'ii')
+        assert swing.ii != null
+
+        swing.imageIcon(resource, description:'<none>', id:'ii')
+        assert swing.ii != null
+        assert swing.ii.description == '<none>'
+
+        swing.imageIcon(file:resource, description:'<none>', id:'ii')
+        assert swing.ii != null
+        assert swing.ii.description == '<none>'
+
+
+        swing.imageIcon(resource, class:Console, id:'ii')
+        assert swing.ii != null
+
+        swing.imageIcon(resource:resource, class:Console, id:'ii')
+        assert swing.ii != null
+
+        swing.imageIcon(resource, description:'<none>', class:Console, id:'ii')
+        assert swing.ii != null
+        assert swing.ii.description == '<none>'
+
+        swing.imageIcon(file:resource, description:'<none>', class:Console, id:'ii')
+        assert swing.ii != null
+        assert swing.ii.description == '<none>'
+
+
+        swing.imageIcon(relativeResource, class:Console, id:'ii')
+        assert swing.ii != null
+
+        swing.imageIcon(resource:relativeResource, class:Console, id:'ii')
+        assert swing.ii != null
+
+        swing.imageIcon(relativeResource, description:'<none>', class:Console, id:'ii')
+        assert swing.ii != null
+        assert swing.ii.description == '<none>'
+
+        swing.imageIcon(file:relativeResource, description:'<none>', class:Console, id:'ii')
+        assert swing.ii != null
+        assert swing.ii.description == '<none>'
+
+    }
+}
diff --git a/groovy/src/test/groovy/text/SimpleTemplateTest.groovy b/groovy/src/test/groovy/text/SimpleTemplateTest.groovy
new file mode 100644
index 0000000..207828a
--- /dev/null
+++ b/groovy/src/test/groovy/text/SimpleTemplateTest.groovy
@@ -0,0 +1,58 @@
+package groovy.text
+
+class SimpleTemplateTest extends GroovyTestCase {
+
+    void testSimpleCallFromGroovyEmpty() {
+        assertEquals('', simpleCall(''))
+    }
+
+    void testSimpleCallFromGroovyStatic() {
+        def input = 'some static text'
+        assertEquals(input, simpleCall(input))
+    }
+
+    void testExpressionAssign() {
+        assertEquals('1',   simpleCall('<%=1%>'))
+        assertEquals(' 1',  simpleCall(' <%=1%>'))
+        assertEquals(' 1 ', simpleCall(' <%=1%> '))
+        assertEquals(' 1 ', simpleCall(' <%= 1%> '))
+        assertEquals(' 1 ', simpleCall(' <%= 1 %> '))
+        assertEquals(' 1 ', simpleCall(" <%=\n 1 \n%> "))
+        assertEquals(' 1', bindingCall([a:1],' <%=a%>'))
+    }
+
+    void testExpressionEval() {
+        assertEquals('1', simpleCall('<%print(1)%>'))
+        assertEquals('01', simpleCall('<%for(i in 0..1){print(i)}%>'))
+    }
+
+    void testWithMarkupBuilder(){
+    def text = '''<%
+        builder = new groovy.xml.MarkupBuilder(out)
+        [1,2,3].each{ count ->
+            out.print(1)
+        }
+    %>'''
+    assertEquals('111', simpleCall(text))
+    }
+
+    void testWithMarkupBuilderWithSemicolons(){
+    def text = '''<%
+        builder = new groovy.xml.MarkupBuilder(out);
+        [1,2,3].each{ count ->
+            out.print(1);
+        }
+    %>'''
+    assertEquals('111', simpleCall(text))
+    }
+
+    String simpleCall(input){
+        bindingCall([:], input)
+    }
+
+    String bindingCall(binding, input){
+        def template = new SimpleTemplateEngine(true).createTemplate(input)
+        return template.make(binding).toString()
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/text/TemplateTest.java b/groovy/src/test/groovy/text/TemplateTest.java
new file mode 100644
index 0000000..baab4bc
--- /dev/null
+++ b/groovy/src/test/groovy/text/TemplateTest.java
@@ -0,0 +1,69 @@
+/*
+ * $Id$version Mar 8, 2004 1:36:31 AM $user Exp $
+ * 
+ * Copyright 2003 (C) Sam Pullara. All Rights Reserved.
+ * 
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met: 1. Redistributions of source code must retain
+ * copyright statements and notices. Redistributions must also contain a copy
+ * of this document. 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution. 3.
+ * The name "groovy" must not be used to endorse or promote products derived
+ * from this Software without prior written permission of The Codehaus. For
+ * written permission, please contact info@codehaus.org. 4. Products derived
+ * from this Software may not be called "groovy" nor may "groovy" appear in
+ * their names without prior written permission of The Codehaus. "groovy" is a
+ * registered trademark of The Codehaus. 5. Due credit should be given to The
+ * Codehaus - http://groovy.codehaus.org/
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *  
+ */
+package groovy.text;
+
+import junit.framework.TestCase;
+import org.codehaus.groovy.control.CompilationFailedException;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author sam
+ */
+public class TemplateTest extends TestCase {
+
+    public void testMixedTemplateText() throws CompilationFailedException, ClassNotFoundException, IOException {
+        Template template1 = new SimpleTemplateEngine().createTemplate("<%= \"test\" %> of expr and <% test = 1 %>${test} script.");
+        assertEquals("test of expr and 1 script.", template1.make().toString());
+
+        Template template2 = new GStringTemplateEngine().createTemplate("<%= \"test\" %> of expr and <% test = 1 %>${test} script.");
+        assertEquals("test of expr and 1 script.", template2.make().toString());
+
+    }
+
+    public void testBinding() throws CompilationFailedException, ClassNotFoundException, IOException {
+        Map binding = new HashMap();
+        binding.put("sam", "pullara");
+
+        Template template1 = new SimpleTemplateEngine().createTemplate("<%= sam %><% print sam %>");
+        assertEquals("pullarapullara", template1.make(binding).toString());
+
+        Template template2 = new GStringTemplateEngine().createTemplate("<%= sam %><% out << sam %>");
+        assertEquals("pullarapullara", template2.make(binding).toString());
+
+    }
+
+}
diff --git a/groovy/src/test/groovy/text/XmlTemplateEngineTest.java b/groovy/src/test/groovy/text/XmlTemplateEngineTest.java
new file mode 100644
index 0000000..d29fbf7
--- /dev/null
+++ b/groovy/src/test/groovy/text/XmlTemplateEngineTest.java
@@ -0,0 +1,30 @@
+package groovy.text;
+
+import junit.framework.TestCase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class XmlTemplateEngineTest extends TestCase {
+
+    public void testBinding() throws Exception {
+        Map binding = new HashMap();
+        binding.put("Christian", "Stein");
+
+        XmlTemplateEngine xmlTemplateEngine = new XmlTemplateEngine();
+        String xmlScript = "<!-- Just a comment. -->\n" //
+                + "<xml xmlns:gsp=\"http://groovy.codehaus.org/2005/gsp\">" //
+                + "  ${Christian}" //
+                + "  <gsp:expression>Christian</gsp:expression>" //
+                + "  <gsp:scriptlet>println Christian</gsp:scriptlet>" //
+                + "</xml>";
+        String xmlResult = "<xml>\n" //
+                + "  Stein\n" //
+                + xmlTemplateEngine.getIndentation() + "Stein\n" //
+                + "Stein" + System.getProperty("line.separator") //
+                + "</xml>\n";
+        Template template = xmlTemplateEngine.createTemplate(xmlScript);
+        assertEquals(xmlResult, template.make(binding).toString());
+    }
+
+}
diff --git a/groovy/src/test/groovy/time/DurationTest.groovy b/groovy/src/test/groovy/time/DurationTest.groovy
new file mode 100644
index 0000000..bb9c58e
--- /dev/null
+++ b/groovy/src/test/groovy/time/DurationTest.groovy
@@ -0,0 +1,52 @@
+package groovy.time
+
+import org.codehaus.groovy.runtime.TimeCategory
+
+class DurationTest extends GroovyTestCase {
+    void testFixedDurationArithmetic() {
+        use(TimeCategory) {
+            def oneDay = 2.days - 1.day
+            assert oneDay.toMilliseconds() == (24 * 60 * 60 * 1000): \
+                "Expected ${24 * 60 * 60 * 1000} but was ${oneDay.toMilliseconds()}"
+            
+            oneDay = 2.days - 1.day + 24.hours - 1440.minutes
+            assert oneDay.toMilliseconds() == (24 * 60 * 60 * 1000): \
+                "Expected ${24 * 60 * 60 * 1000} but was ${oneDay.toMilliseconds()}"
+        }
+   }
+
+    void fixme_testDurationArithmetic() {
+        use(TimeCategory) {
+            //def nowOffset = (new Date()).daylightSavingsOffset
+            def nowOffset = 0.months.from.now.daylightSavingsOffset
+
+            // add two durations
+            def twoMonthsA = 1.month + 1.month
+            def offsetA = twoMonthsA.daylightSavingsOffset - nowOffset
+            // subtract dates which are two months apart
+            def offsetB = 2.months.from.now.daylightSavingsOffset - 0.months.from.now.daylightSavingsOffset
+            def twoMonthsB = 2.months.from.now + offsetB - 0.months.from.now
+            assertEquals "Two months absolute duration should be the same as the difference between two dates two months apart\n",
+                (twoMonthsA + offsetA).toMilliseconds(), twoMonthsB.toMilliseconds()
+
+            // add two durations
+            def monthAndWeekA = 1.month + 1.week
+            offsetA = monthAndWeekA.daylightSavingsOffset - nowOffset
+            // subtract absolute date and a duration from another absolute date
+            offsetB = (1.month.from.now + 1.week).daylightSavingsOffset - 0.months.from.now.daylightSavingsOffset
+            def monthAndWeekB = 1.month.from.now + 1.week + offsetB - 0.months.from.now
+            assertEquals "A week and a month absolute duration should be the same as the difference between two dates that far apart\n",
+                (monthAndWeekA + offsetA).toMilliseconds(), monthAndWeekB.toMilliseconds()
+        }
+    }
+
+    void testDatumDependantArithmetic() {
+        use(TimeCategory) {
+            def start = new Date(961552080000)
+            def then = (start + 1.month) + 1.week
+            def week = then - (start + 1.month)
+            assert week.toMilliseconds() == (7 * 24 * 60 * 60 * 1000): \
+                "Expected ${7 * 24 * 60 * 60 * 1000} but was ${week.toMilliseconds()}"
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/tree/ClosureClassLoaderBug.groovy b/groovy/src/test/groovy/tree/ClosureClassLoaderBug.groovy
new file mode 100644
index 0000000..15eba51
--- /dev/null
+++ b/groovy/src/test/groovy/tree/ClosureClassLoaderBug.groovy
@@ -0,0 +1,16 @@
+package groovy.tree
+
+class ClosureClassLoaderBug extends GroovyTestCase {
+    def b
+    def EXPECTED = 'root1[attributes={}; value=[elem1[attributes={}; value=hello1]]]'
+
+    void testTree() {
+        b = NodeBuilder.newInstance()
+        
+        def root = b.root1( {
+            b.elem1('hello1')
+        })
+        
+        assert EXPECTED == root.toString()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/tree/NavigationTest.groovy b/groovy/src/test/groovy/tree/NavigationTest.groovy
new file mode 100644
index 0000000..bd4ec66
--- /dev/null
+++ b/groovy/src/test/groovy/tree/NavigationTest.groovy
@@ -0,0 +1,49 @@
+package groovy.tree
+
+/**
+ * Simple test of tree walking
+ */
+class NavigationTest extends GroovyTestCase {
+    
+    void testDepthFirst() {
+        def tree = createTree()
+        
+        def names = tree.depthFirst().collect { it.name() }
+        def expected = ['a', 'b1', 'b2', 'c1', 'c2', 'b3', 'b4', 'c3', 'c4', 'b5']
+        
+        assert names == expected
+    }
+    
+    void testBreadthFirst() {
+        def tree = createTree()
+        
+        def names = tree.breadthFirst().collect { it.name() }
+        def expected = ['a', 'b1', 'b2', 'b3', 'b4', 'b5', 'c1', 'c2', 'c3', 'c4']
+        
+        assert names == expected
+    }
+    
+    protected def createTree() {       
+        def b = NodeBuilder.newInstance()
+        
+        def root = b.a(a:5, b:7) {
+            b1()
+            b2 {
+                c1()
+                c2()
+            }
+            b3()
+            b4 {
+                c3()
+                c4()
+            }
+            b5()
+        }
+        
+        assert root != null
+        
+        println(root)
+        
+        return root
+    }
+}
diff --git a/groovy/src/test/groovy/tree/NestedClosureBugTest.groovy b/groovy/src/test/groovy/tree/NestedClosureBugTest.groovy
new file mode 100644
index 0000000..f5d9543
--- /dev/null
+++ b/groovy/src/test/groovy/tree/NestedClosureBugTest.groovy
@@ -0,0 +1,21 @@
+package groovy.tree
+
+/**
+ * Test case for a bug with nested closures
+ */
+class NestedClosureBugTest extends GroovyTestCase {
+    def b
+    def EXPECTED = 'root[attributes={a=xyz}; value=[child[attributes={}; value=[grandChild[attributes={}; value=[]]]]]]'
+
+    void testNestedClosureBug() {
+        b = NodeBuilder.newInstance()
+        
+        def root = b.root(['a':'xyz'], {
+            b.child({
+                b.grandChild()  
+            })
+        })
+
+        assert EXPECTED == root.toString()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/tree/NodePrinterTest.java b/groovy/src/test/groovy/tree/NodePrinterTest.java
new file mode 100644
index 0000000..d13c553
--- /dev/null
+++ b/groovy/src/test/groovy/tree/NodePrinterTest.java
@@ -0,0 +1,94 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.tree;
+
+import groovy.lang.GroovyObject;
+import org.codehaus.groovy.classgen.TestSupport;
+
+import java.util.logging.Logger;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class NodePrinterTest extends TestSupport {
+
+    public void testTree() throws Exception {
+        GroovyObject object = compile("src/test/groovy/tree/TreeTest.groovy");
+        object.invokeMethod("testTree", null);
+    }
+
+    public void testVerboseTree() throws Exception {
+        GroovyObject object = compile("src/test/groovy/tree/VerboseTreeTest.groovy");
+        object.invokeMethod("testTree", null);
+    }
+
+    public void testSmallTree() throws Exception {
+        GroovyObject object = compile("src/test/groovy/tree/SmallTreeTest.groovy");
+        object.invokeMethod("testTree", null);
+    }
+
+    public void testLittleClosure() throws Exception {
+        GroovyObject object = compile("src/test/groovy/LittleClosureTest.groovy");
+        object.invokeMethod("testClosure", null);
+    }
+
+    public void testNestedClosureBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/tree/NestedClosureBugTest.groovy");
+        object.invokeMethod("testNestedClosureBug", null);
+    }
+
+    public void testClosureClassLoaderBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/tree/ClosureClassLoaderBug.groovy");
+        object.invokeMethod("testTree", null);
+    }
+
+    public void testLogging() {
+        Logger log = Logger.getLogger(getClass().getName());
+        log.info("Logging using JDK 1.4 logging");
+    }
+}
diff --git a/groovy/src/test/groovy/tree/SmallTreeTest.groovy b/groovy/src/test/groovy/tree/SmallTreeTest.groovy
new file mode 100644
index 0000000..18750ce
--- /dev/null
+++ b/groovy/src/test/groovy/tree/SmallTreeTest.groovy
@@ -0,0 +1,18 @@
+package groovy.tree
+
+class SmallTreeTest extends GroovyTestCase {
+    def b
+    def EXPECTED = 'root1[attributes={}; value=[elem1[attributes={}; value=hello1]]]'
+
+    void testTree() {
+        b = NodeBuilder.newInstance()
+        
+        def root = b.root1( {
+            elem1('hello1')
+        })
+        
+        assert root != null
+        
+        assert EXPECTED == root.toString()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/tree/TreeTest.groovy b/groovy/src/test/groovy/tree/TreeTest.groovy
new file mode 100644
index 0000000..f8dd6a1
--- /dev/null
+++ b/groovy/src/test/groovy/tree/TreeTest.groovy
@@ -0,0 +1,79 @@
+package groovy.tree
+
+/**
+ * This test uses the concise GroovyMarkup syntax to test the building of trees
+ */
+class TreeTest extends GroovyTestCase {
+    
+    void testSmallTree() {
+        def b = NodeBuilder.newInstance()
+        
+        def root = b.root1(a:5, b:7) {
+            elem1('hello1')
+            elem2('hello2')
+            elem3(x:7)
+        }
+        
+        assert root != null
+        
+        print(root)
+    }
+    
+    void testTree() {
+        def b = NodeBuilder.newInstance()
+        
+        def root = b.root2(a:5, b:7) {
+            elem1('hello1')
+            elem2('hello2')
+            nestedElem(x:'abc', y:'def') {
+                child(z:'def')
+                child2()  
+            }
+            
+            nestedElem2(z:'zzz') {
+                child(z:'def')
+                child2("hello")  
+            }
+        }
+        
+        assert root != null
+        
+        print(root)
+
+        def e1 = root.elem1.get(0)
+        assert e1.value() == 'hello1'
+        
+        def e2 = root.elem2.get(0)
+        assert e2.value() == 'hello2'
+
+        assert root.elem1.get(0).value() == 'hello1'
+        assert root.elem2.get(0).value() == 'hello2'
+
+        assert root.nestedElem.get(0).attributes() == ['x':'abc', 'y':'def']        
+        assert root.nestedElem.child.get(0).attributes() == ['z':'def']
+        
+        assert root.nestedElem.child2.get(0).value() == []
+        assert root.nestedElem.child2.get(0).text() == ''
+
+        assert root.nestedElem2.get(0).attributes() == ['z':'zzz']      
+        assert root.nestedElem2.child.get(0).attributes() == ['z':'def']
+        assert root.nestedElem2.child2.get(0).value() == 'hello'
+        assert root.nestedElem2.child2.get(0).text() == 'hello'
+        
+        def list = root.value()
+        assert list.size() == 4
+
+        assert root.attributes().a == 5
+        assert root.attributes().b == 7
+        
+        assert root.nestedElem.get(0).attributes().x == 'abc'
+        assert root.nestedElem.get(0).attributes().y == 'def'
+        assert root.nestedElem2.get(0).attributes().z == 'zzz'
+        assert root.nestedElem2.child.get(0).attributes().z == 'def'
+        
+        /** @todo parser add .@ as an operation
+                assert root.@a == 5
+                assert root.@b == 7
+        */        
+    }
+}
diff --git a/groovy/src/test/groovy/tree/VerboseTreeTest.groovy b/groovy/src/test/groovy/tree/VerboseTreeTest.groovy
new file mode 100644
index 0000000..5054589
--- /dev/null
+++ b/groovy/src/test/groovy/tree/VerboseTreeTest.groovy
@@ -0,0 +1,82 @@
+package groovy.tree
+
+/**
+ * This test uses the verbose syntax to test the building of trees
+ * using GroovyMarkup
+ */
+class VerboseTreeTest extends GroovyTestCase {
+    
+    def b
+
+    void testSmallTree() {
+        b = NodeBuilder.newInstance()
+        
+        def root = b.root1(['a':5, 'b':7], {
+            elem1('hello1')
+            elem2('hello2')
+            elem3(['x':7])
+        })
+        
+        assert root != null
+        
+        print(root)
+    }
+    
+    void testTree() {
+        b = NodeBuilder.newInstance()
+        
+        def root = b.root2(['a':5, 'b':7], {
+            elem1('hello1')
+            elem2('hello2')
+            nestedElem(['x':'abc', 'y':'def'], {
+                child(['z':'def'])
+                child2()  
+            })
+            
+            nestedElem2(['z':'zzz'], {
+                child(['z':'def'])
+                child2("hello")  
+            })
+        })
+        
+        assert root != null
+        
+        print(root)
+
+        def e1 = root.elem1.get(0)
+        assert e1.value() == 'hello1'
+        
+        def e2 = root.elem2.get(0)
+        assert e2.value() == 'hello2'
+
+        assert root.elem1.get(0).value() == 'hello1'
+        assert root.elem2.get(0).value() == 'hello2'
+
+        assert root.nestedElem.get(0).attributes() == ['x':'abc', 'y':'def']        
+        assert root.nestedElem.child.get(0).attributes() == ['z':'def']
+        
+        assert root.nestedElem.child2.get(0).value() == []
+        assert root.nestedElem.child2.get(0).text() == ''
+
+        assert root.nestedElem2.get(0).attributes() == ['z':'zzz']      
+        assert root.nestedElem2.child.get(0).attributes() == ['z':'def']
+        assert root.nestedElem2.child2.get(0).value() == 'hello'
+        assert root.nestedElem2.child2.get(0).text() == 'hello'
+        
+        def list = root.value()
+        assert list.size() == 4
+
+        assert root.attributes().a == 5
+        assert root.attributes().b == 7
+        
+        assert root.nestedElem.get(0).attributes().x == 'abc'
+        assert root.nestedElem.get(0).attributes().y == 'def'
+        assert root.nestedElem2.get(0).attributes().z == 'zzz'
+        assert root.nestedElem2.child.get(0).attributes().z == 'def'
+        
+        /** @todo parser add .@ as an operation
+                assert root.@a == 5
+                assert root.@b == 7
+        */        
+    }
+}
diff --git a/groovy/src/test/groovy/txn/TransactionBean.java b/groovy/src/test/groovy/txn/TransactionBean.java
new file mode 100644
index 0000000..60e5fac
--- /dev/null
+++ b/groovy/src/test/groovy/txn/TransactionBean.java
@@ -0,0 +1,37 @@
+package groovy.txn;
+
+import groovy.lang.Closure;
+
+/**
+ * @author James Strachan
+ * @version $Revision$
+ */
+public class TransactionBean {
+    private Closure run;
+    private Closure onError;
+    private Closure onSuccess;
+
+    public Closure run() {
+        return run;
+    }
+
+    public Closure onError() {
+        return onError;
+    }
+
+    public Closure onSuccess() {
+        return onSuccess;
+    }
+
+    public void run(Closure run) {
+        this.run = run;
+    }
+
+    public void onError(Closure onError) {
+        this.onError = onError;
+    }
+
+    public void onSuccess(Closure onSuccess) {
+        this.onSuccess = onSuccess;
+    }
+}
diff --git a/groovy/src/test/groovy/txn/TransactionBuilder.java b/groovy/src/test/groovy/txn/TransactionBuilder.java
new file mode 100644
index 0000000..4dad74c
--- /dev/null
+++ b/groovy/src/test/groovy/txn/TransactionBuilder.java
@@ -0,0 +1,24 @@
+package groovy.txn;
+
+import groovy.lang.Closure;
+
+/**
+ * @author James Strachan
+ * @version $Revision$
+ */
+public class TransactionBuilder {
+    public void transaction(Closure closure) {
+        TransactionBean bean = new TransactionBean();
+        closure.setDelegate(bean);
+        closure.call(this);
+
+        // lets call the closures now
+        System.out.println("Performing normal transaction");
+        bean.run().call(this);
+        bean.onSuccess().call(this);
+
+        System.out.println("Performing error transaction");
+        bean.run().call(this);
+        bean.onError().call(this);
+    }
+}
diff --git a/groovy/src/test/groovy/txn/TransactionTest.groovy b/groovy/src/test/groovy/txn/TransactionTest.groovy
new file mode 100644
index 0000000..3e5b9b7
--- /dev/null
+++ b/groovy/src/test/groovy/txn/TransactionTest.groovy
@@ -0,0 +1,20 @@
+package groovy.txn
+
+class TransactionTest extends GroovyTestCase {
+
+    void testTxn() {
+		def builder = new TransactionBuilder()
+		builder.transaction {
+		    run {
+		        println("run code!")
+		    }
+		    onError {
+                println("on error!")
+		    }
+		    onSuccess {
+                println("on success!")
+		    }
+		}
+    }
+
+}
diff --git a/groovy/src/test/groovy/ui/GroovyMainTest.groovy b/groovy/src/test/groovy/ui/GroovyMainTest.groovy
new file mode 100644
index 0000000..a190334
--- /dev/null
+++ b/groovy/src/test/groovy/ui/GroovyMainTest.groovy
@@ -0,0 +1,38 @@
+package groovy.ui
+
+class GroovyMainTest extends GroovyTestCase {
+    private baos = new ByteArrayOutputStream()
+    private ps = new PrintStream(baos)
+
+    void testHelp() {
+        String[] args = ['-h']
+        GroovyMain.processArgs(args, ps)
+        def out = baos.toString()
+        assert out.contains('usage: groovy')
+        ['-a', '-c', '-d', '-e', '-h', '-i', '-l', '-n', '-p', '-v'].each{
+            assert out.contains(it)
+        }
+    }
+
+    void testVersion() {
+        String[] args = ['-v']
+        GroovyMain.processArgs(args, ps)
+        def out = baos.toString()
+        assert out.contains('Groovy Version:')
+        assert out.contains('JVM:')
+    }
+
+    void testNoArgs() {
+        String[] args = []
+        GroovyMain.processArgs(args, ps)
+        def out = baos.toString()
+        assert out.contains('error: neither -e or filename provided')
+    }
+
+    void testAttemptToRunJavaFile() {
+        String[] args = ['abc.java']
+        GroovyMain.processArgs(args, ps)
+        def out = baos.toString()
+        assert out.contains('error: error: cannot compile file with .java extension: abc.java')
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/util/AllTestSuiteTest.groovy b/groovy/src/test/groovy/util/AllTestSuiteTest.groovy
new file mode 100644
index 0000000..62f6fb1
--- /dev/null
+++ b/groovy/src/test/groovy/util/AllTestSuiteTest.groovy
@@ -0,0 +1,53 @@
+package groovy.util
+
+import java.util.logging.Level
+import junit.framework.Test
+
+/**
+    Testing groovy.util.AllTestSuite.
+    The suite() method must properly collect Test files under the given dir and pattern,
+    add found files to the log,
+    produce a proper TestSuite,
+    and wrap Scripts into TestCases.
+    @author Dierk Koenig
+*/
+class AllTestSuiteTest extends GroovyLogTestCase {
+
+    def suite
+
+    void setUp() {
+        suite = null
+    }
+
+    void testSuiteForThisFileOnly() {
+        def result = stringLog(Level.FINEST, 'groovy.util.AllTestSuite') {
+            withProps('src/test/groovy/util','AllTestSuiteTest.groovy') {
+                suite = AllTestSuite.suite()
+            }
+        }
+        assertTrue result, result.contains('AllTestSuiteTest.groovy')
+        assertEquals 1+1, result.count("\n")   // only one entry in the log
+        assert suite, 'Resulting suite should not be null'
+        assertEquals 2, suite.countTestCases() // the 2 test methods in this file
+    }
+
+    void testAddingScriptsThatDoNotInheritFromTestCase() {
+        withProps('src/test/groovy/util','suite/*.groovy') {
+            suite = AllTestSuite.suite()
+        }
+        assert suite
+        assertEquals 1, suite.countTestCases()
+        suite.testAt(0) // call the contained Script to makes sure it is testable
+    }
+
+    /** store old System property values for not overriding them accidentally */
+    void withProps(dir, pattern, yield) {
+        String olddir = System.properties.'groovy.test.dir'
+        String oldpat = System.properties.'groovy.test.pattern'
+        System.properties.'groovy.test.dir' = dir
+        System.properties.'groovy.test.pattern' = pattern
+        yield()
+        if (olddir) System.properties.'groovy.test.dir' = olddir
+        if (oldpat) System.properties.'groovy.test.pattern' = oldpat
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/util/AntTest.groovy b/groovy/src/test/groovy/util/AntTest.groovy
new file mode 100644
index 0000000..7f01dd3
--- /dev/null
+++ b/groovy/src/test/groovy/util/AntTest.groovy
@@ -0,0 +1,238 @@
+package groovy.util
+
+import java.io.File
+import org.apache.tools.ant.BuildEvent
+import org.apache.tools.ant.Project
+import org.apache.tools.ant.ProjectHelper
+import groovy.xml.NamespaceBuilder
+
+/**
+Tests for the <groovy> task.
+@Author Unknown
+@Author Marc Guillemot
+*/
+class AntTest extends GroovyTestCase {
+    
+    void testAnt() {
+    	def ant = new AntBuilder()
+
+        // lets just call one task
+        ant.echo("hello")
+        
+        // here"s an example of a block of Ant inside GroovyMarkup
+        ant.sequential {
+            echo("inside sequential")
+            
+            def myDir = "target/AntTest/"
+            
+            mkdir(dir:myDir) 
+            copy(todir:myDir) {
+                fileset(dir:"src/test") {
+                    include(name:"**/*.groovy")
+                }
+            }
+            
+            echo("done")
+        }
+        
+        // now lets do some normal Groovy again
+        def file = new File("target/AntTest/groovy/util/AntTest.groovy")
+        assert file.exists()
+    }
+    
+    void testFileIteration() {
+        def ant = new AntBuilder()
+        
+        // lets create a scanner of filesets
+        def scanner = ant.fileScanner {
+            fileset(dir:"src/test") {
+                include(name:"**/Ant*.groovy")
+            }
+        }
+        
+        // now lets iterate over 
+        def found = false
+        for (f in scanner) {
+            println("Found file ${f}")
+            
+            found = true
+            
+            assert f instanceof File
+            assert f.name.endsWith(".groovy")
+        }
+        assert found
+    }
+    
+    void testJunitTask() {
+        def ant = new AntBuilder()
+        
+        ant.junit {
+            test(name:'groovy.util.SomethingThatDoesNotExist')
+        }
+    }
+    
+    void testPathBuilding() {
+        def ant = new AntBuilder()
+        
+        def value = ant.path {
+            fileset(dir:"xdocs") {
+                include(name:"*.wiki")
+            }
+        }
+
+        assert value != null
+        assertEquals org.apache.tools.ant.types.Path, value.getClass()
+    }
+
+    void testTaskContainerExecutionSequence() {
+        SpoofTaskContainer.getSpoof().length = 0
+
+        def antFile = new File("src/test/groovy/util/AntTest.xml")
+        assertTrue "Couldn't find ant test script", antFile.exists()
+
+		// run it with ant, to be sure that our assumptions are correct
+		def project = new Project()
+		project.init()
+		ProjectHelper.projectHelper.parse(project, antFile)
+		project.executeTarget(project.defaultTarget);
+		
+        def expectedSpoof =
+"""SpoofTaskContainer ctor
+in addTask
+configuring UnknownElement
+SpoofTask ctor
+begin SpoofTaskContainer execute
+begin SpoofTask execute
+tag name from wrapper: spoof
+attributes map from wrapper: ["foo":"123"]
+param foo: 123
+end SpoofTask execute
+end SpoofTaskContainer execute
+"""
+		println SpoofTaskContainer.getSpoof().toString()
+        assertEquals expectedSpoof, SpoofTaskContainer.getSpoof().toString()
+        SpoofTaskContainer.spoof.length = 0
+
+        def ant = new AntBuilder()
+        def PATH = 'task.path'
+
+		// and now run it with the AntBuilder        
+        ant.path(id:PATH) {ant.pathelement(location:'classes')}
+        ['spoofcontainer': SpoofTaskContainer, 'spoof': SpoofTask].each{ pair ->
+            ant.taskdef(name:pair.key, classname: pair.value.name, classpathref: PATH)
+        }
+        ant.spoofcontainer(){
+            ant.spoof(foo: 123)
+        }
+        assertEquals expectedSpoof, SpoofTaskContainer.getSpoof().toString()
+        
+        // now run it with AntBuilder using Namespaces (test for GROOVY-1070)
+        def antNS = new AntBuilder()
+        SpoofTaskContainer.resetSpoof()
+
+		// and now run it with the AntBuilder        
+        antNS.path(id:PATH) {antNS.pathelement(location:'classes')}
+        ['spoofcontainer': SpoofTaskContainer, 'spoof': SpoofTask].each{ pair ->
+            antNS.taskdef(name:pair.key, classname: pair.value.name, classpathref: PATH, 
+                          uri: 'testNS')
+        }
+		def testNS = NamespaceBuilder.newInstance(antNS,"testNS","testNSprefix");
+        testNS.spoofcontainer(){
+            testNS.spoof(foo: 123)
+        }
+        assertEquals expectedSpoof, SpoofTaskContainer.getSpoof().toString()
+    }
+
+    /** Checks that we can access dynamically (through Ant's property task) defined properties in Groovy scriptlets */
+    void testDynamicProperties() {
+        def antBuilder = new AntBuilder()
+
+        antBuilder.property(name: "testProp1", value: "TEST 1")
+        antBuilder.taskdef(name:"groovy", classname:"org.codehaus.groovy.ant.Groovy")
+        antBuilder.groovy("""
+            ant.property(name: "testProp2", value: "TEST 2")
+
+            assert properties.testProp1 == project.properties.testProp1
+            assert properties.testProp2 == project.properties.testProp2
+        """)
+    }
+    
+    /**
+    * Test access to AntBuilder properties
+    */
+    void testAntBuilderProperties() {
+        def ant = new AntBuilder()
+        
+        assertNull ant.project.properties.'myProp'
+        ant.property(name: 'myProp', value: 'blabla')
+        assertEquals 'blabla', ant.project.properties.'myProp'
+    }
+    
+    /**
+    * Tests that the AntBuilder can handle conditions (conditions aren't tasks)
+    * (test for GROOVY-824)
+    */
+    void testCondition() {
+        def ant = new AntBuilder()
+        ant.condition(property: "containsHi") {
+        	contains([string: "hi", substring: "hi"])
+        }
+        assertEquals "true", ant.project.properties["containsHi"]
+
+        ant.condition(property: "equalsHi", else: "false") {
+        	Equals([arg1: "hi", arg2: "bye"])
+        }
+        assertEquals "false", ant.project.properties["equalsHi"]
+    }
+
+    /**
+     * Tests that using the AntBuilder within the <groovy> task doesn't cause double execution
+     * (test for GROOVY-1602)
+     */
+     void testAntBuilderWithinGroovyTask() {
+        def antFile = new File("src/test/groovy/util/AntTest.xml")
+        assertTrue "Couldn't find ant test script", antFile.exists()
+
+        def project = new Project()
+		project.init()
+		ProjectHelper.projectHelper.parse(project, antFile)
+		
+		def customListener = new SimpleListener()
+ 		project.addBuildListener customListener
+		
+		project.executeTarget("testAntBuilderWithinGroovyTask");
+ 		
+		def expectedSpoof =
+"""started: taskdef["name":"groovy", "classname":"org.codehaus.groovy.ant.Groovy"]
+finished: taskdef["name":"groovy", "classname":"org.codehaus.groovy.ant.Groovy"]
+started: echo["message":"before groovy task"]
+finished: echo["message":"before groovy task"]
+started: groovy[:]
+started: echo["message":"ant builder within groovy task"]
+finished: echo["message":"ant builder within groovy task"]
+finished: groovy[:]
+started: echo["message":"after groovy task"]
+finished: echo["message":"after groovy task"]
+"""
+
+         assertEquals expectedSpoof, customListener.spoof.toString()
+     }
+}
+
+
+class SimpleListener extends org.apache.tools.ant.DefaultLogger
+{
+	def spoof = new StringBuffer()
+	void taskStarted(BuildEvent event)
+	{
+		spoof << "started: " + logTask(event.task) + "\n"
+	}
+	void taskFinished(BuildEvent event)
+	{
+		spoof << "finished: " + logTask(event.task) + "\n"
+	}
+	private String logTask(task)
+	{
+		task.taskName + task.wrapper.attributeMap
+	}
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/util/AntTest.xml b/groovy/src/test/groovy/util/AntTest.xml
new file mode 100755
index 0000000..4179536
--- /dev/null
+++ b/groovy/src/test/groovy/util/AntTest.xml
@@ -0,0 +1,27 @@
+<!-- 
+does exactly the same as the AntBuilder in AntTest... just to be sure that ant behaves the same
+as what we expect from the AntBuilder
+ -->
+<project name="test" default="full">
+
+    <target name="full">
+        <path id="task.path">
+            <pathelement location="classes"/>
+        </path>
+        <taskdef name="spoofcontainer" classname="groovy.util.SpoofTaskContainer" classpathref="task.path"/>
+        <taskdef name="spoof" classname="groovy.util.SpoofTask" classpathref="task.path"/>
+
+        <spoofcontainer>
+            <spoof foo="123"/>
+        </spoofcontainer>
+    </target>
+	
+	<target name="testAntBuilderWithinGroovyTask" description="Test for GROOVY-1602">
+		<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"/>
+		<echo message="before groovy task"/>
+		<groovy>
+			ant.echo(message: 'ant builder within groovy task')
+		</groovy>
+		<echo message="after groovy task"/>
+	</target>
+</project>
diff --git a/groovy/src/test/groovy/util/BuilderSupportTest.groovy b/groovy/src/test/groovy/util/BuilderSupportTest.groovy
new file mode 100644
index 0000000..6133ddd
--- /dev/null
+++ b/groovy/src/test/groovy/util/BuilderSupportTest.groovy
@@ -0,0 +1,185 @@
+package groovy.util
+
+import groovy.lang.MissingMethodException
+
+/**
+     Testing BuilderSupport and reveal how calling
+    methods on it result in implementation callbacks.
+    Using the SpoofBuilder (see below) to call it in various ways
+    and analyze the "spoofed" logs.
+    This is especially useful when designing subclasses of BuilderSupport.
+     @author Dierk Koenig
+**/
+
+
+class BuilderSupportTest extends GroovyTestCase{
+    void testSimpleNode() {
+        def b = new SpoofBuilder()
+        assert b.log == []
+        def node = b.foo()
+        assert b.log == [  'create_with_name','foo',
+                           'node_completed',null, node, 
+                           'post_node_completion',null, node
+                        ]
+    }
+
+    void testSimpleNodeWithValue() {
+        def b = new SpoofBuilder()
+        def node = b.foo('value')
+        assert b.log == [  'create_with_name_and_value','foo',
+                           'value', 'node_completed',null,node,
+                           'post_node_completion',null, node
+                        ]
+    }
+
+    void testSimpleNodeWithOneAttribute() {
+        def b = new SpoofBuilder()
+        def node = b.foo(name:'value')
+        assert b.log == [
+                           'create_with_name_and_map','foo', 
+                           'name','value', 
+                           'node_completed',null,'x',
+                           'post_node_completion',null, 'x'
+                        ]
+    }
+
+    void testSimpleNodeWithClosure() {
+        def b = new SpoofBuilder()
+        b.foo(){
+            b.bar()
+        }
+        assert b.log == [
+            'create_with_name','foo',
+                'create_with_name','bar',
+            'set_parent', 'x', 'x',
+                'node_completed','x','x',
+                'post_node_completion', 'x', 'x',
+            'node_completed',null,'x',
+            'post_node_completion',null, 'x'
+            ]
+    }
+
+    void testSimpleNodeWithOneAttributeAndValue() {
+        def b = new SpoofBuilder()
+        def node = b.foo(bar:'baz', 'value')
+        assert b.log == [
+                          'create_with_name_map_and_value', 'foo', 'bar', 'baz','value', 
+                          'node_completed',null,node,
+                          'post_node_completion',null, node
+                        ]
+    }
+
+    void testSimpleNodeWithValueAndOneAttribute() {
+        def b = new SpoofBuilder()
+        def node = b.foo('value', bar:'baz')
+        assert b.log == [
+                          'create_with_name_map_and_value', 'foo', 'bar', 'baz','value', 
+                          'node_completed',null,node,
+                          'post_node_completion',null, node
+                        ]
+    }
+
+    void testSimpleNodeWithOneAttributeAndValueAndClosure() {
+        def b = new SpoofBuilder()
+        def node = b.foo(bar:'baz', 'value') { 1 }
+        assert b.log == [
+                          'create_with_name_map_and_value', 'foo', 'bar', 'baz','value', 
+                          'node_completed',null,node,
+                          'post_node_completion',null, node
+                        ]
+    }
+
+    void testSimpleNodeWithValueAndOneAttributeAndClosure() {
+        def b = new SpoofBuilder()
+        def node = b.foo('value', bar:'baz') { 1 }
+        assert b.log == [
+                          'create_with_name_map_and_value', 'foo', 'bar', 'baz','value', 
+                          'node_completed',null,node,
+                          'post_node_completion',null, node
+                        ]
+    }
+
+    void testSimpleNodeTwoValues() {
+        def b = new SpoofBuilder()
+        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2')})
+    }
+
+    void testSimpleNodeTwoValuesClosure() {
+        def b = new SpoofBuilder()
+        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2') { 1 } })
+    }
+
+    void testSimpleNodeThreeValues() {
+        def b = new SpoofBuilder()
+        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2', 'arg3') })
+    }
+
+    void testSimpleNodeFourValues() {
+        def b = new SpoofBuilder()
+        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2', 'arg3', 'arg4') })
+    }
+
+    void testNestedMethodCallsResolution() {
+        def b = new SpoofBuilder()
+        b.outest {
+            b.outer {
+                nestedBuilderCall(b)
+            }
+        }
+        assert b.log.contains('inner') 
+    }
+
+    void nestedBuilderCall(builder) {
+        builder.inner()
+    }
+}
+
+/**
+    The SpoofBuilder is a sample instance of the abstract BuilderSupport class
+    that does nothing but logging how it was called, returning 'x' for each node.
+**/
+class SpoofBuilder extends BuilderSupport{
+    def log = []
+    
+    protected void setParent(Object parent, Object child){
+        log << "set_parent"
+        log << parent
+        log << child
+    }
+    protected Object createNode(Object name){
+        log << 'create_with_name'
+        log <<  name
+        return 'x'
+    }
+    protected Object createNode(Object name, Object value){
+        log << 'create_with_name_and_value'
+        log << name
+        log << value
+        return 'x'
+    }
+    protected Object createNode(Object name, Map attributes){
+        log << 'create_with_name_and_map'
+        log << name
+        attributes.each{entry -> log << entry.key; log << entry.value}
+        return 'x'
+    }
+    protected Object createNode(Object name, Map attributes, Object value){
+        log << 'create_with_name_map_and_value'
+        log << name
+        attributes.each{entry -> log << entry.key; log << entry.value}
+        log << value
+        return 'x'
+    }
+    protected void nodeCompleted(Object parent, Object node) {
+        log << 'node_completed'
+        log << parent
+        log << node
+    }
+    
+    protected Object postNodeCompletion(Object parent, Object node) {
+        log << 'post_node_completion'
+        log << parent
+        log << node
+        node
+    }
+}
diff --git a/groovy/src/test/groovy/util/CliBuilderTest.groovy b/groovy/src/test/groovy/util/CliBuilderTest.groovy
new file mode 100644
index 0000000..6a5a065
--- /dev/null
+++ b/groovy/src/test/groovy/util/CliBuilderTest.groovy
@@ -0,0 +1,388 @@
+/*
+ *  Copyright 2003-2007 the original author or authors.
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in
+ *  compliance with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software distributed under the License is
+ *  distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ *  implied.  See the License for the specific language governing permissions and limitations under the
+ *  License.
+ */
+
+package groovy.util
+
+import org.apache.commons.cli.GnuParser
+import org.apache.commons.cli.Option
+import org.apache.commons.cli.OptionBuilder
+import org.apache.commons.cli.PosixParser
+import org.apache.commons.cli.BasicParser
+
+/**
+ *  Test class for the CliBuilder -- but then that is obvious from the name :-)
+ *
+ *  <p>There appear to be issues when using the <code>PosixParser</code> &ndash; when an option with a
+ *  parameter is passed using a long form and a single letter parameter of some sort has been declared (the
+ *  problem does not occur if no single letter option has been declared) then the value "--" is returned
+ *  instead of the option parameter value.  This problem does not happen using the
+ *  <code>GnuParser</code>.</p>
+ *
+ *  <p>There appears to be an issue with <code>GnuParser</code> &ndash; if only a long option is defined
+ *  then the usual Groovy syntax for accessing the option fails to work.  It is fine if a short option of
+ *  some sort is defined.  This must be a <code>CliBuilder</code>/<code>OptionAccessor</code> problem.  This
+ *  problem does not happen with the <code>PosixParser</code>.</p>
+ *
+ *  <p>Commons CLI 1.0 appears not to be able to access arguments using a long name, if that option has a
+ *  short name -- in this case access is only using a short name.  This means it is possible to work with
+ *  long name option if and only if they have no short name.</p>
+ *
+ *  <p>Commons CLI 1.1 whilst it has fixed most of the problems in 1.0, appears to have a broken
+ *  getOptionValues -- it returns only the first value -- and so is worse than useless.</p>
+ *
+ *  <p>1.0 PosixBuilder removes unrecognized single letter options silently.  1.1 version may also do this.
+ *  GnuParser behaves according to the <code>stopAtNonOption</code> parameter -- throw
+ *  <code>UnrecognizedOptionException</code> when <code>false</code>, terminate parse leaving everything
+ *  following unprocessed if <code>true</code>.
+ *
+ *  @author Dierk König
+ *  @author Russel Winder
+ */
+
+//// Tests marked with 4 slashes work in Commons CLI 1.1 but not in 1.0.  There are other differences, read
+//// the comments.
+
+class CliBuilderTest extends GroovyTestCase {
+
+  private StringWriter stringWriter
+  private PrintWriter printWriter
+
+  void setUp ( ) {
+    stringWriter = new StringWriter ( )
+    printWriter = new PrintWriter ( stringWriter )
+  }
+
+  private final expectedParameter = 'ASCII'
+  private final usageString =  'groovy [option]* filename'
+
+  private void runSample ( parser , optionList ) {
+    def cli = new CliBuilder ( usage : usageString , writer : printWriter , parser : parser )
+    cli.h ( longOpt : 'help', 'usage information' )
+    cli.c ( argName : 'charset' , args :1 , longOpt : 'encoding' , 'character encoding' )
+    cli.i ( argName : 'extension' , optionalArg : true, 'modify files in place, create backup if extension is given (e.g. \'.bak\')' )
+    def stringified = cli.options.toString ( )
+    assert stringified =~ /i=. option: i  :: modify files in place, create backup if extension is given/
+    assert stringified =~ /c=. option: c encoding  :: character encoding/
+    assert stringified =~ /h=. option: h help  :: usage information/
+    assert stringified =~ /encoding=. option: c encoding  :: character encoding/
+    assert stringified =~ /help=. option: h help  :: usage information/
+    def options = cli.parse ( optionList )
+    assert options.hasOption ( 'h' )
+    ////assert options.hasOption ( 'help' )
+    assert options.h
+    ////assert options.help
+    if ( options.h ) { cli.usage ( ) }
+    def expectedUsage = """usage: $usageString
+ -c,--encoding <charset>   character encoding
+ -h,--help                 usage information
+ -i                        modify files in place, create backup if
+                           extension is given (e.g. '.bak')"""
+    assertEquals ( expectedUsage , stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
+    stringWriter = new StringWriter ( )
+    printWriter = new PrintWriter ( stringWriter )
+    cli.writer = printWriter
+    ////if ( options.help ) { cli.usage ( ) }
+    ////assertEquals ( expectedUsage , stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
+    assert options.hasOption ( 'c' )
+    assert options.c
+    ////assert options.hasOption ( 'encoding' )
+    ////assert options.encoding
+    assertEquals ( expectedParameter, options.getOptionValue ( 'c' ) )
+    assertEquals ( expectedParameter, options.c )
+    ////assertEquals ( expectedParameter, options.getOptionValue ( 'encoding' ) )
+    ////assertEquals ( expectedParameter, options.encoding )
+    assertEquals ( false, options.noSuchOptionGiven )
+    assertEquals ( false, options.hasOption ( 'noSuchOptionGiven' ) )
+    assertEquals ( false, options.x )
+    assertEquals ( false, options.hasOption ( 'x' ) )
+  }
+  void testSampleShort_BasicParser ( ) {
+    runSample ( new BasicParser ( ) , [ '-h' , '-c' , expectedParameter ] )
+  }
+  void testSampleShort_GnuParser ( ) {
+    runSample ( new GnuParser ( ) , [ '-h' , '-c' , expectedParameter ] )
+  }
+  void testSampleShort_PosixParser ( ) {
+    runSample ( new PosixParser ( ) , [ '-h' , '-c' , expectedParameter ] )
+  }
+  void testSampleLong_BasicParser ( ) {
+    runSample ( new GnuParser ( ) , [ '--help' , '--encoding' , expectedParameter ] )
+  }
+  void testSampleLong_GnuParser ( ) {
+    runSample ( new GnuParser ( ) , [ '--help' , '--encoding' , expectedParameter ] )
+  }
+  /*
+   *  Cannot run this test because of the "--" instead of "ASCII" problem.  See
+   *  testLongAndShortOpts_PosixParser below.  This is a 1.0 and a 1.1 problem.
+   */
+  void XXX_testSampleLong_PosixParser ( ) {
+    runSample ( new PosixParser ( ) , [ '--help' , '--encoding' , expectedParameter ] )
+  }
+
+  void testMultipleArgs ( ) {
+    def cli = new CliBuilder ( )
+    cli.a ( longOpt : 'arg' , args : 2 , valueSeparator : ',' as char , 'arguments' )
+    def options = cli.parse ( [ '-a' , '1,2' ] )
+    assertEquals ( '1' , options.a )
+    assertEquals ( [ '1' , '2' ] , options.as )
+    ////assertEquals ( '1' , options.arg )
+    ////assertEquals ( [ '1' , '2' ] , options.args )
+  }
+
+  void testArgs ( ) {
+    def cli = new CliBuilder ( )
+    cli.a ( [:] , '' )
+    def options = cli.parse ( [ '-a' , '1' , '2' ] )
+    assertEquals ( [ '1' , '2' ] , options.arguments ( ) )
+  }
+
+  void testFailedParsePrintsUsage ( ) {
+    def cli = new CliBuilder ( writer : printWriter )
+    cli.x ( required : true , 'message' )
+    def options = cli.parse ( [ ] )
+
+    //
+    // FIXME: This test is very fragile and is bound to fail on different locales and versions of commons-cli... :-(
+    //
+      
+    assertEquals ( '''error: -x
+usage: groovy
+ -x   message''',  stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
+    }
+
+  void testLongOptsOnly_GnuParser ( ) {
+    def cli = new CliBuilder ( writer : printWriter , parser : new GnuParser ( ) )
+    def anOption = OptionBuilder.withLongOpt ( 'anOption' ).hasArg ( ).withDescription ( 'An option.' ).create ( )
+    cli.options.addOption ( anOption )
+    def options = cli.parse ( [ '-v' , '--anOption' , 'something' ] )
+    
+    assert options.getOptionValue ( 'anOption' ) == null
+    assert false == options.anOption
+  }
+  
+  void testLongOptsOnly_PosixParser ( ) {
+    def cli = new CliBuilder ( writer : printWriter , parser : new PosixParser ( ) )
+    def anOption = OptionBuilder.withLongOpt ( 'anOption' ).hasArg ( ).withDescription ( 'An option.' ).create ( )
+    cli.options.addOption ( anOption )
+    def options = cli.parse ( [ '-v' , '--anOption' , 'something' ] )
+    cli.usage ( )
+    
+    assert 'something' == options.getOptionValue ( 'anOption' )
+    assert 'something' == options.anOption
+    
+    assert ! options.v
+  }
+
+  private createOptionsWithLongAndShortOpts ( parser ) {
+    def cli = new CliBuilder ( writer : printWriter , parser : parser )
+    def anOption = OptionBuilder.withLongOpt ( 'anOption' ).hasArg ( ).withDescription ( 'An option.' ).create ( )
+    cli.options.addOption ( anOption )
+    cli.v ( longOpt : 'verbose' , 'verbose mode' )
+    def options = cli.parse ( [ '-v' , '--anOption' , 'something' ] )
+    cli.usage ( )
+    
+    assert options.v
+    
+    return options
+  }
+
+  void testLongAndShortOpts_BasicParser ( ) {
+    def options = createOptionsWithLongAndShortOpts ( new BasicParser ( ) )
+    assertEquals ( 'something' , options.getOptionValue ( 'anOption' ) )
+    assertEquals ( 'something' , options.anOption )
+  }
+
+  void testLongAndShortOpts_PosixParser ( ) {
+    def options = createOptionsWithLongAndShortOpts ( new PosixParser ( ) )
+    //
+    //  This represents what currently happens, not what should happen.  The problem needs investigating so
+    //  that the expected results here can be set to 'something' as it should.
+    //
+    assertEquals ( '--' , options.getOptionValue ( 'anOption' ) )
+    assertEquals ( '--' , options.anOption )
+  }
+
+  void testLongAndShortOpts_GnuParser ( ) {
+    def options = createOptionsWithLongAndShortOpts ( new GnuParser ( ) )
+    assertEquals ( 'something' , options.getOptionValue ( 'anOption' ) )
+    assertEquals ( 'something' , options.anOption )
+  }
+
+  void unknownOptions ( parser ) {
+    def cli = new CliBuilder ( parser : parser )
+    cli.v ( longOpt : 'verbose' , 'verbose mode' )
+    def options = cli.parse ( [ '-x' , '-yyy' , '--zzz' , 'something' ] )
+    assertEquals ( [ '-x' , '-yyy' , '--zzz' , 'something' ] , options.arguments ( ) )
+  }
+  void testUnrecognizedOptions_BasicParser ( ) { unknownOptions ( new BasicParser ( ) ) }
+  void testUnrecognizedOptions_GnuParser ( ) { unknownOptions ( new GnuParser ( ) ) }
+  //
+  //  The Posix Parser absorbs unrecognized options rather than passing them through.  It is not clear if
+  //  this is the correct or incorrect behaviour.
+  //
+  void testUnrecognizedOptions_PosixParser ( ) {
+    def cli = new CliBuilder ( parser : new PosixParser ( ) )
+    cli.v ( longOpt : 'verbose' , 'verbose mode' )
+    def options = cli.parse ( [ '-x' , '-yyy' , '--zzz' , 'something' ] )
+    assertEquals ( [ '-yyy' , '--zzz' , 'something' ] , options.arguments ( ) )
+ }
+
+  void bizarreProcessing ( parser ) {
+    def cli = new CliBuilder ( parser : parser )
+    def options = cli.parse ( [ '-xxxx' ] )
+    assertEquals ( [ '-xxxx' ] , options.arguments ( ) )
+  }
+  void testBizarreProcessing_BasicParser ( ) { bizarreProcessing ( new BasicParser ( ) ) }
+  void testBizarreProcessing_GnuParser ( ) { bizarreProcessing ( new GnuParser ( ) ) }
+  //
+  //  This behaviour by the PosixParser is truly bizarre and so militates in favour of the switch from
+  //  PosixParser to GnuParser as the CliBuilder default.
+  //
+  void testPosixBizarreness ( ) {
+    def cli = new CliBuilder ( parser : new PosixParser ( ) )
+    def options = cli.parse ( [ '-xxx' ] )
+    assertEquals ( [ 'xxx' , 'xx' , 'x' ] , options.arguments ( ) )
+  }
+
+  private void multipleOccurrencesSeparateSeparate ( parser ) {
+    def cli = new CliBuilder ( parser : parser )
+    cli.a ( longOpt : 'arg' , args : Option.UNLIMITED_VALUES , 'arguments' )
+    def options = cli.parse ( [ '-a' , '1' , '-a' , '2' , '-a' , '3' ] )
+    assertEquals ( '1' , options.a )
+    assertEquals ( [ '1' , '2' , '3' ] , options.as )
+    ////assertEquals ( '1' , options.arg )
+    ////assertEquals ( [ '1' , '2' , '3' ] , options.args )
+    assertEquals ( [ ] , options.arguments ( ) )
+  }
+  void testMultipleOccurrencesSeparateSeparate_BasicParser ( ) { multipleOccurrencesSeparateSeparate ( new BasicParser ( ) ) }
+  void testMultipleOccurrencesSeparateSeparate_GnuParser ( ) { multipleOccurrencesSeparateSeparate ( new GnuParser ( ) ) }
+  void testMultipleOccurrencesSeparateSeparate_PosixParser ( ) { multipleOccurrencesSeparateSeparate ( new PosixParser ( ) ) }
+
+  private void multipleOccurrencesSeparateJuxtaposed ( parser ) {
+    def cli = new CliBuilder ( parser : parser )
+    cli.a ( longOpt : 'arg' , args : Option.UNLIMITED_VALUES , 'arguments' )
+    def options = cli.parse ( [ '-a1' , '-a2' , '-a3' ] )
+    assertEquals ( '1' , options.a )
+    assertEquals ( [ '1' , '2' , '3' ] , options.as )
+    ////assertEquals ( '1' , options.arg )
+    ////assertEquals ( [ '1' , '2' , '3' ] , options.args )
+    assertEquals ( [ ] , options.arguments ( ) )
+  }
+  //
+  //  BasicParser cannot handle this one.
+  //
+  //void testMultipleOccurrencesSeparateJuxtaposed_BasicParser ( ) { multipleOccurrencesSeparateJuxtaposed ( new BasicParser ( ) ) }
+  void testMultipleOccurrencesSeparateJuxtaposed_GnuParser ( ) { multipleOccurrencesSeparateJuxtaposed ( new GnuParser ( ) ) }
+  void testMultipleOccurrencesSeparateJuxtaposed_PosixParser ( ) { multipleOccurrencesSeparateJuxtaposed ( new PosixParser ( ) ) }
+
+  private void multipleOccurrencesTogetherSeparate ( parser ) {
+    def cli = new CliBuilder ( parser : parser )
+    cli.a ( longOpt : 'arg' , args : Option.UNLIMITED_VALUES , valueSeparator : ',' as char , 'arguments' )
+    def options = cli.parse ( [ '-a 1,2,3' ] )
+    assertEquals ( ' 1' , options.a )
+    assertEquals ( [ ' 1' , '2' , '3' ] , options.as )
+    ////assertEquals ( ' 1' , options.arg )
+    ////assertEquals ( [ ' 1' , '2' , '3' ] , options.args )
+    assertEquals ( [ ] , options.arguments ( ) )
+  }
+  //
+  //  BasicParser cannot handle this one.
+  //
+  //void testMultipleOccurrencesTogetherSeparate_BasicParser ( ) { multipleOccurrencesTogetherSeparate ( new BasicParser ( ) ) }
+  void testMultipleOccurrencesTogetherSeparate_GnuParser ( ) { multipleOccurrencesTogetherSeparate ( new GnuParser ( ) ) }
+  void testMultipleOccurrencesTogetherSeparate_PosixParser ( ) { multipleOccurrencesTogetherSeparate ( new PosixParser ( ) ) }
+
+  private void multipleOccurrencesTogetherJuxtaposed ( parser ) {
+    def cli = new CliBuilder ( parser : parser )
+    cli.a ( longOpt : 'arg' , args : Option.UNLIMITED_VALUES , valueSeparator : ',' as char , 'arguments' )
+    def options = cli.parse ( [ '-a1,2,3' ] )
+    assertEquals ( '1' , options.a )
+    assertEquals ( [ '1' , '2' , '3' ] , options.as )
+    ////assertEquals ( '1' , options.arg )
+    ////assertEquals ( [ '1' , '2' , '3' ] , options.args )
+    assertEquals ( [ ] , options.arguments ( ) )
+  }
+  //
+  //  BasicParser cannot handle this one.
+  //
+  //void testMultipleOccurrencesTogetherJuxtaposed_BasicParser ( ) { multipleOccurrencesTogetherJuxtaposed ( new BasicParser ( ) ) }
+  void testMultipleOccurrencesTogetherJuxtaposed_GnuParser ( ) { multipleOccurrencesTogetherJuxtaposed ( new GnuParser ( ) ) }
+  void testMultipleOccurrencesTogetherJuxtaposed_PosixParser ( ) { multipleOccurrencesTogetherJuxtaposed ( new PosixParser ( ) ) }
+
+  /*
+   *  Investigate problems with unrecognized options.  Should add the BasicParser here as well.
+   */
+
+  void testUnrecognizedOptionSilentlyIgnored_GnuParser ( ) {
+    def cli = new CliBuilder ( usage : usageString , writer : printWriter , parser : new GnuParser ( ) )
+    def options = cli.parse ( [ '-v' ] )
+    /*
+     *  The behaviour here depends on whether stopAtNonOption is true or false in the call to parser.parse
+     *  in CliBuilder.parse.  Currently this is true, so parse simply terminates.  Any options after the
+     *  unrecognized one are unprocessed.
+     *
+    assertEquals ( '''error: Unrecognized option: -v
+usage: groovy [option]* filename''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
+    assertEquals ( null , options )
+    */
+    assertEquals ( '''''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
+    assert !options.v
+  }
+  void testUnrecognizedOptionSilentlyIgnored_PosixParser ( ) {
+    //
+    //  The PosixParser silently absorbs unrecognized options.
+    //
+    def cli = new CliBuilder ( usage : usageString , writer : printWriter , parser : new PosixParser ( ) )
+    def options = cli.parse ( [ '-v' ] )
+    assertEquals ( '''''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
+    assert ! options.v
+  }
+  void testUnrecognizedOptionTerminatesParse_GnuParser ( ) {
+    def cli = new CliBuilder ( usage : usageString , writer : printWriter , parser : new GnuParser ( ) )
+    cli.h ( longOpt : 'help', 'usage information' )
+    def options = cli.parse ( [ '-v' , '-h' ] )
+    /*
+     *  The behaviour here depends on whether stopAtNonOption is true or false in the call to parser.parse
+     *  in CliBuilder.parse.  Currently this is true, so parse simply terminates.  Any options after the
+     *  unrecognized one are unprocessed.
+     *
+    assertEquals ( '''error: Unrecognized option: -v
+usage: groovy [option]* filename''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
+    assertEquals ( null , options )
+    */
+    assertEquals ( '''''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
+    assert !options.v
+    //
+    //  Why is options.h not recognized here.  This is weird.
+    //
+    //assert ! options.h
+    assertEquals ( [ '-v' , '-h' ] , options.arguments ( ) )
+  }
+  void testUnrecognizedOptionTerminatesParse_PosixParser ( ) {
+    //
+    //  The PosixParser silently absorbs unrecognized options.
+    //
+    def cli = new CliBuilder ( usage : usageString , writer : printWriter , parser : new PosixParser ( ) )
+    cli.h ( longOpt : 'help', 'usage information' )
+    def options = cli.parse ( [ '-v' , '-h' ] )
+    assertEquals ( '''''', stringWriter.toString ( ).tokenize ( '\r\n' ).join ( '\n' ) )
+    assert ! options.v
+    //
+    //  Why is options.h not recognized here.  This is weird.
+    //
+    //assert ! options.h
+    assertEquals ( [ ] , options.arguments ( ) )
+  }
+
+}
diff --git a/groovy/src/test/groovy/util/ConfigSlurperTest.groovy b/groovy/src/test/groovy/util/ConfigSlurperTest.groovy
new file mode 100644
index 0000000..f102ddc
--- /dev/null
+++ b/groovy/src/test/groovy/util/ConfigSlurperTest.groovy
@@ -0,0 +1,455 @@
+/* Copyright 2006-2007 Graeme Rocher
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util
+/**
+ * Tests for the ConfigSlurper class
+ 
+ * @author Graeme Rocher
+ * @since 0.6
+  *
+ * Created: Jun 19, 2007
+ * Time: 6:29:33 PM
+ * 
+ */
+
+class  ConfigSlurperTest extends GroovyTestCase {
+
+
+    void testConfigSlurperNestedValues() {
+        def config = new ConfigSlurper().parse('''
+foo {
+    bar {
+        password="value"
+    }
+    fruit="orange"
+}
+''')
+
+        assertEquals "value", config.foo.bar.password
+        assertEquals "orange", config.foo.fruit
+        config = new ConfigSlurper().parse('''
+            foo {
+                bar.password="value"
+                fruit="orange"
+            }
+            ''')
+
+        assertEquals "value", config.foo.bar.password
+        assertEquals "orange", config.foo.fruit
+
+    }
+
+    void testConfigBinding() {
+		def slurper = new ConfigSlurper()
+		slurper.binding = [foo:"bar"]
+		def config = slurper.parse('''
+test=foo + 1		
+		''')
+		assertEquals "bar1", config.test
+
+	}
+
+    void testEnvironmentProperties2() {
+        def config = new ConfigSlurper("production").parse('''
+dataSource {
+	pooling = false
+	driverClassName = "org.hsqldb.jdbcDriver"
+	username = "sa"
+	password = ""
+}
+environments {
+	development {
+		dataSource {
+			dbCreate = "create-drop"
+			url = "jdbc:hsqldb:mem:devDB"
+		}
+	}
+	test {
+		dataSource {
+			dbCreate = "update"
+			url = "jdbc:hsqldb:mem:testDb"
+		}
+	}
+	production {
+		dataSource {
+		    password = "secret"
+			dbCreate = "update"
+			url = "jdbc:hsqldb:file:prodDb;shutdown=true"
+		}
+	}
+}''')
+
+        assertEquals false, config.dataSource.pooling
+        assertEquals "org.hsqldb.jdbcDriver", config.dataSource.driverClassName
+        assertEquals "sa", config.dataSource.username
+        assertEquals "secret", config.dataSource.password
+        assertEquals "update", config.dataSource.dbCreate
+        assertEquals "jdbc:hsqldb:file:prodDb;shutdown=true", config.dataSource.url
+    }
+
+    void testParseProperties() {
+        Properties props = new Properties()
+        props['foo'] = 'bar'
+        props['log4j.appender.NULL']='org.apache.log4j.varia.NullAppender'
+        props['log4j.rootLogger']='error, NULL'
+        props['log4j.logger.org.codehaus.groovy.grails.plugins']='info,NULL'
+        props['log4j.additivity.org.codehaus.groovy.grails.plugins']='false'
+        props['log4j.additivity.org.springframework']='false'
+        props['log4j.logger.grails.spring']='info,NULL'
+        props['log4j.appender.NULL.layout']='org.apache.log4j.PatternLayout'
+
+        def config = new ConfigSlurper().parse(props)
+
+        assertEquals "org.apache.log4j.PatternLayout", config.log4j.appender.'NULL.layout' // tests overlapping properties
+        assertEquals "org.apache.log4j.varia.NullAppender", config.log4j.appender.NULL // tests overlapping properties
+        assertEquals 'error, NULL', config.log4j.rootLogger
+        assertEquals 'info,NULL', config.log4j.logger.org.codehaus.groovy.grails.plugins
+        assertEquals 'false', config.log4j.additivity.org.springframework
+        assertEquals 'bar', config.foo
+    }
+    
+    void testSimpleProperties() {
+        def slurper = new ConfigSlurper()
+
+        def config = slurper.parse('''
+smtp.server.url = "localhost"
+smtp.username = "fred"
+''')
+
+        assert config
+        println config
+        println config.smtp.server
+        assertEquals "localhost", config.smtp.server.url
+        assertEquals "fred", config.smtp.username
+    }
+
+    void testScopedProperties() {
+        def slurper = new ConfigSlurper()
+        def config = slurper.parse('''
+    smtp {
+        mail.host = 'smtp.myisp.com'
+        mail.auth.user = 'server'
+    }
+    resources.URL = "http://localhost:80/resources"
+        ''')
+
+        assert config
+        assertEquals "smtp.myisp.com", config.smtp.mail.host
+        assertEquals "server", config.smtp.mail.auth.user
+        assertEquals "http://localhost:80/resources", config.resources.URL                
+
+    }
+
+    void testScopedPropertiesWithNesting() {
+        def slurper = new ConfigSlurper()
+        def config = slurper.parse('''
+    smtp {
+        mail {
+            host = 'smtp.myisp.com'
+            auth.user = 'server'
+        }
+    }
+    resources.URL = "http://localhost:80/resources"
+        ''')
+
+        assert config
+        assert "smtp.myisp.com" == config.smtp.mail.host
+        assertEquals "server", config.smtp.mail.auth.user
+        assertEquals "http://localhost:80/resources", config.resources.URL
+
+    }
+    
+    void testScopedVariableReusage() {
+        def conf = '''
+          a0 = "Goofy"
+          a1 = "$a0"
+          a2."$a0" = "Mickey Mouse and " + "$a0"
+          group1 { a0 = "Donald Duck" }
+          group2 { 
+            a0 = a0
+            a1 = "$group1.a0" 
+            group3 {
+              a0 = "inner$a0" 
+              group4 {
+                a0 = "inner$a0"
+                a1 = a1
+                a3 = "Dagobert Duck"
+              }
+            }
+            a3 = a3 
+          }
+          a3 = "$group1.a0"
+        '''
+
+        def config = new ConfigSlurper().parse(conf)
+        assert config.group1.a0 == "Donald Duck"
+        assert config.group2.a0 == "Goofy"
+        assert config.group2.group3.a0 == "innerGoofy"
+        assert config.group2.group3.group4.a0 == "innerinnerGoofy"
+        assert config.group2.group3.group4.a1 == "Donald Duck"
+        assert config.group2.group3.group4.a3 == "Dagobert Duck"
+        assert config.group2.a3 == [:]
+        assert config.a3 == "Donald Duck"
+    }
+
+    void testLog4jConfiguration() {
+        def slurper = new ConfigSlurper()
+        def config = slurper.parse('''
+log4j {
+    appender {
+        stdout("org.apache.log4j.ConsoleAppender") {
+            layout="org.apache.log4j.PatternLayout"
+        }                
+    }
+    rootLogger="error,stdout"
+    logger {
+        org.codehaus.groovy.grails="info,stdout"
+        org.springframework="info,stdout"
+    }
+    additivity {
+        org.codehaus.groovy.grails=false
+        org.springframework=false
+    }
+}
+        ''')
+
+        assert config
+
+        println config
+
+        assertEquals "org.apache.log4j.ConsoleAppender", config.log4j.appender.stdout
+        assertEquals "org.apache.log4j.PatternLayout", config.log4j.appender."stdout.layout"
+        assertEquals "error,stdout", config.log4j.rootLogger
+        assertEquals "info,stdout", config.log4j.logger.org.codehaus.groovy.grails
+        assertEquals false, config.log4j.additivity.org.codehaus.groovy.grails              
+    }
+
+    void testEnvironmentSpecificConfig() {
+        def slurper = new ConfigSlurper()
+        def config = slurper.parse('''
+log4j {
+    appender {
+        stdout("org.apache.log4j.ConsoleAppender") {
+            layout="org.apache.log4j.PatternLayout"
+        }        
+    }
+    rootLogger="error,stdout"
+    logger {
+        org.codehaus.groovy.grails="info,stdout"
+        org.springframework="info,stdout"
+    }
+    additivity {
+        org.codehaus.groovy.grails=false
+        org.springframework=false
+    }
+}
+environments {
+    development {
+        log4j.logger.org.codehaus.groovy.grails="debug,stdout"
+    }
+}
+        ''')
+
+        assert config
+
+        assertEquals "org.apache.log4j.ConsoleAppender", config.log4j.appender.stdout
+        assertEquals "org.apache.log4j.PatternLayout", config.log4j.appender."stdout.layout"
+        assertEquals "error,stdout", config.log4j.rootLogger
+        assertEquals "info,stdout", config.log4j.logger.org.codehaus.groovy.grails
+        assertEquals false, config.log4j.additivity.org.codehaus.groovy.grails
+
+        slurper.setEnvironment("development")
+        config = slurper.parse('''
+log4j {
+    appender {
+        stdout("org.apache.log4j.ConsoleAppender") {        
+            layout="org.apache.log4j.PatternLayout"
+        }
+    }
+    rootLogger="error,stdout"
+    logger {
+        org.codehaus.groovy.grails="info,stdout"
+        org.springframework="info,stdout"
+    }
+    additivity {
+        org.codehaus.groovy.grails=false
+        org.springframework=false
+    }
+}
+environments {
+    development {
+        log4j.logger.org.codehaus.groovy.grails="debug,stdout"
+        log4j.appender.layout="MyLayout"
+    }
+    production {
+        log4j.appender.stdout="MyRobustFileAppender"
+    }
+}
+        ''')
+
+        assert config
+
+        assertEquals "org.apache.log4j.ConsoleAppender", config.log4j.appender.stdout
+        assertEquals "MyLayout", config.log4j.appender.layout
+        assertEquals "error,stdout", config.log4j.rootLogger
+        assertEquals "debug,stdout", config.log4j.logger.org.codehaus.groovy.grails
+        assertEquals false, config.log4j.additivity.org.codehaus.groovy.grails
+
+    }
+
+
+    void testFlattenConfig() {
+        def slurper = new ConfigSlurper()
+        def config = slurper.parse('''
+log4j {
+    appender {
+        stdout("org.apache.log4j.ConsoleAppender") {        
+            layout="org.apache.log4j.PatternLayout"
+        }
+    }
+    rootLogger="error,stdout"
+    logger {
+        org.codehaus.groovy.grails="info,stdout"
+        org.springframework="info,stdout"
+    }
+    additivity {
+        org.codehaus.groovy.grails=false
+        org.springframework=false
+    }
+}
+        ''')
+
+        config = config.flatten()
+
+
+        assertEquals "org.apache.log4j.ConsoleAppender", config."log4j.appender.stdout"
+        assertEquals "org.apache.log4j.PatternLayout", config."log4j.appender.stdout.layout"
+        assertEquals "error,stdout", config."log4j.rootLogger"
+        assertEquals "info,stdout", config."log4j.logger.org.codehaus.groovy.grails"
+        assertEquals false, config."log4j.additivity.org.codehaus.groovy.grails"              
+
+
+    }
+
+
+    void testToProperties() {
+        def slurper = new ConfigSlurper()
+        def config = slurper.parse('''
+log4j {
+    appender {
+        stdout("org.apache.log4j.ConsoleAppender") {
+           layout="org.apache.log4j.PatternLayout"
+        }
+    }
+    rootLogger="error,stdout"
+    logger {
+        org.codehaus.groovy.grails="info,stdout"
+        org.springframework="info,stdout"
+    }
+    additivity {
+        org.codehaus.groovy.grails=false
+        org.springframework=false
+    }
+}
+        ''')
+
+        def props = config.toProperties()
+        assert props
+
+        assertEquals "org.apache.log4j.ConsoleAppender", props."log4j.appender.stdout"
+        assertEquals "org.apache.log4j.PatternLayout", props."log4j.appender.stdout.layout"
+        assertEquals "error,stdout", props."log4j.rootLogger"
+        assertEquals "info,stdout", props."log4j.logger.org.codehaus.groovy.grails"
+        assertEquals "false", props."log4j.additivity.org.codehaus.groovy.grails"
+
+
+        props = config.log4j.toProperties("log4j")
+        assertEquals "org.apache.log4j.ConsoleAppender", props."log4j.appender.stdout"
+        assertEquals "org.apache.log4j.PatternLayout", props."log4j.appender.stdout.layout"
+        assertEquals "error,stdout", props."log4j.rootLogger"
+        assertEquals "info,stdout", props."log4j.logger.org.codehaus.groovy.grails"
+        assertEquals "false", props."log4j.additivity.org.codehaus.groovy.grails"
+
+    }     
+      
+	void testConfigTokensAsStrings() {
+        def slurper = new ConfigSlurper()
+        def config = slurper.parse('''
+log4j {
+    appender.stdout = "org.apache.log4j.ConsoleAppender"
+	appender."stdout.layout"="org.apache.log4j.PatternLayout"
+	rootLogger="error,stdout"	
+}
+        ''')
+
+		assert config   
+        assertEquals "org.apache.log4j.ConsoleAppender", config.log4j.appender.stdout
+        assertEquals "org.apache.log4j.PatternLayout", config.log4j.appender."stdout.layout"
+        assertEquals "error,stdout", config.log4j.rootLogger		
+	}
+	
+	void testConfigInterReferencing() {
+        def slurper = new ConfigSlurper()
+        def config = slurper.parse('''
+			var.one=5
+			var.two=var.one*2
+        ''')
+		                     
+		assertEquals 5, config.var.one
+		assertEquals 10, config.var.two
+	}
+        
+
+	void testSerializeConfig() {   
+		def text = '''
+log4j {
+    appender.stdout="org.apache.log4j.ConsoleAppender"
+    appender.'stdout.layout'="org.apache.log4j.PatternLayout"        
+    rootLogger="error,stdout"
+    logger {
+        org.codehaus.groovy.grails="info,stdout"
+        org.springframework="info,stdout"
+    }
+     
+    additivity.'default' = true
+    additivity.org.codehaus.groovy.grails=false
+    additivity.org.springframework=false
+}'''
+        def slurper = new ConfigSlurper()
+        def config = slurper.parse(text)
+
+		assert config                  
+		                                   
+		def sw = new StringWriter()
+
+
+		config.writeTo(sw)
+
+		def newText = sw.toString()
+
+		println newText
+
+		config = slurper.parse(newText)
+
+
+        assertEquals "org.apache.log4j.ConsoleAppender", config.log4j.appender.stdout
+        assertEquals "org.apache.log4j.PatternLayout", config.log4j.appender."stdout.layout"
+        assertEquals "error,stdout", config.log4j.rootLogger
+        assertEquals "info,stdout", config.log4j.logger.org.codehaus.groovy.grails
+        assertEquals false, config.log4j.additivity.org.codehaus.groovy.grails
+
+		
+	}
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/util/Dummy.java b/groovy/src/test/groovy/util/Dummy.java
new file mode 100644
index 0000000..64495ba
--- /dev/null
+++ b/groovy/src/test/groovy/util/Dummy.java
@@ -0,0 +1,94 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.util;
+
+public class Dummy implements DummyMBean {
+
+    private String name = "James";
+    private String location = "London";
+    private int size = 12;
+
+    public Dummy() {
+    }
+
+    public Dummy(String name, String location) {
+        this.name = name;
+        this.location = location;
+    }
+
+    public void start() {
+        System.out.println("Started!");
+    }
+
+    public void stop() {
+        System.out.println("Stopped!");
+    }
+
+    public String getLocation() {
+        return location;
+    }
+
+    public void setLocation(String location) {
+        this.location = location;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public int getSize() {
+        return size;
+    }
+
+    public void setSize(int size) {
+        this.size = size;
+    }
+
+}
diff --git a/groovy/src/test/groovy/util/DummyMBean.java b/groovy/src/test/groovy/util/DummyMBean.java
new file mode 100644
index 0000000..462229a
--- /dev/null
+++ b/groovy/src/test/groovy/util/DummyMBean.java
@@ -0,0 +1,64 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package groovy.util;
+
+public interface DummyMBean {
+    void start();
+
+    void stop();
+
+    String getLocation();
+
+    void setLocation(String location);
+
+    String getName();
+
+    void setName(String name);
+
+    int getSize();
+
+    void setSize(int size);
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/util/EmptyScriptTest.groovy b/groovy/src/test/groovy/util/EmptyScriptTest.groovy
new file mode 100644
index 0000000..5a92ca0
--- /dev/null
+++ b/groovy/src/test/groovy/util/EmptyScriptTest.groovy
@@ -0,0 +1,54 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.util;
+
+public class EmptyScriptTest extends GroovyTestCase {
+
+    public void testEmptyScript() throws Exception {
+        assertScript("");
+    }
+}
diff --git a/groovy/src/test/groovy/util/EvalTest.java b/groovy/src/test/groovy/util/EvalTest.java
new file mode 100644
index 0000000..397fe3a
--- /dev/null
+++ b/groovy/src/test/groovy/util/EvalTest.java
@@ -0,0 +1,38 @@
+package groovy.util;
+
+import junit.framework.TestCase;
+import org.codehaus.groovy.control.CompilationFailedException;
+
+/**
+ * Testing the simple Groovy integration with Eval.
+ *
+ * @author Dierk Koenig
+ */
+public class EvalTest extends TestCase {
+    public void testMeSimple() throws CompilationFailedException {
+        Object result = Eval.me("10");
+        assertEquals("10", result.toString());
+    }
+
+    public void testMeWithSymbolAndObject() throws CompilationFailedException {
+        Object result = Eval.me("x", new Integer(10), "x");
+        assertEquals("10", result.toString());
+    }
+
+    public void testX() throws CompilationFailedException {
+        Object result = Eval.x(new Integer(10), "x");
+        assertEquals("10", result.toString());
+    }
+
+    public void testXY() throws CompilationFailedException {
+        Integer ten = new Integer(10);
+        Object result = Eval.xy(ten, ten, "x+y");
+        assertEquals("20", result.toString());
+    }
+
+    public void testXYZ() throws CompilationFailedException {
+        Integer ten = new Integer(10);
+        Object result = Eval.xyz(ten, ten, ten, "x+y+z");
+        assertEquals("30", result.toString());
+    }
+}
diff --git a/groovy/src/test/groovy/util/FactoryBuilderSupportTest.groovy b/groovy/src/test/groovy/util/FactoryBuilderSupportTest.groovy
new file mode 100644
index 0000000..a00e496
--- /dev/null
+++ b/groovy/src/test/groovy/util/FactoryBuilderSupportTest.groovy
@@ -0,0 +1,480 @@
+package groovy.util

+

+import groovy.lang.MissingMethodException

+

+/**

+ *   Test for FactoryBuilderSupport based in BuilderSupportTest

+ *   as it should comply with the same contract

+ *   @author Andres Almiray 

+ */

+

+class FactoryBuilderSupportTest extends GroovyTestCase{

+    void testSimpleNode() {

+        def b = new SpoofFactoryBuilder()

+        assert b.log == []

+        def node = b.foo()

+        assert b.log == [  'new_instance','foo', null,

+                           'handle_node_attributes', node,

+                           'node_completed',null, node, 

+                           'post_node_completion',null, node

+                        ]

+    }

+

+    void testSimpleNodeWithValue() {

+        def b = new SpoofFactoryBuilder()

+        def node = b.foo('value')

+        assert b.log == [  'new_instance','foo', 'value',

+                           'handle_node_attributes', node,

+                           'node_completed',null,node,

+                           'post_node_completion',null, node

+                        ]

+    }

+

+    void testSimpleNodeWithOneAttribute() {

+        def b = new SpoofFactoryBuilder()

+        def node = b.foo(name:'value')

+        assert b.log == [

+                           'new_instance','foo',null,'name','value',

+                           'handle_node_attributes',node,'name','value', 

+                           'node_completed',null, node,

+                           'post_node_completion',null, node 

+                        ]

+    }

+

+    void testSimpleNodeWithClosure() {

+        def b = new SpoofFactoryBuilder()

+        b.foo(){

+            b.bar()

+        }

+        assert b.log == [

+            'new_instance','foo',null,

+            'handle_node_attributes','x',

+                'new_instance','bar',null,

+                'handle_node_attributes','x',

+            'set_parent', 'x', 'x',

+            'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x',

+            'node_completed',null,'x',

+            'post_node_completion',null, 'x'

+            ]

+    }

+

+    void testSimpleNodeWithOneAttributeAndValue() {

+        def b = new SpoofFactoryBuilder()

+        def node = b.foo(bar:'baz', 'value')

+        assert b.log == [

+                          'new_instance', 'foo', 'value', 'bar','baz', 

+                          'handle_node_attributes',node,'bar','baz',

+                          'node_completed',null,node,

+                          'post_node_completion',null, node

+                        ]

+    }

+

+    void testSimpleNodeWithValueAndOneAttribute() {

+        def b = new SpoofFactoryBuilder()

+        def node = b.foo('value', bar:'baz')

+        assert b.log == [

+                          'new_instance', 'foo', 'value', 'bar','baz', 

+                          'handle_node_attributes',node,'bar','baz',

+                          'node_completed',null,node,

+                          'post_node_completion',null, node

+                        ]

+    }

+

+    void testSimpleNodeWithOneAttributeAndValueAndClosure() {

+        def b = new SpoofFactoryBuilder()

+        def node = b.foo(bar:'baz', 'value') { 1 }

+        assert b.log == [

+                          'new_instance', 'foo', 'value', 'bar','baz', 

+                          'handle_node_attributes',node,'bar','baz',

+                          'node_completed',null,node,

+                          'post_node_completion',null, node

+                        ]

+    }

+

+    void testSimpleNodeWithValueAndOneAttributeAndClosure() {

+        def b = new SpoofFactoryBuilder()

+        def node = b.foo('value', bar:'baz') { 1 }

+        assert b.log == [

+                          'new_instance', 'foo', 'value', 'bar','baz', 

+                          'handle_node_attributes',node,'bar','baz',

+                          'node_completed',null,node,

+                          'post_node_completion',null, node

+                        ]

+    }

+

+    void testSimpleNodeTwoValues() {

+        def b = new SpoofFactoryBuilder()

+        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2')})

+    }

+

+    void testSimpleNodeTwoValuesClosure() {

+        def b = new SpoofFactoryBuilder()

+        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2') { 1 } })

+    }

+

+    void testSimpleNodeThreeValues() {

+        def b = new SpoofFactoryBuilder()

+        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2', 'arg3') })

+    }

+

+    void testSimpleNodeFourValues() {

+        def b = new SpoofFactoryBuilder()

+        shouldFail(MissingMethodException, {def node = b.foo('arg1', 'arg2', 'arg3', 'arg4') })

+    }

+

+    void testNestedMethodCallsResolution() {

+        def b = new SpoofFactoryBuilder()

+        b.outest {

+            b.outer {

+                nestedBuilderCall(b)

+            }

+        }

+        assert b.log.contains('inner') 

+    }

+

+    void nestedBuilderCall(builder) {

+        builder.inner()

+    }

+

+    // ==================================

+

+    void testNestedBuilderSimpleNode() {

+        def n = new SpoofFactoryBuilder()

+        def b = new SpoofFactoryBuilder(proxyBuilder:n)

+        assert b.log == []

+        assert n.log == []

+        def node = b.foo()

+        assert b.log == []

+        assert n.log == [  'new_instance','foo', null,

+                           'handle_node_attributes', node,

+                           'node_completed',null, node, 

+                           'post_node_completion',null, node

+                        ]

+    }

+

+    void testNestedBuilderSimpleNodeWithValue() {

+        def n = new SpoofFactoryBuilder()

+        def b = new SpoofFactoryBuilder(proxyBuilder:n)

+        def node = b.foo('value')

+        assert b.log == []

+        assert n.log == [  'new_instance','foo', 'value',

+                           'handle_node_attributes', node,

+                           'node_completed',null,node,

+                           'post_node_completion',null, node

+                        ]

+    }

+

+    void testNestedBuilderSimpleNodeWithOneAttribute() {

+        def n = new SpoofFactoryBuilder()

+        def b = new SpoofFactoryBuilder(proxyBuilder:n)

+        def node = b.foo(name:'value')

+        assert b.log == []

+        assert n.log == [

+                           'new_instance','foo',null,'name','value',

+                           'handle_node_attributes',node,'name','value', 

+                           'node_completed',null, node,

+                           'post_node_completion',null, node 

+                        ]

+    }

+

+    void testNestedBuilderSimpleNodeWithClosure() {

+        def n = new SpoofFactoryBuilder()

+        def b = new SpoofFactoryBuilder(proxyBuilder:n)

+        b.foo(){

+            b.bar()

+        }

+        assert b.log == []

+        assert n.log == [

+            'new_instance','foo',null,

+            'handle_node_attributes','x',

+                'new_instance','bar',null,

+                'handle_node_attributes','x',

+            'set_parent', 'x', 'x',

+            'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x',

+            'node_completed',null,'x',

+            'post_node_completion',null, 'x'

+            ]

+    }

+

+    void testNestedBuilderSimpleNodeWithOneAttributeAndValue() {

+        def n = new SpoofFactoryBuilder()

+        def b = new SpoofFactoryBuilder(proxyBuilder:n)

+        def node = b.foo(bar:'baz', 'value')

+        assert b.log == []

+        assert n.log == [

+                          'new_instance', 'foo', 'value', 'bar','baz', 

+                          'handle_node_attributes',node,'bar','baz',

+                          'node_completed',null,node,

+                          'post_node_completion',null, node

+                        ]

+    }

+

+    void testNestedBuilderSimpleNodeWithValueAndOneAttribute() {

+        def n = new SpoofFactoryBuilder()

+        def b = new SpoofFactoryBuilder(proxyBuilder:n)

+        def node = b.foo('value', bar:'baz')

+        assert b.log == []

+        assert n.log == [

+                          'new_instance', 'foo', 'value', 'bar','baz', 

+                          'handle_node_attributes',node,'bar','baz',

+                          'node_completed',null,node,

+                          'post_node_completion',null, node

+                        ]

+    }

+

+    void testNestedBuilderSimpleNodeWithOneAttributeAndValueAndClosure() {

+        def n = new SpoofFactoryBuilder()

+        def b = new SpoofFactoryBuilder(proxyBuilder:n)

+        def node = b.foo(bar:'baz', 'value') { 1 }

+        assert b.log == []

+        assert n.log == [

+                          'new_instance', 'foo', 'value', 'bar','baz', 

+                          'handle_node_attributes',node,'bar','baz',

+                          'node_completed',null,node,

+                          'post_node_completion',null, node

+                        ]

+    }

+

+    void testNestedBuilderSimpleNodeWithValueAndOneAttributeAndClosure() {

+        def n = new SpoofFactoryBuilder()

+        def b = new SpoofFactoryBuilder(proxyBuilder:n)

+        def node = b.foo('value', bar:'baz') { 1 }

+        assert b.log == []

+        assert n.log == [

+                          'new_instance', 'foo', 'value', 'bar','baz', 

+                          'handle_node_attributes',node,'bar','baz',

+                          'node_completed',null,node,

+                          'post_node_completion',null, node

+                        ]

+    }

+

+    void testWithBuilder() {

+        def b = new SpoofFactoryBuilder()

+        def c = new SpoofFactoryBuilder()

+        def factory = new XFactory( builder:c )

+        c.registerFactory( "fooz", factory )

+        c.registerFactory( "baz", factory )

+        b.foo(){

+            bar()

+            withBuilder(c){

+               fooz(){

+                  baz()

+               }   

+            }

+        }

+

+        assert b.log == [

+            'new_instance','foo',null,

+            'handle_node_attributes','x',

+                'new_instance','bar',null,

+                'handle_node_attributes','x',

+            'set_parent', 'x', 'x',

+            'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x',

+            'node_completed',null,'x',

+            'post_node_completion',null, 'x'

+            ]

+        assert c.log == [

+            'new_instance','fooz',null,

+            'handle_node_attributes','x',

+                'new_instance','baz',null,

+                'handle_node_attributes','x',

+            'set_parent', 'x', 'x',

+            'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x',

+            'node_completed',null,'x',

+            'post_node_completion',null, 'x'

+            ]

+    }

+

+    void testWithBuilderAndName() {

+        def b = new SpoofFactoryBuilder()

+        def c = new SpoofFactoryBuilder()

+        def factory = new XFactory( builder:c )

+        c.registerFactory( "fooz", factory )

+        c.registerFactory( "baz", factory )

+        b.foo(){

+            bar()

+            withBuilder(c,'foo'){

+               fooz(){

+                  baz()

+               }   

+            }

+        }

+

+        assert b.log == [

+            'new_instance','foo',null,

+            'handle_node_attributes','x',

+                'new_instance','bar',null,

+                'handle_node_attributes','x',

+                'set_parent', 'x', 'x',

+                'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x',

+                'new_instance','foo','x',

+                'handle_node_attributes','x',

+                'set_parent', 'x', 'x',

+                'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x',

+            'node_completed',null,'x',

+            'post_node_completion',null, 'x'

+            ]

+        assert c.log == [

+            'new_instance','fooz',null,

+            'handle_node_attributes','x',

+                'new_instance','baz',null,

+                'handle_node_attributes','x',

+                'set_parent', 'x', 'x',

+                'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x',

+            'node_completed',null,'x',

+            'post_node_completion',null, 'x'

+            ]

+    }

+

+    void testWithBuilderAndNameAndAttributes() {

+        def b = new SpoofFactoryBuilder()

+        def c = new SpoofFactoryBuilder()

+        def factory = new XFactory( builder:c )

+        c.registerFactory( "fooz", factory )

+        c.registerFactory( "baz", factory )

+        b.foo(){

+            bar()

+            withBuilder(c,'foo',bar:'baz'){

+               fooz(){

+                  baz()

+               }   

+            }

+        }

+

+        assert b.log == [

+            'new_instance','foo',null,

+            'handle_node_attributes','x',

+                'new_instance','bar',null,

+                'handle_node_attributes','x',

+                'set_parent', 'x', 'x',

+                'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x',

+                'new_instance','foo','x','bar','baz',

+                'handle_node_attributes','x','bar','baz',

+                'set_parent', 'x', 'x',

+                'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x',

+            'node_completed',null,'x',

+            'post_node_completion',null, 'x'

+            ]

+        assert c.log == [

+            'new_instance','fooz',null,

+            'handle_node_attributes','x',

+                'new_instance','baz',null,

+                'handle_node_attributes','x',

+                'set_parent', 'x', 'x',

+                'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x',

+            'node_completed',null,'x',

+            'post_node_completion',null, 'x'

+            ]

+    }

+

+    void testWithBuilderAndThrowAnException() {

+        def b = new SpoofFactoryBuilder()

+        def c = new SpoofFactoryBuilder()

+

+        shouldFail( RuntimeException ) {

+            b.foo(){

+                bar()

+                withBuilder(c){

+                   throw new RuntimeException("expected")

+                }

+            }

+        }

+

+        assert b.log == [

+            'new_instance','foo',null,

+            'handle_node_attributes','x',

+                'new_instance','bar',null,

+                'handle_node_attributes','x',

+                'set_parent', 'x', 'x',

+                'set_child', 'x', 'x',

+                'node_completed','x','x',

+                'post_node_completion', 'x', 'x'/*,

+            'node_completed',null,'x',

+            'post_node_completion',null, 'x'*/

+            // node foo() was not completed successfuly

+            ]

+        assert c.log == []

+    }

+}

+

+/**

+    The SpoofFactoryBuilder is a sample instance of the abstract FactoryBuilderSupport class

+    that does nothing but logging how it was called, returning 'x' for each node.

+**/

+class SpoofFactoryBuilder extends FactoryBuilderSupport{

+    def log = []

+

+    SpoofFactoryBuilder() {

+       def factory = new XFactory( builder:this )

+       registerFactory( "foo", factory )

+       registerFactory( "bar", factory )

+       registerFactory( "outest", factory )

+       registerFactory( "outer", factory )

+       registerFactory( "inner", factory )

+    }

+    

+    protected Object postNodeCompletion(Object parent, Object node) {

+        log << 'post_node_completion'

+        log << parent

+        log << node

+        node

+    }

+}

+

+class XFactory extends AbstractFactory {

+    SpoofFactoryBuilder builder

+

+    public Object newInstance( FactoryBuilderSupport builder, Object name, Object value, Map properties )

+            throws InstantiationException, IllegalAccessException {

+        builder.log << 'new_instance'

+        builder.log << name

+        builder.log << value

+        properties.each{entry -> builder.log << entry.key; builder.log << entry.value}

+        return 'x'

+    }

+

+    public boolean onHandleNodeAttributes( FactoryBuilderSupport builder, Object node, Map attributes ) {

+        builder.log << 'handle_node_attributes'

+        builder.log << node

+        attributes.each{entry -> builder.log << entry.key; builder.log << entry.value}

+        return false 

+    }

+

+    public void onNodeCompleted( FactoryBuilderSupport builder, Object parent, Object node ) {

+        builder.log << 'node_completed'

+        builder.log << parent

+        builder.log << node

+    }

+

+    public void setParent( FactoryBuilderSupport builder, Object parent, Object child ) {

+        builder.log << "set_parent"

+        builder.log << parent

+        builder.log << child

+    }

+

+    public void setChild( FactoryBuilderSupport builder, Object parent, Object child ) {

+        builder.log << "set_child"

+        builder.log << parent

+        builder.log << child

+    }

+}

diff --git a/groovy/src/test/groovy/util/FileNameFinderTest.groovy b/groovy/src/test/groovy/util/FileNameFinderTest.groovy
new file mode 100644
index 0000000..df7a404
--- /dev/null
+++ b/groovy/src/test/groovy/util/FileNameFinderTest.groovy
@@ -0,0 +1,19 @@
+package groovy.util
+
+/**
+    Make sure FileNameFinder uses Ant filesets correctly.
+    @author Dierk Koenig
+    @author Paul King
+*/
+class fileNameFinderTest extends GroovyLogTestCase {
+
+    void testFilesInTestDirArePickedUp() {
+        def finder = new FileNameFinder()
+        def files1 = finder.getFileNames('src/test','*')
+        assert files1, 'There should be files in src/test'
+        // now collect all those not starting with the letter 'J'
+        def files2 = finder.getFileNames('src/test','*','J*')
+        assert files2, 'There should be files in src/test'
+        assert files1.size() > files2.size()
+    }
+}
diff --git a/groovy/src/test/groovy/util/GroovyCollectionsStarImportTest.groovy b/groovy/src/test/groovy/util/GroovyCollectionsStarImportTest.groovy
new file mode 100644
index 0000000..b0197cb
--- /dev/null
+++ b/groovy/src/test/groovy/util/GroovyCollectionsStarImportTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util
+
+import static groovy.util.GroovyCollections.*
+
+/**
+* Tests GroovyCollections
+*
+* @author Paul King
+*/
+public class GroovyCollectionsStarImportTest extends GroovyTestCase {
+
+    void testCombinations() {
+        // use Sets because we don't care about order
+        Set expected = [['a', 1], ['a', 2], ['b', 1], ['b', 2]]
+        assert combinations(['a', 'b'], [1, 2]) as Set == expected
+    }
+
+    void testTranspose() {
+        assert transpose(['a', 'b'], [1, 2, 3]) == [['a', 1], ['b', 2]]
+    }
+
+    void testMin() {
+        assert min('a', 'b') == 'a'
+    }
+
+    void testMax() {
+        assert max(1, 2, 3) == 3
+    }
+
+    void testSum() {
+        assert sum(1, 2, 3, 4) == 10
+    }
+
+}
diff --git a/groovy/src/test/groovy/util/GroovyCollectionsTest.groovy b/groovy/src/test/groovy/util/GroovyCollectionsTest.groovy
new file mode 100644
index 0000000..554a8d4
--- /dev/null
+++ b/groovy/src/test/groovy/util/GroovyCollectionsTest.groovy
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util
+
+import static GroovyCollections.min
+import static GroovyCollections.max
+import static GroovyCollections.combinations
+import static GroovyCollections.transpose
+import static GroovyCollections.sum
+
+/**
+* Tests GroovyCollections
+*
+* @author Paul King
+*/
+public class GroovyCollectionsTest extends GroovyTestCase {
+
+    void testCombinations() {
+        // use Sets because we don't care about order
+        Set expected = [
+                        ['a', 1], ['a', 2], ['a', 3],
+                        ['b', 1], ['b', 2], ['b', 3]
+                ]
+        Collection input = [['a', 'b'], [1, 2, 3]]
+
+        // normal varargs versions should match Object[]
+        assert GroovyCollections.combinations(['a', 'b'], [1, 2, 3]) as Set == expected
+        assert combinations(['a', 'b'], [1, 2, 3]) as Set == expected
+
+        // spread versions should match Object[]
+        assert GroovyCollections.combinations(*input) as Set == expected
+        assert combinations(*input) as Set == expected
+
+        // collection versions should match Collection
+        assert GroovyCollections.combinations(input) as Set == expected
+        assert combinations(input) as Set == expected
+    }
+
+    void testTranspose() {
+        // normal varargs versions should match Object[]
+        assert GroovyCollections.transpose(['a', 'b'], [1, 2, 3]) == [['a', 1], ['b', 2]]
+        assert transpose(['a', 'b'], [1, 2, 3]) == [['a', 1], ['b', 2]]
+        assert GroovyCollections.transpose([1, 2, 3], [4, 5, 6]) == [[1, 4], [2, 5], [3, 6]]
+        assert transpose([1, 2, 3], [4, 5, 6]) == [[1, 4], [2, 5], [3, 6]]
+        assert GroovyCollections.transpose([1, 2, 3], [4, 5], [9], [6, 7, 8]) == [[1, 4, 9, 6]]
+        assert transpose([1, 2, 3], [4, 5], [9], [6, 7, 8]) == [[1, 4, 9, 6]]
+
+        // collection versions
+        assert GroovyCollections.transpose([[1, 2, 3]]) == [[1], [2], [3]]
+        assert transpose([[1, 2, 3]]) == [[1], [2], [3]]
+        assert GroovyCollections.transpose([]) == []
+        assert transpose([]) == []
+    }
+
+    void testMin() {
+        // normal varargs versions should match Object[]
+        assert GroovyCollections.min('a', 'b') == 'a'
+        assert min('a', 'b') == 'a'
+        assert GroovyCollections.min(1, 2, 3) == 1
+        assert min(1, 2, 3) == 1
+
+        // collection versions
+        assert GroovyCollections.min(['a', 'b']) == 'a'
+        assert min(['a', 'b']) == 'a'
+        assert GroovyCollections.min([1, 2, 3]) == 1
+        assert min([1, 2, 3]) == 1
+    }
+
+    void testMax() {
+        // normal varargs versions should match Object[]
+        assert GroovyCollections.max('a', 'b') == 'b'
+        assert max('a', 'b') == 'b'
+        assert GroovyCollections.max(1, 2, 3) == 3
+        assert max(1, 2, 3) == 3
+
+        // collection versions
+        assert GroovyCollections.max(['a', 'b']) == 'b'
+        assert max(['a', 'b']) == 'b'
+        assert GroovyCollections.max([1, 2, 3]) == 3
+        assert max([1, 2, 3]) == 3
+    }
+
+    void testSum() {
+        // normal varargs versions should match Object[]
+        assert GroovyCollections.sum('a', 'b') == 'ab'
+        assert sum('a', 'b') == 'ab'
+        assert GroovyCollections.sum(1, 2, 3) == 6
+        assert sum(1, 2, 3) == 6
+
+        // collection versions
+        assert GroovyCollections.sum(['a', 'b']) == 'ab'
+        assert sum(['a', 'b']) == 'ab'
+        assert GroovyCollections.sum([1, 2, 3]) == 6
+        assert sum([1, 2, 3]) == 6
+    }
+
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/util/GroovyTestCaseTest.groovy b/groovy/src/test/groovy/util/GroovyTestCaseTest.groovy
new file mode 100644
index 0000000..74deb5c
--- /dev/null
+++ b/groovy/src/test/groovy/util/GroovyTestCaseTest.groovy
@@ -0,0 +1,70 @@
+package groovy.util
+
+/**
+    Testing the notYetImplemented feature of GroovyTestCase.
+    Todo: testing all other features.
+    @author Dierk Koenig
+*/
+
+class GroovyTestCaseTest extends GroovyTestCase {
+
+    void testNotYetImplementedSubclassUse () {
+        if (notYetImplemented()) return
+        fail 'here the code that is expected to fail'
+    }
+    void testNotYetImplementedStaticUse () {
+        if (GroovyTestCase.notYetImplemented(this)) return
+        fail 'here the code that is expected to fail'
+    }
+
+    // we cannot test this automatically...
+    // remove the leading x, run the test and see it failing
+    void xtestSubclassFailing() {
+        if (notYetImplemented()) return
+        assert true // passes unexpectedly
+    }
+    void xtestStaticFailing() {
+        if (GroovyTestCase.notYetImplemented(this)) return
+        assert true // passes unexpectedly
+    }
+
+// ----------------
+
+    void testShouldFailWithMessage() {
+        def msg = shouldFail { throw new RuntimeException('x') }
+        assertEquals 'java.lang.RuntimeException: x', msg
+    }
+    void testShouldFailWithMessageForClass() {
+        def msg = shouldFail(RuntimeException.class) { throw new RuntimeException('x') }
+        println msg
+        assertEquals 'x', msg
+    }
+
+    void testShouldFail() {
+        shouldFail(MyException) {
+            new Foo().createBar()
+        }
+    }
+
+    void testShouldFailWithNestedException() {
+        shouldFail(MyException) {
+            new Foo().createBarWithNestedException()
+        }
+    }
+}
+
+class Foo {
+    def createBar() {
+        throw new MyException(null)
+    }
+
+    def createBarWithNestedException() {
+        throw new MyException(new NullPointerException())
+    }
+}
+
+class MyException extends RuntimeException {
+    MyException(Throwable cause) {
+        super(cause);
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/util/MBeanTest.java b/groovy/src/test/groovy/util/MBeanTest.java
new file mode 100644
index 0000000..10a4a04
--- /dev/null
+++ b/groovy/src/test/groovy/util/MBeanTest.java
@@ -0,0 +1,83 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.util;
+
+import groovy.lang.GroovyObject;
+import org.codehaus.groovy.classgen.TestSupport;
+
+import javax.management.MBeanServer;
+import javax.management.MBeanServerFactory;
+import javax.management.ObjectName;
+
+/**
+ * Tests using the GroovyObject API from Java to access MBeans via
+ * the normal properties API (to simulate normal Groovy property access)
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MBeanTest extends TestSupport {
+
+    public void testGetProperty() throws Exception {
+        MBeanServer mbeanServer = MBeanServerFactory.createMBeanServer();
+        ObjectName name = new ObjectName("groovy.test:role=TestMBean,type=Dummy");
+        mbeanServer.registerMBean(new Dummy(), name);
+
+        assertEquals("JMX value of Name", "James", mbeanServer.getAttribute(name, "Name"));
+
+        GroovyObject object = new GroovyMBean(mbeanServer, name);
+
+        Object value = object.getProperty("Name");
+        assertEquals("Name property", "James", value);
+
+        object.setProperty("Name", "Bob");
+        assertEquals("Name property", "Bob", object.getProperty("Name"));
+
+        // now lets look up the name via JMX to checki
+        assertEquals("JMX value of Name", "Bob", mbeanServer.getAttribute(name, "Name"));
+    }
+}
diff --git a/groovy/src/test/groovy/util/NavToWiki.groovy b/groovy/src/test/groovy/util/NavToWiki.groovy
new file mode 100644
index 0000000..6477ced
--- /dev/null
+++ b/groovy/src/test/groovy/util/NavToWiki.groovy
@@ -0,0 +1,43 @@
+package groovy.util
+
+import groovy.util.XmlParser
+
+if (args.size() < 1) {
+    println "Usage: NavToWiki fileName"
+}
+else {
+    file = args[0]
+    println "About to parse ${file}"
+    doc = new XmlParser().parse(file)
+
+    println """
+QuickLinks page
+-------------------------------
+
+
+"""
+    links = doc.body.links.item
+    println links.collect {
+        return "{link:" + it['@name'] + "|" + it['@href'] + "}"
+    }.join(" | ")
+
+    println """
+
+
+
+Navigation page
+-------------------------------
+
+
+"""
+    menus = doc.body.menu
+    menus.each {
+        println "h3:${it['@name']}"
+
+        it.item.each {
+            println "- {link:" + it['@name'] + "|" + it['@href'] + "}"
+        }
+        println ""
+    }
+}
+
diff --git a/groovy/src/test/groovy/util/NodeTest.groovy b/groovy/src/test/groovy/util/NodeTest.groovy
new file mode 100644
index 0000000..ef5d618
--- /dev/null
+++ b/groovy/src/test/groovy/util/NodeTest.groovy
@@ -0,0 +1,188 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.util;
+
+
+import groovy.util.GroovyTestCase;
+import groovy.util.Node;
+import groovy.xml.QName;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * Tests the use of the structured Attribute type
+ * 
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class NodeTest extends GroovyTestCase {
+
+    public void testSimpleAttribute() {
+        Node attribute = new Node(null, "transactional");
+        assertEquals("name", "transactional", attribute.name());
+        assertEquals("attributes", 0, attribute.attributes().size());
+        assertEquals("value", 0, attribute.children().size());
+        assertEquals("text", "", attribute.text());
+
+        dump(attribute);
+    }
+
+    public void testAttributeWithAttributes() {
+        Map attributes = new HashMap();
+        attributes.put("a", "xyz");
+        
+        Node attribute = new Node(null, "foo", attributes);
+        assertEquals("name", "foo", attribute.name());
+        assertEquals("attributes", 1, attribute.attributes().size());
+        assertEquals("value", 0, attribute.children().size());
+        assertEquals("text", "", attribute.text());
+
+        dump(attribute);
+    }
+
+    public void testAttributeWithText() {
+        Node attribute = new Node(null, "foo", "the text");
+        assertEquals("name", "foo", attribute.name());
+        assertEquals("attributes", 0, attribute.attributes().size());
+        assertEquals("value", 1, attribute.children().size());
+        assertEquals("text", "the text", attribute.text());
+
+        dump(attribute);
+    }
+
+    public void testAttributeWithAttributesAndChildren() {
+        Map attributes = new HashMap();
+        attributes.put("a", "xyz");
+        
+        List children = new ArrayList();
+        children.add(new Node(null, "person", "James"));
+        children.add(new Node(null, "person", "Bob"));
+        children.add("someText");
+        
+        Node attribute = new Node(null, "foo", attributes, children);
+        assertEquals("name", "foo", attribute.name());
+        assertEquals("attributes", 1, attribute.attributes().size());
+        assertEquals("value", 3, attribute.children().size());
+        assertEquals("text", "someText", attribute.text());
+
+        dump(attribute);
+    }
+
+    public void testAttributeWithAttributesAndChildrenWithMixedText() {
+        Map attributes = new HashMap();
+        attributes.put("a", "xyz");
+        
+        List children = new ArrayList();
+        children.add("someText");
+        Node node1 = new Node(null, "person", "James");
+        children.add(node1);
+        children.add("moreText");
+        Node node2 = new Node(null, "person", "Bob");
+        children.add(node2);
+        children.add("moreText");
+        
+        Node attribute = new Node(null, "foo", attributes, children);
+        assertEquals("name", "foo", attribute.name());
+        assertEquals("attributes", 1, attribute.attributes().size());
+        assertEquals("value", 5, attribute.children().size());
+        assertEquals("text", "someTextmoreTextmoreText", attribute.text());
+        
+        
+        // lets test get
+        List list = (List) attribute.get("person");
+        assertEquals("Expected list size: " + list, 2, list.size());
+        
+        assertEquals("Node1", node1, list.get(0));
+        assertEquals("Node2", node2, list.get(1));
+
+        dump(attribute);
+    }
+    
+    public void testNavigationUsingQNames() throws Exception {
+        QName name1 = new QName("http://something", "foo", "f");
+        
+        Node node = new Node(null, null, new ArrayList());
+        Node child = new Node(null, new QName("http://something", "foo", "f"), new HashMap(), new ArrayList());
+        child.attributes().put("cheese", "Edam");
+        Node grandChild = new Node(null, new QName("http://something", "bar", "f"), new HashMap(), new ArrayList());
+        grandChild.attributes().put("drink", "Beer");
+        grandChild.children().add("I am a youngling");
+        child.children().add(grandChild);
+        
+        node.children().add(child);
+
+        // lets look up by QName
+        Object value = node.getAt(name1);
+        assertTrue("Should return a list: " + value, value instanceof NodeList);
+        NodeList list = (NodeList) value;
+        assertEquals("Size", 1, list.size());
+        
+        Node answer = (Node) list.get(0);
+        assertNotNull("Node is null!", answer);
+        
+        System.out.println("Found node: " + answer);
+        
+        // now lets navigate the list
+        NodeList gc = list.getAt(new QName("http://something", "bar"));
+        assertEquals("grand children size", 1, gc.size());
+        
+        System.out.println("Found grandChild: " + gc);
+        
+        String text= gc.text();
+        assertEquals("text of grandchild", "I am a youngling", text);
+    }
+
+    protected void dump(Node node) {
+        NodePrinter printer = new NodePrinter();
+        printer.print(node);
+    }
+
+}
diff --git a/groovy/src/test/groovy/util/ObjectGraphBuilderTest.groovy b/groovy/src/test/groovy/util/ObjectGraphBuilderTest.groovy
new file mode 100644
index 0000000..6b3444b
--- /dev/null
+++ b/groovy/src/test/groovy/util/ObjectGraphBuilderTest.groovy
@@ -0,0 +1,146 @@
+package groovy.util

+

+class ObjectGraphBuilderTest extends GroovyTestCase {

+   ObjectGraphBuilder builder

+

+   void testCompany() {

+      def expected = new Company( name: 'ACME', employees: [] )

+      def actual = builder.company( name: 'ACME', employees: [] )

+      assert actual != null

+      //assert actual.class == Company

+      assert actual.name == expected.name

+      assert actual.address == expected.address

+      assert actual.employees == expected.employees

+   }

+

+   void testCompanyAndAddress() {

+      def expectedAddress = new Address( line1: '123 Groovy Rd', zip: 12345, state: 'JV' )

+      def expectedCompany = new Company( name: 'ACME', employees: [], address: expectedAddress )

+      def actualCompany = builder.company( name: 'ACME', employees: [] ) {

+         address( line1: '123 Groovy Rd', zip: 12345, state: 'JV' )

+      }

+      assert actualCompany != null

+      //assert actualCompany.class == Company

+      assert actualCompany.name == expectedCompany.name

+      assert actualCompany.employees == expectedCompany.employees

+      def actualAddress = actualCompany.address

+      assert actualAddress != null

+      //assert actualAddress.class == Address

+      assert actualAddress.line1 == expectedAddress.line1

+      assert actualAddress.line2 == expectedAddress.line2

+      assert actualAddress.zip == expectedAddress.zip

+      assert actualAddress.state == expectedAddress.state

+   }

+

+   void testCompanyAndEmployeeAndAddress() {

+      def expectedAddress = new Address( line1: '123 Groovy Rd', zip: 12345, state: 'JV' )

+      def expectedEmployee = new Employee( name: 'Duke', employeeId: 1, address: expectedAddress )

+      def expectedCompany = new Company( name: 'ACME' )

+      def actualCompany = builder.company( name: 'ACME', employees: [] ) {

+         employee(  name: 'Duke', employeeId: 1 ) {

+            address( line1: '123 Groovy Rd', zip: 12345, state: 'JV' )

+         }

+      }

+      assert actualCompany != null

+      //assert actualCompany.class == Company

+      assert actualCompany.name == expectedCompany.name

+      assert actualCompany.employees.size() == 1

+      def actualEmployee = actualCompany.employees[0]

+      //assert actualEmployee.class == Employee

+      assert actualEmployee.name == expectedEmployee.name

+      assert actualEmployee.employeeId == expectedEmployee.employeeId

+      def actualAddress = actualEmployee.address

+      assert actualAddress != null

+      //assert actualAddress.class == Address

+      assert actualAddress.line1 == expectedAddress.line1

+      assert actualAddress.line2 == expectedAddress.line2

+      assert actualAddress.zip == expectedAddress.zip

+      assert actualAddress.state == expectedAddress.state

+   }

+

+   void testCompanyAndEmployeeSameAddress() {

+      def expectedAddress = new Address( line1: '123 Groovy Rd', zip: 12345, state: 'JV' )

+      def expectedEmployee = new Employee( name: 'Duke', employeeId: 1, address: expectedAddress )

+      def expectedCompany = new Company( name: 'ACME' )

+      def actualCompany = builder.company( name: 'ACME', employees: [] ) {

+         address( id: 'a1', line1: '123 Groovy Rd', zip: 12345, state: 'JV' )

+         employee(  name: 'Duke', employeeId: 1, address: a1 )

+      }

+      assert actualCompany != null

+      //assert actualCompany.class == Company

+      assert actualCompany.name == expectedCompany.name

+      assert actualCompany.employees.size() == 1

+      def actualEmployee = actualCompany.employees[0]

+      //assert actualEmployee.class == Employee

+      assert actualEmployee.name == expectedEmployee.name

+      assert actualEmployee.employeeId == expectedEmployee.employeeId

+      assert actualCompany.address == actualEmployee.address

+   }

+

+   void testCompanyAndEmployeeSameAddressWithRef() {

+      def expectedAddress = new Address( line1: '123 Groovy Rd', zip: 12345, state: 'JV' )

+      def expectedEmployee = new Employee( name: 'Duke', employeeId: 1, address: expectedAddress )

+      def expectedCompany = new Company( name: 'ACME' )

+      def actualCompany = builder.company( name: 'ACME', employees: [] ) {

+         address( id: 'a1', line1: '123 Groovy Rd', zip: 12345, state: 'JV' )

+         employee(  name: 'Duke', employeeId: 1 ){

+            address( refId: 'a1' )

+         }

+      }

+      assert actualCompany != null

+      //assert actualCompany.class == Company

+      assert actualCompany.name == expectedCompany.name

+      assert actualCompany.employees.size() == 1

+      def actualEmployee = actualCompany.employees[0]

+      //assert actualEmployee.class == Employee

+      assert actualEmployee.name == expectedEmployee.name

+      assert actualEmployee.employeeId == expectedEmployee.employeeId

+      assert actualCompany.address == actualEmployee.address

+      assert actualEmployee.company == actualCompany

+   }

+

+   void testCompanyAndManyEmployees() {

+      def actualCompany = builder.company( name: 'ACME', employees: [] ) {

+         3.times {

+            employee(  name: "Duke $it", employeeId: it )

+         }

+      }

+      assert actualCompany != null

+      assert actualCompany.employees.size() == 3

+      3.times {

+         assert actualCompany.employees[it].name == "Duke $it"

+      }

+      //assert actualCompany.employees*.getClass() == [Employee,Employee,Employee]

+   }

+

+   void setUp() {

+      builder = new ObjectGraphBuilder()

+      builder.classNameResolver = "groovy.util"

+   }

+}

+

+class Company {

+   String name

+   Address address

+   List employees = []

+

+   String toString() { "Company=[name:${name}, address:${address}, employees:${employees}]" }

+}

+

+class Address {

+   String line1

+   String line2

+   int zip

+   String state

+

+   String toString() { "Address=[line1:${line1}, line2:${line2}, zip:${zip}, state:${state}]" }

+}

+

+class Employee {

+   String name

+   int employeeId

+   Address address

+   Company company

+

+   String toString() { "Employee=[name:${name}, employeeId:${employeeId}, address:${address}, company:${company.name}]" }

+}

diff --git a/groovy/src/test/groovy/util/ObservableMapTest.groovy b/groovy/src/test/groovy/util/ObservableMapTest.groovy
new file mode 100644
index 0000000..a624def
--- /dev/null
+++ b/groovy/src/test/groovy/util/ObservableMapTest.groovy
@@ -0,0 +1,105 @@
+/*

+ * Copyright 2003-2007 the original author or authors.

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License");

+ * you may not use this file except in compliance with the License.

+ * You may obtain a copy of the License at

+ *

+ *     http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+

+package groovy.util

+

+import java.beans.PropertyChangeEvent

+import java.beans.PropertyChangeListener

+

+/**

+ * @author <a href="mailto:aalmiray@users.sourceforge.net">Andres Almiray</a>

+ */

+class ObservableMapTest extends GroovyTestCase {

+   void testFireEvent_withoutTest(){

+      def map = new ObservableMap()

+      def listener = new SamplePropertyChangeListener()

+      map.addPropertyChangeListener( listener )

+

+      def key = 'key'

+      def value1 = 'value1'

+      def value2 = 'value2'

+      map[key] = null

+      assertNull( listener.event )

+

+      map[key] = value1

+      assertNotNull( listener.event )

+      assertEquals( map, listener.event.source )

+      assertEquals( key, listener.event.propertyName )

+      assertNull( listener.event.oldValue )

+      assertEquals( value1, listener.event.newValue )

+

+      map[key] = value2

+      assertNotNull( listener.event )

+      assertEquals( map, listener.event.source )

+      assertEquals( key, listener.event.propertyName )

+      assertEquals( value1, listener.event.oldValue )

+      assertEquals( value2, listener.event.newValue )

+

+      listener.event = null

+      map[key] = value2

+      assertNull( listener.event )

+

+   }

+

+   void testFireEvent_withTest(){

+      def map = new ObservableMap( { it != 'value2' } )

+      def listener = new SamplePropertyChangeListener()

+      map.addPropertyChangeListener( listener )

+

+      def key = 'key'

+      def value1 = 'value1'

+      def value2 = 'value2'

+      map[key] = value1

+      assertNotNull( listener.event )

+      assertEquals( map, listener.event.source )

+      assertEquals( key, listener.event.propertyName )

+      assertNull( listener.event.oldValue )

+      assertEquals( value1, listener.event.newValue )

+

+      listener.event = null

+      map[key] = value2

+      assertNull( listener.event )

+   }

+

+   void testFireEvent_withTestOnKey(){

+      def map = new ObservableMap( { name, value -> name != 'key' } )

+      def listener = new SamplePropertyChangeListener()

+      map.addPropertyChangeListener( listener )

+      

+      def key = 'key'

+      def value1 = 'value1'

+      def value2 = 'value2'

+      map[key] = value1

+      assertNull( listener.event )

+      map[key] = value2

+      assertNull( listener.event )

+

+      map['key2'] = value1

+      assertNotNull( listener.event )

+      assertEquals( map, listener.event.source )

+      assertEquals( 'key2', listener.event.propertyName )

+      assertNull( listener.event.oldValue )

+      assertEquals( value1, listener.event.newValue )

+   }

+}

+

+class SamplePropertyChangeListener implements PropertyChangeListener {

+   PropertyChangeEvent event

+

+   public void propertyChange( PropertyChangeEvent evt ){

+      event = evt

+   }

+}

diff --git a/groovy/src/test/groovy/util/OrderByTest.groovy b/groovy/src/test/groovy/util/OrderByTest.groovy
new file mode 100644
index 0000000..6f4b98b
--- /dev/null
+++ b/groovy/src/test/groovy/util/OrderByTest.groovy
@@ -0,0 +1,56 @@
+package groovy.util
+
+class OrderByTest extends GroovyTestCase {
+
+    void testSortByOneField() {
+        def builder = new NodeBuilder()
+        def tree = builder.people {
+            person(name:'James', cheese:'Edam', location:'London')
+            person(name:'Bob', cheese:'Cheddar', location:'Atlanta')
+            person(name:'Chris', cheese:'Red Leicester', location:'London')
+            person(name:'Joe', cheese:'Brie', location:'London')
+        }
+        
+        def people = tree.children()
+        
+        /** @todo parser should allow this syntax sugar
+        def order = new OrderBy { it.get('@cheese') }
+        */
+        def order = new OrderBy( { it.get('@cheese') } )
+        def sorted = people.sort(order)
+        
+        assert sorted.get(0).get('@name') == 'Joe'
+        assert sorted.get(1).get('@name') == 'Bob'
+        assert sorted.get(2).get('@name') == 'James'
+        assert sorted.get(3).get('@name') == 'Chris'
+        
+        order = new OrderBy( { it.get('@name') } )
+        sorted = people.sort(order)
+        
+        assert sorted.get(0).get('@name') == 'Bob'
+        assert sorted.get(1).get('@name') == 'Chris'
+        assert sorted.get(2).get('@name') == 'James'
+        assert sorted.get(3).get('@name') == 'Joe'
+    }
+
+
+    void testSortByMultipleFields() {
+        def builder = new NodeBuilder()
+        def tree = builder.people {
+            person(name:'James', cheese:'Edam', location:'London')
+            person(name:'Bob', cheese:'Cheddar', location:'Atlanta')
+            person(name:'Chris', cheese:'Red Leicester', location:'London')
+            person(name:'Joe', cheese:'Brie', location:'London')
+        }
+        
+        def people = tree.children()
+
+        def order = new OrderBy([ { it.get('@location') }, { it.get('@cheese') } ])
+        def sorted = people.sort(order)
+        
+        assert sorted.get(0).get('@name') == 'Bob'
+        assert sorted.get(1).get('@name') == 'Joe'
+        assert sorted.get(2).get('@name') == 'James'
+        assert sorted.get(3).get('@name') == 'Chris'
+    }
+}
diff --git a/groovy/src/test/groovy/util/ProxyGeneratorTest.groovy b/groovy/src/test/groovy/util/ProxyGeneratorTest.groovy
new file mode 100644
index 0000000..dd07203
--- /dev/null
+++ b/groovy/src/test/groovy/util/ProxyGeneratorTest.groovy
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.util
+
+/**
+ * @author Paul King
+ */
+class ProxyGeneratorTest extends GroovyTestCase {
+
+    void testAggregateFromBaseClass() {
+        Map map = [myMethodB: {"the new B"}, myMethodX: {"the injected X"}]
+        def testClass = ProxyGenerator.instantiateAggregateFromBaseClass(map, TestClass)
+        assert testClass instanceof TestClass
+        assert testClass.myMethodA() == "the original A"
+        assert testClass.myMethodB() == "the new B"
+        assert testClass.myMethodX() == "the injected X"
+    }
+
+    void testAggregateFromAbstractBaseClass() {
+        Map map = [myMethodG: {"the concrete G"}, myMethodX: {"the injected X"}]
+        def testClass = ProxyGenerator.instantiateAggregateFromBaseClass(map, AbstractClass)
+        assert testClass instanceof AbstractClass
+        assert testClass.myMethodA() == "the original A"
+        assert testClass.myMethodG() == "the concrete G"
+        assert testClass.myMethodX() == "the injected X"
+    }
+
+    void testAggregateFromInterface() {
+        Map map = [myMethodC: {"the injected C"}]
+        def testClass = ProxyGenerator.instantiateAggregateFromInterface(map, TestInterface)
+        assert testClass instanceof TestInterface
+        assert testClass instanceof GroovyObject
+        assert testClass.myMethodC() == "the injected C"
+    }
+
+    void testAggregate() {
+        Map map = [myMethodE: {"the injected E"}, myMethodB: {"the new B"}, myMethodX: {"the injected X"}]
+        def testClass = ProxyGenerator.instantiateAggregate(map, [TestInterface, TestOtherInterface], TestClass)
+        assert testClass instanceof TestInterface
+        assert testClass instanceof TestOtherInterface
+        assert testClass instanceof TestClass
+        assert testClass.myMethodA() == "the original A"
+        assert testClass.myMethodB() == "the new B"
+        assert testClass.myMethodX() == "the injected X"
+        assert testClass.myMethodE() == "the injected E"
+    }
+
+    void testDelegate() {
+        def delegate = new TestClass()
+        Map map = [myMethodE: {"the injected E"}, myMethodB: {"the new B"}, myMethodX: {"the injected X"}]
+        def testClass = ProxyGenerator.instantiateDelegate(map, [TestInterface, TestOtherInterface], delegate)
+        assert testClass instanceof TestInterface
+        assert testClass instanceof TestOtherInterface
+        assert testClass.myMethodA() == "the original A"
+        assert testClass.myMethodB() == "the new B"
+        assert testClass.myMethodX() == "the injected X"
+        assert testClass.myMethodE() == "the injected E"
+    }
+
+    void testDelegateWithBaseClass() {
+        def delegate = new TestClass()
+        Map map = [myMethodE: {"the injected E"}, myMethodB: {"the new B"}, myMethodX: {"the injected X"}]
+        TestClass testClass = ProxyGenerator.instantiateDelegateWithBaseClass(map, [TestInterface, TestOtherInterface], delegate)
+        assert testClass instanceof TestInterface
+        assert testClass instanceof TestOtherInterface
+        assert testClass.myMethodA() == "the original A"
+        assert testClass.myMethodB() == "the new B"
+        assert testClass.myMethodX() == "the injected X"
+        assert testClass.myMethodE() == "the injected E"
+    }
+}
+
+class TestClass {
+    def myMethodA() { return "the original A" }
+    def myMethodB() { return "the original B" }
+}
+
+interface TestInterface {
+    def myMethodA()
+    def myMethodC()
+    def myMethodD()
+}
+
+interface TestOtherInterface {
+    def myMethodB()
+    def myMethodE()
+    def myMethodF()
+}
+
+abstract class AbstractClass {
+    def myMethodA() { return "the original A" }
+    abstract myMethodG()
+}
diff --git a/groovy/src/test/groovy/util/ProxyTest.groovy b/groovy/src/test/groovy/util/ProxyTest.groovy
new file mode 100644
index 0000000..46920d8
--- /dev/null
+++ b/groovy/src/test/groovy/util/ProxyTest.groovy
@@ -0,0 +1,64 @@
+package groovy.util
+
+/**
+* @author Dierk Koenig
+**/
+class ProxyTest extends GroovyTestCase {
+
+    void testStringDecoration(){
+        def original = 'decorated String'
+        def proxy = new StringDecorator().wrap(original)
+        // method, that is only known on proxy
+        assertSame original, proxy.adaptee
+        // method, that is only known on adaptee is relayed through the proxy
+        assertEquals original.size(), proxy.size()
+        // method, that is availabe in both objects should come from proxy
+        assertEquals 0, proxy.length()
+        // method, that is availabe in both objects
+        // but should come from adaptee needs explicit relay
+        assertEquals original, proxy.toString()
+        // method from decorator, that is not in adaptee
+        assertEquals 'new Method reached', proxy.someNewMethod()
+    }
+
+  /*
+   *  Some test cases to probe perceived problems with each and collect on Proxy objects.
+   *  cf. GROOVY-1461.  Jonathan Carlson <Jonathan.Carlson@katun.com> made a proposal for a test
+   *  as a single method, Russel Winder <russel@russel.org.uk> split things up when entering
+   *  them so that there is only a single assert per method to try and maximize the benefit of
+   *  the tests.
+   */
+
+  void testProxyCollect ( ) {
+    def collection = [ 1 , 2 , 3 ]
+    def proxy = ( new Proxy ( ) ).wrap ( collection ) 
+    assertEquals ( [ 2 , 3 , 4 ] , proxy.collect { it + 1 } )
+  }
+
+  void testProxyAny ( ) {
+    def collection = [ 1 , 2 , 3 ]
+    def proxy = ( new Proxy ( ) ).wrap ( collection ) 
+    assertEquals ( true , proxy.any { it == 2 } )
+  }
+
+  void testProxyFind ( ) {
+    def collection = [ 1 , 2 , 3 ]
+    def proxy = ( new Proxy ( ) ).wrap ( collection ) 
+    assertEquals ( 2 , proxy.find { it == 2 } )
+  }
+
+  void testProxyEach ( ) {
+    def collection = [ 1 , 2 , 3 ]
+    def proxy = ( new Proxy ( ) ).wrap ( collection ) 
+    def testString = ''
+    proxy.each { testString += it }
+    assertEquals ( '123' , testString )
+  }
+
+}
+
+class StringDecorator extends Proxy{
+    int length()          { 0 }
+    String toString()     { adaptee.toString()}
+    String someNewMethod(){ 'new Method reached' }
+}
diff --git a/groovy/src/test/groovy/util/SpoofTask.java b/groovy/src/test/groovy/util/SpoofTask.java
new file mode 100644
index 0000000..fdcc8dd
--- /dev/null
+++ b/groovy/src/test/groovy/util/SpoofTask.java
@@ -0,0 +1,31 @@
+package groovy.util;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+public class SpoofTask extends Task {
+    private int foo;
+
+    public SpoofTask() {
+        super();
+        SpoofTaskContainer.spoof("SpoofTask ctor");
+    }
+
+    public void setFoo(final int i) {
+        foo = i;
+    }
+
+
+    public void execute() throws BuildException {
+        SpoofTaskContainer.spoof("begin SpoofTask execute");
+        SpoofTaskContainer.spoof("tag name from wrapper: " + getWrapper().getElementTag());
+        // don't rely on Map.toString(), behaviour is not documented
+        SpoofTaskContainer.spoof("attributes map from wrapper: "
+                + InvokerHelper.toMapString(getWrapper().getAttributeMap()));
+        SpoofTaskContainer.spoof("param foo: " + foo);
+
+        SpoofTaskContainer.spoof("end SpoofTask execute");
+    }
+
+}
diff --git a/groovy/src/test/groovy/util/SpoofTaskContainer.java b/groovy/src/test/groovy/util/SpoofTaskContainer.java
new file mode 100644
index 0000000..9587e27
--- /dev/null
+++ b/groovy/src/test/groovy/util/SpoofTaskContainer.java
@@ -0,0 +1,54 @@
+package groovy.util;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.UnknownElement;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class SpoofTaskContainer extends Task implements TaskContainer {
+    private List tasks = new ArrayList();
+    static StringBuffer spoof = new StringBuffer();
+
+    public SpoofTaskContainer() {
+        super();
+        spoof("SpoofTaskContainer ctor");
+    }
+
+    static StringBuffer getSpoof() {
+        return spoof;
+    }
+
+    static void resetSpoof() {
+        spoof = new StringBuffer();
+    }
+
+    static void spoof(String message) {
+        spoof.append(message);
+        spoof.append("\n");
+    }
+
+    public void addTask(Task task) {
+        // to work with ant 1.6
+        spoof("in addTask");
+        if (task instanceof UnknownElement) {
+            spoof("configuring UnknownElement");
+            task.maybeConfigure();
+            task = ((UnknownElement) task).getTask();
+        }
+        tasks.add(task);
+    }
+
+    public void execute() throws BuildException {
+        spoof("begin SpoofTaskContainer execute");
+        for (Iterator iter = tasks.iterator(); iter.hasNext();) {
+            Task task = (Task) iter.next();
+            task.perform();
+        }
+        spoof("end SpoofTaskContainer execute");
+    }
+
+}
diff --git a/groovy/src/test/groovy/util/StringTestUtil.groovy b/groovy/src/test/groovy/util/StringTestUtil.groovy
new file mode 100644
index 0000000..34f7bb1
--- /dev/null
+++ b/groovy/src/test/groovy/util/StringTestUtil.groovy
@@ -0,0 +1,14 @@
+package groovy.util

+

+import junit.framework.Assert

+

+class StringTestUtil {

+    static void assertMultilineStringsEqual(String a, String b) {

+        def aLines = a.trim().replaceAll('\r','').split('\n')

+        def bLines = b.trim().replaceAll('\r','').split('\n')

+        assert aLines.size() == bLines.size()

+        for (i in 0..<aLines.size()) {

+            Assert.assertEquals(aLines[i].trim(), bLines[i].trim())

+        }

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/util/XmlParserTest.groovy b/groovy/src/test/groovy/util/XmlParserTest.groovy
new file mode 100644
index 0000000..9cab995
--- /dev/null
+++ b/groovy/src/test/groovy/util/XmlParserTest.groovy
@@ -0,0 +1,238 @@
+package groovy.util
+
+import groovy.xml.TraversalTestSupport
+import groovy.xml.GpathSyntaxTestSupport
+import groovy.xml.MixedMarkupTestSupport
+
+class XmlParserTest extends GroovyTestCase {
+
+    def getRoot = { xml -> new XmlParser().parseText(xml) }
+
+    static bookXml = """
+<html xmlns="http://www.w3.org/HTML/1998/html4"
+  xmlns:g="http://groovy.codehaus.org/roles"
+  xmlns:dc="http://purl.org/dc/elements/1.1/">
+  <head><title>GINA Book Review</title></head>
+  <body>
+<h1><dc:title>Groovy in Action Review</dc:title></h1>
+<table>
+  <tr align="center">
+    <td>Author</td><td>Price</td><td>Pages</td><td>Date</td>
+  </tr>
+  <tr align="left">
+    <td><dc:author>
+      <g:developer>Dierk König</g:developer>,
+      <g:advocate>Andrew Glover</g:advocate>,
+      <g:developer>Paul King</g:developer>,
+      <g:projectmanager>Guillaume Laforge</g:projectmanager>,
+      <g:advocate>Jon Skeet</g:advocate>,
+    </dc:author></td>
+    <td>49.99</td>
+    <td>696</td>
+    <td><dc:date>January, 2007</dc:date></td>
+  </tr>
+</table>
+<p>Review: Great book!</p>
+  </body>
+</html>
+"""
+
+    void testNodePrinter() {
+        def text = """
+<p>Please read the <a href="index.html">Home</a> page</p>
+"""
+        def node = new XmlParser().parseText(text)
+        def StringWriter sw = new StringWriter()
+        new NodePrinter(new PrintWriter(sw)).print(node)
+        def result = fixEOLs(sw.toString())
+        def expected = '''\
+p() {
+  builder.append(Please read the)
+  a(href:'index.html') {
+    builder.append(Home)
+  }
+  builder.append(page)
+}
+'''
+        assert result == expected
+    }
+
+    void testXmlNodePrinter() {
+        def text = """
+<p>Please read the <a href="index.html">Home</a> page</p>
+"""
+        def node = new XmlParser().parseText(text)
+        def StringWriter sw = new StringWriter()
+        new XmlNodePrinter(new PrintWriter(sw)).print(node)
+        def result = fixEOLs(sw.toString())
+        def expected = '''\
+<p>
+  Please read the
+  <a href="index.html">
+    Home
+  </a>
+  page
+</p>
+'''
+        assert result == expected
+    }
+
+    void testXmlNodePrinterNamespaces() {
+        def html = new XmlParser().parseText(bookXml)
+        def StringWriter sw = new StringWriter()
+        new XmlNodePrinter(new PrintWriter(sw)).print(html)
+        def result = fixEOLs(sw.toString())
+        def expected = '''\
+<html>
+  <head>
+    <title>
+      GINA Book Review
+    </title>
+  </head>
+  <body>
+    <h1>
+      <dc:title>
+        Groovy in Action Review
+      </dc:title>
+    </h1>
+    <table>
+      <tr align="center">
+        <td>
+          Author
+        </td>
+        <td>
+          Price
+        </td>
+        <td>
+          Pages
+        </td>
+        <td>
+          Date
+        </td>
+      </tr>
+      <tr align="left">
+        <td>
+          <dc:author>
+            <g:developer>
+              Dierk König
+            </g:developer>
+            ,
+            <g:advocate>
+              Andrew Glover
+            </g:advocate>
+            ,
+            <g:developer>
+              Paul King
+            </g:developer>
+            ,
+            <g:projectmanager>
+              Guillaume Laforge
+            </g:projectmanager>
+            ,
+            <g:advocate>
+              Jon Skeet
+            </g:advocate>
+            ,
+          </dc:author>
+        </td>
+        <td>
+          49.99
+        </td>
+        <td>
+          696
+        </td>
+        <td>
+          <dc:date>
+            January, 2007
+          </dc:date>
+        </td>
+      </tr>
+    </table>
+    <p>
+      Review: Great book!
+    </p>
+  </body>
+</html>
+'''
+        assert result == expected
+    }
+
+    void testNamespaceGPath() {
+        def anyName = new groovy.xml.QName("*", "*")
+        def anyHtml = new groovy.xml.QName("http://www.w3.org/HTML/1998/html4", "*")
+        def anyTitle = new groovy.xml.QName("*", "title")
+        def html = new XmlParser().parseText(bookXml)
+
+        // qname style
+        def result = html[anyName][anyHtml][anyTitle].text()
+        assert result == 'Groovy in Action Review'
+
+        // string wildcard style
+        result = html.'*:*'.':*'.'*:title'.text()
+        assert result == 'Groovy in Action Review'
+
+        // just for fun, mix the styles
+        result = html.'*'.'http://www.w3.org/HTML/1998/html4:*'[anyTitle].text()
+        assert result == 'Groovy in Action Review'
+
+        // try traversal
+        assert html.'**'['dc:*']*.name()*.localPart == ["title", "author", "date"]
+    }
+
+    void testElement() {
+        GpathSyntaxTestSupport.checkElement(getRoot)
+        GpathSyntaxTestSupport.checkFindElement(getRoot)
+        GpathSyntaxTestSupport.checkElementTypes(getRoot)
+        GpathSyntaxTestSupport.checkElementClosureInteraction(getRoot)
+    }
+
+    void testAttribute() {
+        GpathSyntaxTestSupport.checkAttribute(getRoot)
+        GpathSyntaxTestSupport.checkAttributes(getRoot)
+    }
+
+    void testNavigation() {
+        GpathSyntaxTestSupport.checkChildren(getRoot)
+        GpathSyntaxTestSupport.checkParent(getRoot)
+        GpathSyntaxTestSupport.checkNestedSizeExpressions(getRoot)
+    }
+
+    void testTraversal() {
+        TraversalTestSupport.checkDepthFirst(getRoot)
+        TraversalTestSupport.checkBreadthFirst(getRoot)
+    }
+
+    void testMixedMarkup() {
+        MixedMarkupTestSupport.checkMixedMarkup(getRoot)
+    }
+
+    void testWhitespaceTrimming() {
+        def text = '<outer><inner>   Here is some text    </inner></outer>'
+        def parser = new XmlParser()
+        def outer = parser.parseText(text)
+        assert outer.inner.text() == 'Here is some text'
+        parser.setTrimWhitespace false
+        outer = parser.parseText(text)
+        assert outer.inner.text() == '   Here is some text    '
+    }
+
+    void testUpdate() {
+        def xml = '<root></root>'
+        def parser = new XmlParser()
+        def root = parser.parseText(xml)
+        root.appendNode('child', [attr:'child attr'], 'child text')
+        root.@attr = 'root attr'
+        root.'@other' = 'other attr'
+
+        def writer = new StringWriter()
+        new XmlNodePrinter(new PrintWriter(writer)).print(root)
+        def result = writer.toString()
+        assert result == '''\
+<root attr="root attr" other="other attr">
+  <child attr="child attr">
+    child text
+  </child>
+</root>
+'''
+    }
+}
diff --git a/groovy/src/test/groovy/util/XmlSlurperTest.groovy b/groovy/src/test/groovy/util/XmlSlurperTest.groovy
new file mode 100644
index 0000000..a483c98
--- /dev/null
+++ b/groovy/src/test/groovy/util/XmlSlurperTest.groovy
@@ -0,0 +1,79 @@
+package groovy.util
+
+import groovy.xml.TraversalTestSupport
+import groovy.xml.GpathSyntaxTestSupport
+import groovy.xml.MixedMarkupTestSupport
+import groovy.xml.StreamingMarkupBuilder
+
+class XmlSlurperTest extends GroovyTestCase {
+
+    def getRoot = { xml -> new XmlSlurper().parseText(xml) }
+
+    void testWsdl() {
+        def wsdl = '''
+            <definitions name="AgencyManagementService"
+                         xmlns:ns1="http://www.example.org/NS1"
+                         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+                         xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
+                         xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
+                         xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
+                         xmlns="http://schemas.xmlsoap.org/wsdl/">                                              
+                <message name="SomeRequest">                                                          
+                    <part name="parameters" element="ns1:SomeReq" />                                  
+                </message>                                                                            
+                <message name="SomeResponse">                                                         
+                    <part name="result" element="ns1:SomeRsp" />                                      
+                </message>                                                                            
+            </definitions>                                                                            
+            '''
+        def xml = new XmlSlurper().parseText(wsdl)
+        assert xml.message.part.@element.findAll {it =~ /.Req$/}.size() == 1
+        assert xml.message.part.findAll { true }.size() == 2
+        assert xml.message.part.find { it.name() == 'part' }.name() == 'part'
+        assert xml.message.findAll { true }.size() == 2
+        assert xml.message.part.lookupNamespace("ns1") == "http://www.example.org/NS1"
+        assert xml.message.part.lookupNamespace("") == "http://schemas.xmlsoap.org/wsdl/"
+        assert xml.message.part.lookupNamespace("undefinedPrefix") == null
+        xml.message.findAll { true }.each { assert it.name() == "message"}
+    }
+
+    void testElement() {
+        GpathSyntaxTestSupport.checkElement(getRoot)
+        GpathSyntaxTestSupport.checkFindElement(getRoot)
+        GpathSyntaxTestSupport.checkElementTypes(getRoot)
+        GpathSyntaxTestSupport.checkElementClosureInteraction(getRoot)
+    }
+
+    void testAttribute() {
+        GpathSyntaxTestSupport.checkAttribute(getRoot)
+        GpathSyntaxTestSupport.checkAttributes(getRoot)
+    }
+
+    void testNavigation() {
+        GpathSyntaxTestSupport.checkChildren(getRoot)
+        GpathSyntaxTestSupport.checkParent(getRoot)
+        GpathSyntaxTestSupport.checkNestedSizeExpressions(getRoot)
+    }
+
+    void testTraversal() {
+        TraversalTestSupport.checkDepthFirst(getRoot)
+        TraversalTestSupport.checkBreadthFirst(getRoot)
+    }
+
+    void testMixedMarkup() {
+        MixedMarkupTestSupport.checkMixedMarkup(getRoot)
+    }
+    
+    void testReplace() {
+        def input = "<doc><sec>Hello<p>World</p></sec></doc>"
+        def replaceSlurper = new XmlSlurper().parseText(input) 
+        
+        replaceSlurper.sec.replaceNode{ node ->
+          t(){ delegate.mkp.yield node.getBody() }
+        }
+        
+        def outputSlurper = new StreamingMarkupBuilder()
+        String output = outputSlurper.bind{ mkp.yield replaceSlurper }
+        assert output == "<doc><t>Hello<p>World</p></t></doc>"
+    }
+}
diff --git a/groovy/src/test/groovy/util/suite/ATestScriptThatsNoTestCase.groovy b/groovy/src/test/groovy/util/suite/ATestScriptThatsNoTestCase.groovy
new file mode 100644
index 0000000..35de296
--- /dev/null
+++ b/groovy/src/test/groovy/util/suite/ATestScriptThatsNoTestCase.groovy
@@ -0,0 +1,2 @@
+// used for testing ScriptTestCaseAdapter usage with AllTestSuite
+assert true
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/DOMTest.groovy b/groovy/src/test/groovy/xml/DOMTest.groovy
new file mode 100644
index 0000000..09749d2
--- /dev/null
+++ b/groovy/src/test/groovy/xml/DOMTest.groovy
@@ -0,0 +1,81 @@
+package groovy.xml
+
+/**
+ * This test uses the concise syntax to test the building of 
+ * W3C DOM trees using GroovyMarkup
+ */
+class DOMTest extends TestXmlSupport {
+    
+    void testSmallTree() {
+        def b = DOMBuilder.newInstance()
+        
+        def root = b.root1(a:5, b:7) {
+            elem1('hello1')
+            elem2('hello2')
+            elem3(x:7)
+        }
+        
+        assert root != null
+        
+        dump(root)
+    }
+    
+    void testTree() {
+        def b = DOMBuilder.newInstance()
+        
+        def root = b.root2(a:5, b:7) {
+            elem1('hello1')
+            elem2('hello2')
+            nestedElem(x:'abc', y:'def') {
+                child(z:'def')
+                child2()  
+            }
+            
+            nestedElem2(z:'zzz') {
+                child(z:'def')
+                child2("hello")  
+            }
+        }
+        
+        assert root != null
+        
+        dump(root)
+
+/*
+        def elem1 = root.elem1
+        assert elem1.value() := 'hello1'
+        
+        def elem2 = root.elem2
+        assert elem2.value() := 'hello2'
+
+        assert root.elem1.value() := 'hello1'
+        assert root.elem2.value() := 'hello2'
+
+        assert root.nestedElem.attributes() := ['x':'abc', 'y':'def']        
+        assert root.nestedElem.child.attributes() := ['z':'def']
+        assert root.nestedElem.child2.value() := []
+        assert root.nestedElem.child2.text() := ''
+
+        assert root.nestedElem2.attributes() := ['z':'zzz']      
+        assert root.nestedElem2.child.attributes() := ['z':'def']
+        assert root.nestedElem2.child2.value() := 'hello'
+        assert root.nestedElem2.child2.text() := 'hello'
+        
+        def list = root.value()
+        assert list.size() := 4
+        
+        assert root.attributes().a := 5
+        assert root.attributes().b := 7
+
+        assert root.nestedElem.attributes().x := 'abc'
+        assert root.nestedElem.attributes().y := 'def'
+        assert root.nestedElem2.attributes().z := 'zzz'
+        assert root.nestedElem2.child.attributes().z := 'def'
+*/        
+        /** @todo parser add .@ as an operation
+                assert root.@a := 5
+                assert root.@b := 7
+        */        
+    }
+    
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/FactorySupportTest.java b/groovy/src/test/groovy/xml/FactorySupportTest.java
new file mode 100644
index 0000000..0909126
--- /dev/null
+++ b/groovy/src/test/groovy/xml/FactorySupportTest.java
@@ -0,0 +1,45 @@
+package groovy.xml;
+
+import junit.framework.TestCase;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+public class FactorySupportTest extends TestCase {
+    private static final PrivilegedActionException PRIVILEGED_ACTION_EXCEPTION = new PrivilegedActionException(new IllegalStateException());
+    private static final ParserConfigurationException PARSER_CONFIGURATION_EXCEPTION = new ParserConfigurationException();
+
+    public void testCreatesFactories() throws Exception {
+        assertNotNull(FactorySupport.createDocumentBuilderFactory());
+        assertNotNull(FactorySupport.createSaxParserFactory());
+    }
+
+    public void testParserConfigurationExceptionNotWrapped() throws ParserConfigurationException {
+        try {
+            FactorySupport.createFactory(new PrivilegedExceptionAction() {
+                public Object run() throws Exception {
+                    throw PARSER_CONFIGURATION_EXCEPTION;
+                }
+            });
+            fail("Exception was not caught");
+        } catch (Throwable t) {
+            assertSame(PARSER_CONFIGURATION_EXCEPTION, t);
+        }
+    }
+
+    public void testOtherExceptionsWrappedAsUnchecked() throws ParserConfigurationException {
+        try {
+            FactorySupport.createFactory(new PrivilegedExceptionAction() {
+                public Object run() throws Exception {
+                    throw PRIVILEGED_ACTION_EXCEPTION;
+                }
+            });
+            fail("Exception was not caught");
+        } catch (RuntimeException re) {
+            assertSame(PRIVILEGED_ACTION_EXCEPTION, re.getCause());
+        } catch (Throwable t) {
+            fail("Exception was not wrapped as runtime");
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/GpathSyntaxTestSupport.groovy b/groovy/src/test/groovy/xml/GpathSyntaxTestSupport.groovy
new file mode 100644
index 0000000..37f438e
--- /dev/null
+++ b/groovy/src/test/groovy/xml/GpathSyntaxTestSupport.groovy
@@ -0,0 +1,230 @@
+package groovy.xml
+
+class GpathSyntaxTestSupport {
+    private static def sampleXml = '''
+<characters>
+    <character id="1" name="Wallace">
+    	<likes>cheese</likes>
+    </character>
+    <character id="2" name="Gromit">
+	    <likes>sleep</likes>
+    </character>
+    <numericValue>1</numericValue>
+    <booleanValue>y</booleanValue>
+    <uriValue>http://example.org/</uriValue>
+    <urlValue>http://example.org/</urlValue>
+    <empty/>
+</characters>
+'''
+
+    private static def nestedXml = '''
+<root>
+    <a><z/><z/><y/></a>
+    <b><z/></b>
+    <c><x/></c>
+    <d></d>
+</root>
+'''
+
+    static void checkElement(Closure getRoot) {
+        def root = getRoot(sampleXml)
+        assert root != null
+        def characters = root.character
+        assert 2 == characters.size()
+        assert 2 == root.'character'.size()
+        assert 2 == root['character'].size()
+        def wallace = characters[0]
+        assert wallace.name() == 'character'
+        def likes = characters.likes
+        assert 2 == likes.size()
+        def wallaceLikes = likes[0]
+        assert wallaceLikes.name() == 'likes'
+        assert wallaceLikes.text() == 'cheese'
+        checkEmptyMissingCases(root)
+        if (isDom(root)) {
+            // additional DOM long-hand syntax
+            // for illustrative purposes only
+            assert likes.item(0).nodeName == 'likes'
+            assert wallaceLikes.firstChild.nodeValue == 'cheese'
+            if (wallaceLikes.class.name.contains('xerces')) {
+                assert 'cheese' == wallaceLikes.textContent
+            }
+        }
+    }
+
+    private static void checkEmptyMissingCases(root) {
+        def unknownChild = root.xxx
+        assert unknownChild.isEmpty()
+        def unknownAttr = root.'@xxx'
+        assert isSlurper(root) || !unknownAttr
+        assert !isSlurper(root) || unknownAttr.isEmpty()
+        assert root.'empty'.text() == ''
+    }
+
+    static void checkFindElement(Closure getRoot) {
+        def root = getRoot(sampleXml)
+        // lets find Gromit
+        def gromit = root.character.find { it.'@id' == '2' }
+        assert gromit != null, "Should have found Gromit!"
+        assert gromit['@name'] == "Gromit"
+        // lets find what Wallace likes in 1 query
+        def answer = root.character.find { it['@id'] == '1' }.likes[0].text()
+        assert answer == "cheese"
+    }
+
+    static void checkNestedSizeExpressions(Closure getRoot) {
+        def root = getRoot(nestedXml)
+        assert root.'*'.size() == 4, "Expected size 4 but was ${root.'*'.size()}"
+        assert root.'a'.'z'.size() == 2
+        assert root.'*'.'*'.size() == 5
+        assert root.a.'*'.size() == 3
+        assert root.'*'.z.size() == 3
+        assert root.'*'.'*'.collect{it.name()} == ["z", "z", "y", "z", "x"]
+        assert root.a.'*'.collect{it.name()} == ["z", "z", "y"]
+        assert root.'*'.findAll{ it.z.size() > 0 }.collect{it.name()} == ["a", "b"]
+    }
+
+    static void checkElementTypes(Closure getRoot) {
+        def root = getRoot(sampleXml)
+        def numericValue = root.numericValue[0]
+        def booleanValue = root.booleanValue[0]
+        def uriValue     = root.uriValue[0]
+        def urlValue     = root.urlValue[0]
+        assert numericValue.text().toInteger() == 1
+        assert numericValue.text().toLong() == 1
+        assert numericValue.text().toFloat() == 1
+        assert numericValue.text().toDouble() == 1
+        assert numericValue.text().toBigInteger() == 1
+        assert numericValue.text().toBigDecimal() == 1
+        assert booleanValue.text().toBoolean() == true
+        assert uriValue.text().toURI() == "http://example.org/".toURI()
+        assert urlValue.text().toURL() == "http://example.org/".toURL()
+        if (isSlurper(root)) {
+            // slurper shorthand - are these really pulling their weight?
+            assert numericValue.toInteger() == 1
+            assert numericValue.toLong() == 1
+            assert numericValue.toFloat() == 1
+            assert numericValue.toDouble() == 1
+            assert numericValue.toBigInteger() == 1
+            assert numericValue.toBigDecimal() == 1
+            assert booleanValue.toBoolean() == true
+            assert uriValue.toURI() == "http://example.org/".toURI()
+            assert urlValue.toURL() == "http://example.org/".toURL()
+        }
+    }
+
+    static void checkElementClosureInteraction(Closure getRoot) {
+        def root = getRoot(sampleXml)
+        def sLikes = root.character.likes.findAll{ it.text().startsWith('s') }
+        assert sLikes.size() == 1
+        assert root.likes.size() == 0
+        if (isDom(root)) {
+            // addtitional DOMCategory long-hand notation gets nested nodes from root
+            assert root.getElementsByTagName('likes').size() == 2
+        }
+        assert 'sleep' == sLikes[0].text()
+        assert 'cheesesleep' == root.character.likes.collect{ it.text() }.join()
+        assert root.character.likes.every{ it.text().contains('ee') }
+        def groupLikesByFirstLetter
+        def likes = root.character.likes.collect{ it }
+        if (isSlurper(root)) {
+            groupLikesByFirstLetter = likes.groupBy{ like ->
+                root.character.find{ it.likes[0].text() == like.text() }.@name.toString()[0]
+            }
+            // TODO: Broken? Why doesn't below work?
+            //groupLikesByFirstLetter = likes.groupBy{ it.parent().@name.toString()[0] }
+        } else {
+            groupLikesByFirstLetter = likes.groupBy{ it.parent().'@name'[0] }
+        }
+        groupLikesByFirstLetter.keySet().each{
+            groupLikesByFirstLetter[it] = groupLikesByFirstLetter[it][0].text()
+        }
+        assert groupLikesByFirstLetter == [W:'cheese', G:'sleep']
+    }
+
+    static void checkAttribute(Closure getRoot) {
+        def root = getRoot(sampleXml)
+        if (isSlurper(root)) {
+            assert 'Wallace' == root.character[0].'@name'.text()
+            assert 'Wallace' == root.character[0]['@name'].text()
+            assert 'Wallace' == (root.character.'@name')[0].text()
+            assert ['Wallace', 'Gromit'] == root.character.'@name'.list()*.text()
+            assert 'WallaceGromit' == root.character.'@name'.text()
+        } else {
+            assert 'Wallace' == root.character[0].'@name'
+            assert 'Wallace' == root.character[0]['@name']
+            assert 'Wallace' == (root.character.'@name')[0]
+            assert ['Wallace', 'Gromit'] == root.character.collect{ it.'@name' }
+            assert 'WallaceGromit' == root.character.'@name'.join()
+        }
+        if (isSlurper(root)) {
+            // additional slurper shorthand
+            assert 'Wallace' == root.character[0].@name.text()
+            def gromit = root.character.find{ it.@id == '2' }
+            assert gromit.@name.name() == "name"
+        }
+        if (isParser(root)) {
+            // additional parser shorthand
+            assert 'Wallace' == root.character[0].@name
+            def gromit = root.character.find {it.@id == '2'}
+            def actualName = gromit.@name
+            assert actualName == 'Gromit'
+        }
+    }
+
+    static void checkAttributes(Closure getRoot) {
+        def root = getRoot(sampleXml)
+        def attributes = root.character[0].attributes()
+        assert         2 == attributes.size()
+        assert 'Wallace' == attributes['name']
+        assert 'Wallace' == attributes.name
+        assert       '1' == attributes.'id'
+    }
+
+    static void checkChildren(Closure getRoot) {
+        def root = getRoot(sampleXml)
+        def children = root.children()
+        // count direct children
+        assert children.size() == 7, "Children ${children.size()}"
+        assert root.'*'.size() == 7
+        // illustrative purposes only
+        if (isDom(root)) {
+            // count whitespace and nested children
+            assert root.childNodes.size() == 15
+            // count nested children
+            assert root.getElementsByTagName('*').size() == 9
+        }
+    }
+
+    static void checkParent(Closure getRoot) {
+        def root = getRoot(sampleXml)
+        def gromit = root.character.find { it['@id'] == '2' }
+        assert gromit.likes[0].parent() == gromit
+        assert gromit.likes[0].'..' == gromit
+        assert gromit.likes[0].parent().parent() == root
+        assert gromit.parent() == root
+        if (isSlurper(root)) {
+            // additional slurper shorthand
+            assert gromit.likes.parent() == gromit
+        }
+        if (isSlurper(root)) {
+            assert root.parent() == root
+        } else if (isParser(root)) {
+            assert root.parent() == null
+        } else if (isDom(root)) {
+            assert (root.parent() instanceof org.w3c.dom.Document)
+        }
+    }
+
+    private static boolean isSlurper(node) {
+        return node.getClass().name.contains('slurper')
+    }
+
+    private static boolean isParser(node) {
+        return (node instanceof groovy.util.Node)
+    }
+
+    private static boolean isDom(node) {
+        return node.getClass().name.contains('Element')
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/MarkupBuilderTest.groovy b/groovy/src/test/groovy/xml/MarkupBuilderTest.groovy
new file mode 100644
index 0000000..c6c0501
--- /dev/null
+++ b/groovy/src/test/groovy/xml/MarkupBuilderTest.groovy
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml
+
+import org.custommonkey.xmlunit.*
+
+/**
+ * Tests that special XML chars are entitized by MarkupBuilder.
+ *
+ * @version $Revision: 1.4 $
+ *
+ *   @author Scott Stirling
+ *   @author Pilho Kim
+ *   @author Paul King
+ */
+class MarkupBuilderTest extends GroovyTestCase {
+    private StringWriter writer
+    private MarkupBuilder xml
+
+    protected void setUp() {
+        writer = new StringWriter()
+        xml = new MarkupBuilder(writer)
+        XMLUnit.setIgnoreWhitespace(true)
+    }
+
+    private assertExpectedXml(expectedXml) {
+        def xmlDiff = new Diff(expectedXml, writer.toString())
+        assert xmlDiff.similar(), xmlDiff.toString()
+    }
+
+    void testSmallTree() {
+        xml.root1(a:5, b:7) {
+            elem1('hello1')
+            elem2('hello2')
+            elem3(x:7)
+        }
+        assertExpectedXml('''\
+<root1 a='5' b='7'>
+  <elem1>hello1</elem1>
+  <elem2>hello2</elem2>
+  <elem3 x='7' />
+</root1>''')
+    }
+
+    // It is not recommended practice to use the value attribute
+    // when also using nested content as there is no way to specify
+    // the ordering of such mixed content. The default behaviour is
+    // to include the value as the first node in the resulting xml.
+    void testSmallTreeWithTextAndAttributes() {
+        xml.root1('hello1', a:5, b:7) {
+            elem1('hello2', c:4) {
+                elem2('hello3', d:4)
+            }
+            elem1('hello2', c:4) {
+                elem2('hello3')
+                elem2('hello3', d:4)
+            }
+            elem1('hello2', c:4) {
+                elem2('hello3', d:4)
+                elem2('hello3')
+            }
+            elem1('hello2', c:4) {
+                elem2(d:4)
+                elem2('hello3', d:4)
+            }
+            elem1('hello2', c:4) {
+                elem2('hello3', d:4)
+                elem2(d:4)
+            }
+            elem1('hello2') {
+                elem2('hello3', d:4)
+                elem2(d:4)
+            }
+        }
+        assertExpectedXml('''\
+<root1 a='5' b='7'>hello1<elem1 c='4'>hello2<elem2 d='4'>hello3</elem2>
+</elem1>
+<elem1 c='4'>hello2<elem2>hello3</elem2>
+<elem2 d='4'>hello3</elem2>
+</elem1>
+<elem1 c='4'>hello2<elem2 d='4'>hello3</elem2>
+<elem2>hello3</elem2>
+</elem1>
+<elem1 c='4'>hello2<elem2 d='4' />
+<elem2 d='4'>hello3</elem2>
+</elem1>
+<elem1 c='4'>hello2<elem2 d='4'>hello3</elem2>
+<elem2 d='4' />
+</elem1>
+<elem1>hello2<elem2 d='4'>hello3</elem2>
+<elem2 d='4' />
+</elem1>
+</root1>''')
+    }
+
+    void testTree() {
+        xml.root2(a:5, b:7) {
+            elem1('hello1')
+            elem2('hello2')
+            nestedElem(x:'abc', y:'def') {
+                child(z:'def', nulltest:null)
+                child2()
+            }
+            nestedElem2(z:'zzz') {
+                child(z:'def')
+                child2("hello")
+            }
+        }
+        assertExpectedXml('''\
+<root2 a='5' b='7'>
+  <elem1>hello1</elem1>
+  <elem2>hello2</elem2>
+  <nestedElem x='abc' y='def'>
+    <child z='def' />
+    <child2 />
+  </nestedElem>
+  <nestedElem2 z='zzz'>
+    <child z='def' />
+    <child2>hello</child2>
+  </nestedElem2>
+</root2>''')
+    }
+
+    void testContentAndDataInMarkup() {
+        xml.a(href:"http://groovy.codehaus.org", "groovy")
+        assertExpectedXml("<a href='http://groovy.codehaus.org'>groovy</a>")
+    }
+
+    void testMarkupWithColonsAndNamespaces() {
+        def expectedXml = '''\
+<ns1:customer-description>
+  <last-name>Laforge</last-name>
+  <first-name>
+    <first>Guillaume</first>
+    <initial-letters>A.J.</initial-letters>
+  </first-name>
+</ns1:customer-description>'''
+        xml."ns1:customer-description"{
+            "last-name"("Laforge")
+            "first-name"{
+                first("Guillaume")
+                "initial-letters"("A.J.")
+            }
+        }
+        assertEquals(expectedXml, fixEOLs(writer.toString()))
+    }
+
+    /**
+     * Main test method. Checks that well-formed XML is generated
+     * and that the appropriate characters are escaped with the
+     * correct entities.
+     */
+    void testBuilder() {
+        String expectedXml = '''\
+<chars>
+  <ampersand a='&amp;'>&amp;</ampersand>
+  <quote attr='"'>"</quote>
+  <apostrophe attr='&apos;'>'</apostrophe>
+  <lessthan attr='value'>chars: &amp; &lt; &gt; '</lessthan>
+  <element attr='value 1 &amp; 2'>chars: &amp; &lt; &gt; " in middle</element>
+  <greaterthan>&gt;</greaterthan>
+  <emptyElement />
+</chars>'''
+
+        // Generate the markup.
+        xml.chars {
+            ampersand(a: "&", "&")
+            quote(attr: "\"", "\"")
+            apostrophe(attr: "'", "'")
+            lessthan(attr: "value", "chars: & < > '")
+            element(attr: "value 1 & 2", "chars: & < > \" in middle")
+            greaterthan(">")
+            emptyElement()
+        }
+
+        assertEquals(expectedXml, fixEOLs(writer.toString()))
+    }
+
+    /**
+     * Tests the builder with double quotes for attribute values.
+     */
+    void testBuilderWithDoubleQuotes() {
+        String expectedXml = '''\
+<chars>
+  <ampersand a="&amp;">&amp;</ampersand>
+  <quote attr="&quot;">"</quote>
+  <apostrophe attr="'">'</apostrophe>
+  <lessthan attr="value">chars: &amp; &lt; &gt; '</lessthan>
+  <element attr="value 1 &amp; 2">chars: &amp; &lt; &gt; " in middle</element>
+  <greaterthan>&gt;</greaterthan>
+  <emptyElement />
+</chars>'''
+
+        // Generate the markup.
+        xml.doubleQuotes = true
+        xml.chars {
+            ampersand(a: "&", "&")
+            quote(attr: "\"", "\"")
+            apostrophe(attr: "'", "'")
+            lessthan(attr: "value", "chars: & < > '")
+            element(attr: "value 1 & 2", "chars: & < > \" in middle")
+            greaterthan(">")
+            emptyElement()
+        }
+
+        assertEquals(expectedXml, fixEOLs(writer.toString()))
+    }
+
+    /**
+     * Tests that MarkupBuilder escapes element content correctly, even
+     * when the content contains line-endings.
+     */
+    void testEscapingMultiLineContent() {
+
+        // Generate the markup.
+        xml.element('''This is multi-line content with characters, such as <, that
+require escaping. The other characters consist of:
+
+    * > - greater than
+    * & - ampersand
+''')
+
+        assertExpectedXml('''\
+<element>This is multi-line content with characters, such as &lt;, that
+require escaping. The other characters consist of:
+
+    * &gt; - greater than
+    * &amp; - ampersand
+</element>''')
+    }
+
+    /**
+     * Checks against a regression bug whereby some empty elements were
+     * not closed.
+     */
+    void testMarkupForClosingTags() {
+
+        // Generate the XML.
+        def list = ['first', 'second', 'third']
+
+        xml.ELEM1() {
+            list.each(){ r ->
+                xml.ELEM2(id:r, type:'2') {
+                    xml.ELEM3A(id:r)
+                    xml.ELEM3B(type:'3', 'text')
+                }
+            }
+        }
+
+        assertExpectedXml('''\
+<ELEM1>
+  <ELEM2 type='2' id='first'>
+    <ELEM3A id='first' />
+    <ELEM3B type='3'>text</ELEM3B>
+  </ELEM2>
+  <ELEM2 type='2' id='second'>
+    <ELEM3A id='second' />
+    <ELEM3B type='3'>text</ELEM3B>
+  </ELEM2>
+  <ELEM2 type='2' id='third'>
+    <ELEM3A id='third' />
+    <ELEM3B type='3'>text</ELEM3B>
+  </ELEM2>
+</ELEM1>''')
+    }
+
+    void testMixedMarkup() {
+        xml.p {
+            em('Usually')
+            mkp.yield ' Hearts & Diamonds '
+            b('beats')
+            mkp.yieldUnescaped ' Spades &amp; Clubs'
+        }
+
+        assertExpectedXml('''\
+<p><em>Usually</em> Hearts &amp; Diamonds <b>beats</b> Spades &amp; Clubs </p>''')
+    }
+    
+    void testCallingMethod() {
+       // this test is to ensure compatiblity only
+       xml.p { 
+         def aValue = myMethod([:]).value
+         em(aValue)
+      }
+      
+      assertExpectedXml('<p><em>call to outside</em></p>')
+    }
+    
+    private myMethod(x) {
+      x.value='call to outside'
+      return x
+    }
+
+}
diff --git a/groovy/src/test/groovy/xml/MarkupWithWriterTest.groovy b/groovy/src/test/groovy/xml/MarkupWithWriterTest.groovy
new file mode 100644
index 0000000..190d39f
--- /dev/null
+++ b/groovy/src/test/groovy/xml/MarkupWithWriterTest.groovy
@@ -0,0 +1,28 @@
+package groovy.xml
+
+/**
+ * This test uses GroovyMarkup with writers other than System.out
+ */
+class MarkupWithWriterTest extends TestXmlSupport {
+
+    void testSmallTreeWithStringWriter() {
+        def writer = new java.io.StringWriter()
+        def b = new MarkupBuilder(writer)
+
+        b.root1(a:5, b:7) {
+            elem1('hello1')
+            elem2('hello2')
+            elem3(x:7)
+        }
+        println writer.toString()
+//        assertEquals "<root1 a='5' b='7'>\n" +
+//                "  <elem1>hello1</elem1>\n" +
+//                "  <elem2>hello2</elem2>\n" +
+//                "  <elem3 x='7' />\n" +
+//                "</root1>", writer.toString()
+    }
+
+    void testWriterUseInScriptFile() {
+        assertScriptFile 'src/test/groovy/xml/UseMarkupWithWriterScript.groovy'
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/MixedMarkupTestSupport.groovy b/groovy/src/test/groovy/xml/MixedMarkupTestSupport.groovy
new file mode 100644
index 0000000..de700a3
--- /dev/null
+++ b/groovy/src/test/groovy/xml/MixedMarkupTestSupport.groovy
@@ -0,0 +1,37 @@
+package groovy.xml
+
+class MixedMarkupTestSupport {
+
+    private static def mixedXml = '''
+<p>Please read the <a href="index.html">Home</a> page</p>
+'''
+    static void checkMixedMarkup(Closure getRoot) {
+        def root = getRoot(mixedXml)
+        assert root != null
+        def children = root.children()
+        if (isSlurper(root)) {
+            assert children.size() == 1
+            assert children[0].name() == 'a'
+        } else {
+            assert children.size() == 3
+            assert children[1].name() == 'a'
+            if (isParser(root)) {
+                assert children[2] == 'page'
+            } else {
+                assert children[2].text() == 'page'
+            }
+        }
+    }
+
+    private static boolean isSlurper(node) {
+        return node.getClass().name.contains('slurper')
+    }
+
+    private static boolean isParser(node) {
+        return (node instanceof groovy.util.Node)
+    }
+
+    private static boolean isDom(node) {
+        return node.getClass().name.contains('Element')
+    }
+}
diff --git a/groovy/src/test/groovy/xml/NamespaceDOMTest.groovy b/groovy/src/test/groovy/xml/NamespaceDOMTest.groovy
new file mode 100644
index 0000000..e30f2f5
--- /dev/null
+++ b/groovy/src/test/groovy/xml/NamespaceDOMTest.groovy
@@ -0,0 +1,73 @@
+package groovy.xml
+
+/**
+ * Test the building of namespaced XML using GroovyMarkup
+ */
+class NamespaceDOMTest extends TestXmlSupport {
+    
+    void testTree() {
+        def builder = DOMBuilder.newInstance()
+        def xmlns = new NamespaceBuilder(builder)
+        
+        def xsd = xmlns.namespace('http://www.w3.org/2001/XMLSchema', 'xsd')
+        
+        def root = xsd.schema(xmlns:['foo':'http://someOtherNamespace']) {
+          annotation {
+              documentation("Purchase order schema for Example.com.")
+              //documentation(xmlns=[xml.lang:'en']) ["Purchase order schema for Example.com."]
+          }
+          element(name:'purchaseOrder', type:'PurchaseOrderType')
+          element(name:'comment', type:'xsd:string')
+          complexType(name:'PurchaseOrderType') {
+            sequence {
+              element(name:'shipTo', type:'USAddress')
+              element(name:'billTo', type:'USAddress')
+              element(minOccurs:'0', ref:'comment')
+              element(name:'items', type:'Items')
+            }
+            attribute(name:'orderDate', type:'xsd:date')
+          }
+          complexType(name:'USAddress') {
+            sequence {
+              element(name:'name', type:'xsd:string')
+              element(name:'street', type:'xsd:string')
+              element(name:'city', type:'xsd:string')
+              element(name:'state', type:'xsd:string')
+              element(name:'zip', type:'xsd:decimal')
+            }
+            attribute(fixed:'US', name:'country', type:'xsd:NMTOKEN')
+          }
+          complexType(name:'Items') {
+            sequence {
+              element(maxOccurs:'unbounded', minOccurs:'0', name:'item') {
+                complexType {
+                  sequence {
+                    element(name:'productName', type:'xsd:string')
+                    element(name:'quantity') {
+                      simpleType {
+                        restriction(base:'xsd:positiveInteger') {
+                          maxExclusive(value:'100')
+                        }
+                      }
+                    }
+                    element(name:'USPrice', type:'xsd:decimal')
+                    element(minOccurs:'0', ref:'comment')
+                    element(minOccurs:'0', name:'shipDate', type:'xsd:date')
+                  }
+                  attribute(name:'partNum', type:'SKU', use:'required')
+                }
+              }
+            }
+          }
+          /* Stock Keeping Unit, a code for identifying products */
+          simpleType(name:'SKU') {
+            restriction(base:'xsd:string') {
+              pattern(value:'\\d{3}-[A-Z]{2}')
+            }
+          }
+        }        
+        assert root != null
+        
+        dump(root)
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/NamespaceNodeGPathTest.groovy b/groovy/src/test/groovy/xml/NamespaceNodeGPathTest.groovy
new file mode 100644
index 0000000..400d918
--- /dev/null
+++ b/groovy/src/test/groovy/xml/NamespaceNodeGPathTest.groovy
@@ -0,0 +1,52 @@
+package groovy.xml
+
+/**
+ * Test the use of GPath navigation with namespaces
+ */
+class NamespaceNodeGPathTest extends TestXmlSupport {
+
+    void testTree() {
+        Node root = new XmlParser().parseText("""
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <xsd:annotation xsd:cheese="Edam">
+     <xsd:documentation>Purchase order schema for Example.com.</xsd:documentation>
+  </xsd:annotation>
+</xsd:schema>
+""")
+
+        Namespace xsd = new Namespace('http://www.w3.org/2001/XMLSchema', 'xsd')
+
+        def children = root.children()
+
+        def name = root.name()
+
+        root.children().each {println "has a child with name ${it.name()} and content $it"}
+
+        def foo = xsd.annotation
+        println "qname url is $foo.namespaceURI"
+        println "qname prefix is $foo.prefix"
+        println "qname localPart is $foo.localPart"
+
+        def a = root[xsd.annotation]
+        println "Found results $a"
+
+        assert a.size() == 1: " size is $a.size()"
+
+        def aNode = a[0]
+        def cheese = aNode.attributes()[xsd.cheese]
+        assert cheese == "Edam"
+        println "Found namespaced attribute $cheese"
+
+        cheese = aNode.attribute(xsd.cheese)
+        assert cheese == "Edam"
+        println "Found namespaced attribute $cheese"
+
+        def e = root[xsd.annotation][xsd.documentation]
+        assert e.size() == 1: " size is $e.size()"
+        assert e.text() == "Purchase order schema for Example.com."
+
+        e = a[xsd.documentation]
+        assert e.size() == 1: " size is $e.size()"
+        assert e.text() == "Purchase order schema for Example.com."
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/NamespaceNodeTest.groovy b/groovy/src/test/groovy/xml/NamespaceNodeTest.groovy
new file mode 100644
index 0000000..81b25be
--- /dev/null
+++ b/groovy/src/test/groovy/xml/NamespaceNodeTest.groovy
@@ -0,0 +1,100 @@
+package groovy.xml
+
+/**
+ * Test the building of namespaced XML using GroovyMarkup
+ */
+class NamespaceNodeTest extends TestXmlSupport {
+
+    void testTree_FAILS() {if (notYetImplemented()) return
+        def builder = NodeBuilder.newInstance()
+        def xmlns = new NamespaceBuilder(builder)
+
+        def xsd = xmlns.namespace('http://www.w3.org/2001/XMLSchema', 'xsd')
+
+        def root = xsd.schema(xmlns: ['foo': 'http://someOtherNamespace']) {
+            annotation {
+                documentation("Purchase order schema for Example.com.")
+                //documentation(xmlns=[xml.lang:'en']) ["Purchase order schema for Example.com."]
+            }
+            element(name: 'purchaseOrder', type: 'PurchaseOrderType')
+            element(name: 'comment', type: 'xsd:string')
+            complexType(name: 'PurchaseOrderType') {
+                sequence {
+                    element(name: 'shipTo', type: 'USAddress')
+                    element(name: 'billTo', type: 'USAddress')
+                    element(minOccurs: '0', ref: 'comment')
+                    element(name: 'items', type: 'Items')
+                }
+                attribute(name: 'orderDate', type: 'xsd:date')
+            }
+            complexType(name: 'USAddress') {
+                sequence {
+                    element(name: 'name', type: 'xsd:string')
+                    element(name: 'street', type: 'xsd:string')
+                    element(name: 'city', type: 'xsd:string')
+                    element(name: 'state', type: 'xsd:string')
+                    element(name: 'zip', type: 'xsd:decimal')
+                }
+                attribute(fixed: 'US', name: 'country', type: 'xsd:NMTOKEN')
+            }
+            complexType(name: 'Items') {
+                sequence {
+                    element(maxOccurs: 'unbounded', minOccurs: '0', name: 'item') {
+                        complexType {
+                            sequence {
+                                element(name: 'productName', type: 'xsd:string')
+                                element(name: 'quantity') {
+                                    simpleType {
+                                        restriction(base: 'xsd:positiveInteger') {
+                                            maxExclusive(value: '100')
+                                        }
+                                    }
+                                }
+                                element(name: 'USPrice', type: 'xsd:decimal')
+                                element(minOccurs: '0', ref: 'comment')
+                                element(minOccurs: '0', name: 'shipDate', type: 'xsd:date')
+                            }
+                            attribute(name: 'partNum', type: 'SKU', use: 'required')
+                        }
+                    }
+                }
+            }
+            /* Stock Keeping Unit, a code for identifying products */
+            simpleType(name: 'SKU') {
+                restriction(base: 'xsd:string') {
+                    pattern(value: '\\d{3}-[A-Z]{2}')
+                }
+            }
+        }
+        assert root != null
+
+        assertGPaths(root)
+    }
+
+    void assertGPaths(Node root) {
+        Namespace xsd = new Namespace('http://www.w3.org/2001/XMLSchema', 'xsd')
+
+        def children = root.children()
+        println "has children $children"
+
+        def name = root.name()
+        println "name is of type ${name.getClass()} with value $name"
+
+        root.children().each {println "has a child with name ${it.name()} and content $it"}
+
+        def foo = xsd.annotation
+        println "qname is $foo"
+        println "qname url is $foo.namespaceURI"
+        println "qname prefix is $foo.prefix"
+        println "qname localPart is $foo.localPart"
+
+        def a = root[xsd.annotation]
+        println "Found results $a"
+
+        def e = root[xsd.annotation][xsd.documentation]
+
+        String text = e.text()
+        println "Found element: $e with text: $text"
+        assert text == "Purchase order schema for Example.com."
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/SAXTest.groovy b/groovy/src/test/groovy/xml/SAXTest.groovy
new file mode 100644
index 0000000..3bb642a
--- /dev/null
+++ b/groovy/src/test/groovy/xml/SAXTest.groovy
@@ -0,0 +1,36 @@
+package groovy.xml
+
+/**
+ * This test uses the concise syntax to test the generation
+ * of SAX events using GroovyMarkup
+ */
+class SAXTest extends TestXmlSupport {
+    
+    void testSmallTree() {
+        def b = createSAXBuilder()
+        
+        def root = b.root1(a:5, b:7) {
+            elem1('hello1')
+            elem2('hello2')
+            elem3(x:7)
+        }
+    }
+    
+    void testTree() {
+        def b = createSAXBuilder()
+        
+        def root = b.root2(a:5, b:7) {
+            elem1('hello1')
+            elem2('hello2')
+            nestedElem(x:'abc', y:'def') {
+                child(z:'def')
+                child2()  
+            }
+            
+            nestedElem2(z:'zzz') {
+                child(z:'def')
+                child2("hello")  
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/SmallNamespaceDOMTest.groovy b/groovy/src/test/groovy/xml/SmallNamespaceDOMTest.groovy
new file mode 100644
index 0000000..09d7555
--- /dev/null
+++ b/groovy/src/test/groovy/xml/SmallNamespaceDOMTest.groovy
@@ -0,0 +1,26 @@
+package groovy.xml
+
+class SmallNamespaceDOMTest extends TestXmlSupport {
+    
+    void testTree() {
+        def builder = DOMBuilder.newInstance()
+        def xsd = NamespaceBuilder.newInstance(builder, 'http://www.w3.org/2001/XMLSchema', 'xsd')
+        
+        def root = xsd.schema() {
+          element(name:'purchaseOrder', type:'PurchaseOrderType')
+          element(name:'comment', type:'xsd:string')
+          complexType(name:'PurchaseOrderType') {
+            sequence {
+              element(name:'shipTo', type:'USAddress')
+              element(name:'billTo', type:'USAddress')
+              element(minOccurs:'0', ref:'comment')
+              element(name:'items', type:'Items')
+            }
+            attribute(name:'orderDate', type:'xsd:date')
+          }
+        }        
+        assert root != null
+        
+        dump(root)
+    }
+}
diff --git a/groovy/src/test/groovy/xml/StreamingMarkupBuilderTest.groovy b/groovy/src/test/groovy/xml/StreamingMarkupBuilderTest.groovy
new file mode 100644
index 0000000..29cc14a
--- /dev/null
+++ b/groovy/src/test/groovy/xml/StreamingMarkupBuilderTest.groovy
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package groovy.xml
+
+import org.custommonkey.xmlunit.*
+
+/**
+ * This test uses the concise syntax to test the building of 
+ * textual markup (XML or HTML) using GroovyMarkup
+ */
+class StreamingMarkupBuilderTest extends TestXmlSupport {
+    
+    void testSmallTree() {
+        def b = new StreamingMarkupBuilder()
+        def writer = new StringWriter()
+        
+        def m = {
+            mkp.pi("xml-stylesheet":[href:"mystyle.css", type:"text/css"])
+            root1(a:5, b:7) {
+                elem1('hello1')
+                elem2('hello2')
+                elem3(x:7)
+            }
+        }
+
+        def expectedXml = '''\
+<?xml-stylesheet href="mystyle.css" type="text/css"?>
+<root1 a='5' b='7'>
+  <elem1>hello1</elem1>
+  <elem2>hello2</elem2>
+  <elem3 x='7'/>
+</root1>'''
+        writer << b.bind(m)
+        XMLUnit.ignoreWhitespace = true
+        def xmlDiff = new Diff(expectedXml, writer.toString())
+        assert xmlDiff.similar(), xmlDiff.toString()
+    }
+
+    void testTree() {
+        def b = new StreamingMarkupBuilder()
+        def writer = new StringWriter()
+
+        def m = {
+            root2(a:5, b:7) {
+                elem1('hello1')
+                elem2('hello2')
+                nestedElem(x:'abc', y:'def') {
+                    child(z:'def')
+                    child2()
+                }
+
+                nestedElem2(z:'zzz') {
+                    child(z:'def')
+                    child2("hello")
+                }
+            }
+        }
+
+        def expectedXml = '''\
+<root2 a='5' b='7'>
+  <elem1>hello1</elem1>
+  <elem2>hello2</elem2>
+  <nestedElem x='abc' y='def'>
+    <child z='def'/>
+    <child2/>
+  </nestedElem>
+  <nestedElem2 z='zzz'>
+    <child z='def'/>
+    <child2>hello</child2>
+  </nestedElem2>
+</root2>'''
+
+        writer << b.bind(m)
+        XMLUnit.ignoreWhitespace = true
+        def xmlDiff = new Diff(expectedXml, writer.toString())
+        assert xmlDiff.similar(), xmlDiff.toString()
+    }
+
+    void testObjectOperationsInMarkup() {
+        def doc = new StreamingMarkupBuilder().bind {
+            root {
+                (1..3).each {
+                    item()
+                }
+            }
+        }
+        assert doc.toString() == "<root><item/><item/><item/></root>"
+    }
+
+    void testMixedMarkup() {
+        def b = new StreamingMarkupBuilder()
+        def writer = new StringWriter()
+
+        def m = {
+// uncomment if you want entities like &nbsp; (and add to expected too)
+//            mkp.yieldUnescaped '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+//                                  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'''
+            html {
+                body {
+                    p {
+                        mkp.yield 'The '
+                        i('quick')
+                        mkp.yieldUnescaped ' brown fox jumped over the <b>lazy</b> dog &amp; sleepy cat'
+                    }
+                }
+            }
+        }
+
+        def expectedXml = '''\
+<html>
+  <body>
+    <p>The <i>quick</i> brown fox jumped over the <b>lazy</b> dog &amp; sleepy cat</p>
+  </body>
+</html>'''
+
+        writer << b.bind(m)
+        XMLUnit.ignoreWhitespace = true
+        def xmlDiff = new Diff(expectedXml, writer.toString())
+        assert xmlDiff.similar(), xmlDiff.toString()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/TestXmlSupport.java b/groovy/src/test/groovy/xml/TestXmlSupport.java
new file mode 100644
index 0000000..aeb1454
--- /dev/null
+++ b/groovy/src/test/groovy/xml/TestXmlSupport.java
@@ -0,0 +1,84 @@
+/*
+$Id$
+
+Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+Redistribution and use of this software and associated documentation
+("Software"), with or without modification, are permitted provided
+that the following conditions are met:
+
+1. Redistributions of source code must retain copyright
+statements and notices.  Redistributions must also contain a
+copy of this document.
+
+2. Redistributions in binary form must reproduce the
+above copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+3. The name "groovy" must not be used to endorse or promote
+products derived from this Software without prior written
+permission of The Codehaus.  For written permission,
+please contact info@codehaus.org.
+
+4. Products derived from this Software may not be called "groovy"
+nor may "groovy" appear in their names without prior written
+permission of The Codehaus. "groovy" is a registered
+trademark of The Codehaus.
+
+5. Due credit should be given to The Codehaus -
+http://groovy.codehaus.org/
+
+THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+package groovy.xml;
+
+import groovy.xml.dom.DOMUtil;
+import org.codehaus.groovy.classgen.TestSupport;
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class TestXmlSupport extends TestSupport {
+
+    protected void dump(Node node) throws IOException {
+        DOMUtil.serialize((Element) node, System.out);
+    }
+
+    protected SAXBuilder createSAXBuilder() throws IOException {
+        return new SAXBuilder(new LoggingDefaultHandler());
+    }
+
+    private static class LoggingDefaultHandler extends DefaultHandler {
+        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
+            System.out.println("Start Element: " + localName);
+        }
+        public void endElement(String uri, String localName, String qName) throws SAXException {
+            System.out.println("End Element: " + localName);
+        }
+        public void characters(char ch[], int start, int length) throws SAXException {
+            System.out.println("Characters: " + new String(ch).substring(start, start + length - 1));
+        }
+    }
+}
diff --git a/groovy/src/test/groovy/xml/TraversalTestSupport.groovy b/groovy/src/test/groovy/xml/TraversalTestSupport.groovy
new file mode 100644
index 0000000..81fac56
--- /dev/null
+++ b/groovy/src/test/groovy/xml/TraversalTestSupport.groovy
@@ -0,0 +1,35 @@
+package groovy.xml
+
+class TraversalTestSupport {
+    private static def nestedXml = '''
+    <_1>
+        <_1_1>
+            <_1_1_1/>
+            <_1_1_2>
+                <_1_1_2_1/>
+            </_1_1_2>
+        </_1_1>
+        <_1_2>
+            <_1_2_1/>
+        </_1_2>
+    </_1>
+    '''
+
+    static void checkDepthFirst(Closure getRoot) {
+        def root = getRoot(nestedXml)
+        def trace = ''
+        root.depthFirst().each{ trace += it.name() + ' ' }
+        assert trace == '_1 _1_1 _1_1_1 _1_1_2 _1_1_2_1 _1_2 _1_2_1 '
+        // test shorthand
+        trace = ''
+        root.'_1_2'.'**'.each{ trace += it.name() + ' ' }
+        assert trace == '_1_2 _1_2_1 '
+    }
+
+    static void checkBreadthFirst(Closure getRoot) {
+        def root = getRoot(nestedXml)
+        def trace = ''
+        root.breadthFirst().each{ trace += it.name() + ' ' }
+        assert trace == '_1 _1_1 _1_2 _1_1_1 _1_1_2 _1_2_1 _1_1_2_1 '
+    }
+}
diff --git a/groovy/src/test/groovy/xml/UseMarkupWithWriterScript.groovy b/groovy/src/test/groovy/xml/UseMarkupWithWriterScript.groovy
new file mode 100644
index 0000000..5d01a94
--- /dev/null
+++ b/groovy/src/test/groovy/xml/UseMarkupWithWriterScript.groovy
@@ -0,0 +1,8 @@
+// used by MarkupWithWriterTest.testWriterUseInScriptFile
+
+writer = new java.io.StringWriter()
+b = new groovy.xml.MarkupBuilder(writer)
+
+b.root1(a:5)
+println writer.toString()
+assert "<root1 a='5' />" == writer.toString()
\ No newline at end of file
diff --git a/groovy/src/test/groovy/xml/VerboseDOMTest.groovy b/groovy/src/test/groovy/xml/VerboseDOMTest.groovy
new file mode 100644
index 0000000..83b99b9
--- /dev/null
+++ b/groovy/src/test/groovy/xml/VerboseDOMTest.groovy
@@ -0,0 +1,81 @@
+package groovy.xml
+
+/**
+ * This test uses the verbose syntax to test the building of 
+ * W3C DOM trees using GroovyMarkup
+ */
+class VerboseDOMTest extends TestXmlSupport {
+    
+    void testSmallTree() {
+        def b = DOMBuilder.newInstance()
+        
+        def root = b.root1(['a':5, 'b':7], {->
+            elem1('hello1')
+            elem2('hello2')
+            elem3(['x':7])
+        })
+        
+        assert root != null
+        
+        dump(root)
+    }
+    
+    void testTree() {
+        def b = DOMBuilder.newInstance()
+        
+        def root = b.root2(['a':5, 'b':7], {
+            elem1('hello1')
+            elem2('hello2')
+            nestedElem(['x':'abc', 'y':'def'], {->
+                child(['z':'def'])
+                child2()
+            })
+
+            nestedElem2(['z':'zzz'], {->
+                child(['z':'def'])
+                child2("hello")  
+            })
+        })
+        
+        assert root != null
+        
+        dump(root)
+
+/*
+		def elem1 = root.elem1
+        assert elem1.value() := 'hello1'
+        
+        def elem2 = root.elem2
+        assert elem2.value() := 'hello2'
+
+        assert root.elem1.value() := 'hello1'
+        assert root.elem2.value() := 'hello2'
+
+        assert root.nestedElem.attributes() := ['x':'abc', 'y':'def']        
+        assert root.nestedElem.child.attributes() := ['z':'def']
+        assert root.nestedElem.child2.value() := []
+        assert root.nestedElem.child2.text() := ''
+
+        assert root.nestedElem2.attributes() := ['z':'zzz']      
+        assert root.nestedElem2.child.attributes() := ['z':'def']
+        assert root.nestedElem2.child2.value() := 'hello'
+        assert root.nestedElem2.child2.text() := 'hello'
+        
+        def list = root.value()
+        assert list.size() := 4
+        
+        assert root.attributes().a := 5
+        assert root.attributes().b := 7
+
+        assert root.nestedElem.attributes().x := 'abc'
+        assert root.nestedElem.attributes().y := 'def'
+        assert root.nestedElem2.attributes().z := 'zzz'
+        assert root.nestedElem2.child.attributes().z := 'def'
+*/        
+        /** @todo parser add .@ as an operation
+                assert root.@a := 5
+                assert root.@b := 7
+        */        
+    }
+    
+}
diff --git a/groovy/src/test/groovy/xml/XmlTest.java b/groovy/src/test/groovy/xml/XmlTest.java
new file mode 100644
index 0000000..2ac1fe9
--- /dev/null
+++ b/groovy/src/test/groovy/xml/XmlTest.java
@@ -0,0 +1,78 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package groovy.xml;
+
+import groovy.lang.GroovyObject;
+import org.codehaus.groovy.classgen.TestSupport;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class XmlTest extends TestSupport {
+
+    public void testTree() throws Exception {
+        GroovyObject object = compile("src/test/groovy/xml/SmallNamespaceDOMTest.groovy");
+        object.invokeMethod("testTree", null);
+    }
+
+    public void testQName() throws Exception {
+        QName qname = new QName("urn:mynamespace", "localPart", "x");
+
+        assertTrue(qname.equals(new QName("urn:mynamespace", "localPart")));
+        assertTrue(qname.equals("urn:mynamespace:localPart"));
+        assertTrue(qname.equals("x:localPart"));
+
+        assertTrue(!qname.equals(null));
+        assertTrue(!qname.equals(""));
+        assertTrue(!qname.equals(" "));
+        assertTrue(!qname.equals("localPart"));
+        assertTrue(!qname.equals("x:"));
+        assertTrue(!qname.equals(":"));
+        assertTrue(!qname.equals(":localPart"));
+    }
+}
diff --git a/groovy/src/test/groovy/xml/dom/DOMCategoryTest.groovy b/groovy/src/test/groovy/xml/dom/DOMCategoryTest.groovy
new file mode 100644
index 0000000..f9b8067
--- /dev/null
+++ b/groovy/src/test/groovy/xml/dom/DOMCategoryTest.groovy
@@ -0,0 +1,53 @@
+package groovy.xml.dom
+
+import groovy.xml.DOMBuilder
+import groovy.xml.GpathSyntaxTestSupport
+import groovy.xml.TraversalTestSupport
+import groovy.xml.MixedMarkupTestSupport
+
+class DOMCategoryTest extends GroovyTestCase {
+
+    def getRoot = { xml ->
+        def reader = new StringReader(xml)
+        def doc    = DOMBuilder.parse(reader)
+        def root   = doc.documentElement
+    }
+
+    void testMixedMarkup() {
+        use(DOMCategory) {
+            MixedMarkupTestSupport.checkMixedMarkup(getRoot)
+        }
+    }
+
+    void testElement() {
+        use(DOMCategory) {
+            GpathSyntaxTestSupport.checkElement(getRoot)
+            GpathSyntaxTestSupport.checkFindElement(getRoot)
+            GpathSyntaxTestSupport.checkElementTypes(getRoot)
+            GpathSyntaxTestSupport.checkElementClosureInteraction(getRoot)
+        }
+    }
+
+    void testAttribute() {
+        use(DOMCategory) {
+            GpathSyntaxTestSupport.checkAttribute(getRoot)
+            GpathSyntaxTestSupport.checkAttributes(getRoot)
+        }
+    }
+
+    void testNavigation() {
+        use(DOMCategory) {
+            GpathSyntaxTestSupport.checkChildren(getRoot)
+            GpathSyntaxTestSupport.checkParent(getRoot)
+            GpathSyntaxTestSupport.checkNestedSizeExpressions(getRoot)
+        }
+    }
+
+    void testTraversal() {
+        use(DOMCategory) {
+            TraversalTestSupport.checkDepthFirst(getRoot)
+            TraversalTestSupport.checkBreadthFirst(getRoot)
+        }
+    }
+
+}
diff --git a/groovy/src/test/groovy/xml/dom/DOMTest.groovy b/groovy/src/test/groovy/xml/dom/DOMTest.groovy
new file mode 100644
index 0000000..925390b
--- /dev/null
+++ b/groovy/src/test/groovy/xml/dom/DOMTest.groovy
@@ -0,0 +1,99 @@
+package groovy.xml.dom
+
+import groovy.xml.*
+import org.codehaus.groovy.tools.xml.*
+
+class DOMTest extends GroovyTestCase {
+    def benchmark = false
+
+    void testDOMParser() {
+        def xml = new StringReader("<html><head><title class='mytitle'>Test</title></head><body><p class='mystyle'>This is a test.</p></body></html>")
+        def doc = DOMBuilder.parse(xml)
+        def html = doc.documentElement
+        if (!benchmark) assertCorrect html
+    }
+
+    void testDOMBuilder() {
+        def html = DOMBuilder.newInstance().
+        html {
+          head {
+            title (class:"mytitle", "Test")
+          }
+          body {
+            p (class:"mystyle", "This is a test.")
+          }
+        }
+        if (!benchmark) assertCorrect html
+    }
+
+    void testDOMBuilderWithNullValue() {
+        def html = DOMBuilder.newInstance().
+        html {
+          head {
+            title (testAttr:null, "Test")
+          }
+        }
+        use (DOMCategory) {
+            assert html.head.title[0].'@testAttr' == ''
+        }
+    }
+
+    void testStreamingDOMBuilder() {
+        def builder = new StreamingDOMBuilder()
+        def doc = builder.bind{
+          html {
+            head {
+              title (class:"mytitle", "Test")
+            }
+            body {
+              p (class:"mystyle", "This is a test.")
+            }
+          }
+        }
+        // TODO re-enable this test now that xerces is gone
+//        if (!benchmark) assertCorrect doc().documentElement
+    }
+
+    private def assertCorrect(html) {
+        use (DOMCategory) {
+            assert html.head.title.text() == 'Test', "Expected 'Test' but was: ${html.head.title[0].text()}"
+            assert html.body.p[0].text() == 'This is a test.'
+            assert html.find { it.tagName == 'body' }.tagName == 'body'
+            assert html.getElementsByTagName('*').findAll{ it.'@class' != '' }.size() == 2
+        }
+        // should fail outside category
+        shouldFail (MissingPropertyException) { html.head }
+    }
+
+    static void main(args) {
+        // Relative results:
+        // When:      05 May 2004  14 Oct 2006  4 Mar 2007
+        // Notes:                  Xerces 2.4   Xerces 2.8
+        // Parser:    1.0          1.0          1.0
+        // Builder:   1.05         2.90         1.20
+        // Streaming: 0.77         0.20         0.20
+        def x = args.size() == 0 ? 1000 : Integer.parseInt(args[0])
+        def mydomtest = new DOMTest()
+        def standard = 0
+        mydomtest.benchmark = true
+        [{ mydomtest.testDOMParser() },
+         { mydomtest.testDOMBuilder() },
+         { mydomtest.testStreamingDOMBuilder() }].eachWithIndex { testMethod, index ->
+            // Run the method once to fill any caches and to load classes
+            testMethod()
+            def start = System.currentTimeMillis()
+            def lastIndex
+            for (i in 1..x) {
+                testMethod()
+                lastIndex = i
+            }
+            def elapsed = System.currentTimeMillis() - start
+            def result = lastIndex * 1000 / elapsed
+
+            standard = (standard == 0 ? result : standard)
+            def factor = result/standard
+            def relative = 1/factor
+            println "${index}: ${factor}x relative=${relative} (${result} trees/s)"
+        }
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/ant/GroovyTest.java b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest.java
new file mode 100644
index 0000000..89c6be4
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest.java
@@ -0,0 +1,115 @@
+package org.codehaus.groovy.ant;
+
+import groovy.lang.GroovyRuntimeException;
+import groovy.util.GroovyTestCase;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.regex.Pattern;
+
+/**
+ * Unit tests for the {@link Groovy} ant task.
+ * Caution: the *.groovy files used by this test should not get compiled with the rest of the
+ * test classes compilation process otherwise they would be available in the classpath
+ * and the tests here would be meaningless (tested by testClasspath_missing).
+ *
+ * @author Marc Guillemot
+ */
+public class GroovyTest extends GroovyTestCase {
+    public static String FLAG = null;
+    private final File antFile = new File("src/test/org/codehaus/groovy/ant/GroovyTest.xml");
+    private Project project;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        project = new Project();
+        project.init();
+        ProjectHelper.getProjectHelper().parse(project, antFile);
+        FLAG = null;
+    }
+
+    public void testGroovyCodeWithinTag() {
+        assertNull(FLAG);
+        project.executeTarget("groovyCodeWithinTask");
+        assertEquals("from groovy inlined in ant", FLAG);
+    }
+
+    public void testGroovyCodeExternalFile() {
+        assertNull(FLAG);
+        project.executeTarget("groovyCodeInExternalFile");
+        assertEquals("from groovy file called from ant", FLAG);
+    }
+
+    public void testGroovyCodeInExternalFileWithOtherClass() {
+        assertNull(FLAG);
+        project.executeTarget("groovyCodeInExternalFileWithOtherClass");
+        assertEquals("from GroovyTest2Class.doSomething()", FLAG);
+    }
+
+    public void testClasspath_missing() {
+        try {
+            project.executeTarget("groovyClasspath_missing");
+            fail();
+        }
+        catch (final Exception e) {
+            assertEquals(BuildException.class, e.getClass());
+        }
+
+    }
+
+    public void testClasspath_classpathAttribute() {
+        assertNull(FLAG);
+        project.executeTarget("groovyClasspath_classpathAttribute");
+        assertEquals("from groovytest3.GroovyTest3Class.doSomething()", FLAG);
+    }
+
+    public void testClasspath_classpathrefAttribute() {
+        assertNull(FLAG);
+        project.executeTarget("groovyClasspath_classpathrefAttribute");
+        assertEquals("from groovytest3.GroovyTest3Class.doSomething()", FLAG);
+    }
+
+    public void testClasspath_nestedclasspath() {
+        assertNull(FLAG);
+        project.executeTarget("groovyClasspath_nestedClasspath");
+        assertEquals("from groovytest3.GroovyTest3Class.doSomething()", FLAG);
+    }
+
+    public void testGroovyArgUsage() {
+        assertNull(FLAG);
+        project.executeTarget("groovyArgUsage");
+        assertEquals("from groovytest3.GroovyTest3Class.doSomethingWithArgs() 1 2 3", FLAG);
+    }
+
+    /**
+     * Test that helpful "file name" appears in the stack trace and not just "Script1" 
+     */
+    public void testFileNameInStackTrace() {
+    	testFileNameInStackTrace("groovyErrorMsg", "\\(embedded_script_in_.*GroovyTest.xml");
+    	testFileNameInStackTrace("groovyErrorMsg_ExternalFile", "GroovyTest_errorMessage.groovy");
+    }
+
+    private void testFileNameInStackTrace(final String target, final String fileNamePattern) {
+        try {
+            project.executeTarget(target);
+            fail();
+        }
+        catch (final BuildException e) {
+            assertEquals(BuildException.class, e.getClass());
+            final Throwable cause = e.getCause();
+            assertTrue(cause instanceof GroovyRuntimeException);
+
+            final StringWriter sw = new StringWriter();
+            cause.printStackTrace(new PrintWriter(sw));
+            
+            final String stackTrace = sw.toString();
+            final Pattern pattern = Pattern.compile(fileNamePattern);
+            assertTrue("Does >" + stackTrace + "< contain >" + fileNamePattern + "<?", 
+            		pattern.matcher(stackTrace).find());
+        }
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/ant/GroovyTest.xml b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest.xml
new file mode 100644
index 0000000..edd6ad4
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+
+<project name="test-groovy-task" default="groovyCodeWithinTask" basedir=".">
+
+    <target name="groovyCodeWithinTask" depends="defineTask">
+        <groovy>
+            org.codehaus.groovy.ant.GroovyTest.FLAG = "from groovy inlined in ant"
+        </groovy>
+    </target>
+
+    <target name="groovyCodeInExternalFile" depends="defineTask">
+        <groovy src="GroovyTest1.groovy"/>
+    </target>
+
+    <target name="groovyCodeInExternalFileWithOtherClass" depends="defineTask">
+        <groovy src="GroovyTest2.groovy"/>
+    </target>
+
+    <target name="groovyClasspath_missing" depends="defineTask" description="should fail!">
+        <groovy>
+            def foo = new GroovyTest3Class()
+            foo.doSomething()
+        </groovy>
+    </target>
+
+    <target name="groovyClasspath_classpathAttribute" depends="defineTask">
+        <groovy classpath="groovytest3">
+            def foo = new GroovyTest3Class()
+            foo.doSomething()
+        </groovy>
+    </target>
+
+    <target name="groovyClasspath_classpathrefAttribute" depends="defineTask">
+        <path id="myClasspathRef">
+            <pathelement location="groovytest3"/>
+        </path>
+
+        <groovy classpathref="myClasspathRef">
+            def foo = new GroovyTest3Class()
+            foo.doSomething()
+        </groovy>
+    </target>
+
+    <target name="groovyClasspath_nestedClasspath" depends="defineTask">
+        <groovy classpathref="myClasspathRef">
+            <classpath>
+                <pathelement location="groovytest3"/>
+            </classpath>
+            def foo = new GroovyTest3Class()
+            foo.doSomething()
+        </groovy>
+    </target>
+
+    <target name="groovyArgUsage" depends="defineTask">
+        <groovy classpath="groovytest3">
+            <arg line="1 2 3"/>
+            def foo = new GroovyTest3Class()
+            foo.doSomethingWithArgs(args)
+        </groovy>
+    </target>
+
+    <target name="groovyErrorMsg" depends="defineTask">
+        <groovy>
+        	// should produce an error message
+
+        	def f = {
+        		t.notExisting()
+        	}
+
+        	f()
+        </groovy>
+    </target>
+
+    <target name="groovyErrorMsg_ExternalFile" depends="defineTask">
+        <groovy src="GroovyTest_errorMessage.groovy"/>
+    </target>
+
+    <target name="defineTask">
+        <taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"/>
+    </target>
+</project>
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/ant/GroovyTest1.groovy b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest1.groovy
new file mode 100644
index 0000000..869cdeb
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest1.groovy
@@ -0,0 +1,2 @@
+
+org.codehaus.groovy.ant.GroovyTest.FLAG = "from groovy file called from ant"
diff --git a/groovy/src/test/org/codehaus/groovy/ant/GroovyTest2.groovy b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest2.groovy
new file mode 100644
index 0000000..b058150
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest2.groovy
@@ -0,0 +1,3 @@
+
+def foo = new GroovyTest2Class()
+foo.doSomething()
diff --git a/groovy/src/test/org/codehaus/groovy/ant/GroovyTest2Class.groovy b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest2Class.groovy
new file mode 100644
index 0000000..3ed9537
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest2Class.groovy
@@ -0,0 +1,9 @@
+
+class GroovyTest2Class
+{
+
+	void doSomething()
+	{
+		org.codehaus.groovy.ant.GroovyTest.FLAG = "from GroovyTest2Class.doSomething()"
+	}
+}
diff --git a/groovy/src/test/org/codehaus/groovy/ant/GroovyTest_errorMessage.groovy b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest_errorMessage.groovy
new file mode 100644
index 0000000..6ad0676
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/ant/GroovyTest_errorMessage.groovy
@@ -0,0 +1,7 @@
+// should produce an error message
+
+def f = {
+	t.notExisting()
+}
+
+f()
diff --git a/groovy/src/test/org/codehaus/groovy/ant/groovytest3/GroovyTest3Class.groovy b/groovy/src/test/org/codehaus/groovy/ant/groovytest3/GroovyTest3Class.groovy
new file mode 100644
index 0000000..9953e74
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/ant/groovytest3/GroovyTest3Class.groovy
@@ -0,0 +1,13 @@
+
+class GroovyTest3Class
+{
+	void doSomething()
+	{
+		org.codehaus.groovy.ant.GroovyTest.FLAG = "from groovytest3.GroovyTest3Class.doSomething()"
+	}
+
+	void doSomethingWithArgs(args)
+	{
+		org.codehaus.groovy.ant.GroovyTest.FLAG = "from groovytest3.GroovyTest3Class.doSomethingWithArgs() " + args.join(" ")
+	}
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/AnnotationSourceParsingTest.java b/groovy/src/test/org/codehaus/groovy/antlr/AnnotationSourceParsingTest.java
new file mode 100644
index 0000000..5dd8956
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/AnnotationSourceParsingTest.java
@@ -0,0 +1,27 @@
+package org.codehaus.groovy.antlr;

+

+import java.io.StringReader;

+

+/**

+ * Parsing annotations.

+ * 

+ * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>

+ */

+public class AnnotationSourceParsingTest extends SourceParserTest {

+  public void testMultiLineAttributes() {

+    StringReader reader = new StringReader(

+            "class OtherSection\n"

+        +   "{\n" 

+        +		"    @CollectionOfElements\n" 

+        +		"    @JoinTable\n" 

+        + 	"    (\n" 

+        +		"        table=@Table(name=\"gaga\"),\n" 

+        +		"        joinColumns = @JoinColumn(name=\"BoyId\")\n" 

+        +		"    )\n" 

+        +		"    @Column(name=\"favoritepoupon\", \n" 

+        +       		    "nullable=false)\n" 

+        +		"    Set<String> questions = new HashSet<String> ()\n\n" 

+        +		"}");

+    parse("testMultiLineAttributes", reader);

+  }

+}

diff --git a/groovy/src/test/org/codehaus/groovy/antlr/EnumSourceParsingTest.java b/groovy/src/test/org/codehaus/groovy/antlr/EnumSourceParsingTest.java
new file mode 100644
index 0000000..e631269
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/EnumSourceParsingTest.java
@@ -0,0 +1,111 @@
+package org.codehaus.groovy.antlr;

+

+import java.io.StringReader;

+

+

+/**

+ * Parser tests for Enum definitions.

+ *

+ * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a>

+ */

+public class EnumSourceParsingTest extends SourceParserTest {

+    public void testParseEnumConstants() {

+        StringReader reader = new StringReader(

+                "enum One {\n"

+                        + "  ONE, TWO, THREE\n"

+                        + "}");

+        parse("testParseEnumConstants", reader);

+    }

+

+    public void testParseEnumMultiLine() {

+      StringReader reader = new StringReader(

+          "enum ParseCode\n" +

+          "{\n" +

+          "    COMPLETE,\n" +

+          "    INCOMPLETE,\n" +

+          "    ERROR\n" +

+          "}");

+      parse("testParseEnumMultiLine", reader);

+    }

+    

+    public void testParseEnumImplementsMultiLine() {

+        StringReader reader = new StringReader(

+            "enum ParseCode implements I\n" +

+            "{\n" +

+            "    COMPLETE,\n" +

+            "    INCOMPLETE,\n" +

+            "    ERROR\n" +

+            "}");

+        parse("testParseEnumImplementsMultiLine", reader);

+    }

+    

+    public void testParseEnumImplementsMultiLine2() {

+        StringReader reader = new StringReader(

+            "enum ParseCode\n" +

+            "implements I\n" +

+            "{\n" +

+            "    COMPLETE,\n" +

+            "    INCOMPLETE,\n" +

+            "    ERROR\n" +

+            "}");

+        parse("testParseEnumImplementsMultiLine2", reader);

+    }

+    

+    public void testParseEnumConstantsOneLiner() {

+        StringReader reader = new StringReader(

+                "enum One { ONE, TWO, THREE }");

+        parse("testParseEnumConstantsOneLiner", reader);

+    }

+

+    public void testParseEnumImplements() {

+        StringReader reader = new StringReader(

+                "enum Two implements I1 {\n"

+                        + "ONE, TWO, THREE\n"

+                        + "}");

+        parse("testParseEnumImplements", reader);

+    }

+

+    public void testParseEnumWithValues() {

+        StringReader reader = new StringReader(

+                "enum Three1 {\n"

+                        + "    ONE(1), TWO(2)\n\n"

+                        + "    Three1(val) {\n"

+                        + "        value = val\n"

+                        + "    }\n\n"

+                        + "    private final int value"

+                        + "}");

+        parse("testParseEnumWithValues", reader);

+

+        reader = new StringReader(

+                "enum Three1 {\n"

+                        + "    @Annotation ONE(1), TWO(2)\n\n"

+                        + "    Three1(val) {\n"

+                        + "        value = val\n"

+                        + "    }\n\n"

+                        + "    private final int value"

+                        + "}");

+        parse("testParseEnumWithValues2", reader);

+    }

+

+    public void testParseEnumWithMethodDefinitions() {

+        StringReader reader = new StringReader(

+                "enum Four {\n"

+                        + "    ONE, TWO, THREE\n\n"

+                        + "    def someMethod() { }\n"

+                        + "    public m2(args) { }\n"

+                        + "    int m3(String arg) { }\n"

+                        + "}");

+        parse("testParseEnumWithMethodDefinitions", reader);

+    }

+

+    public void testParseCompleteEnum() {

+        StringReader reader = new StringReader(

+                "enum Five {\n"

+                        + "    ONE { double eval(int v) { return (double) v } }, \n"

+                        + "    TWO {\n"

+                        + "        double eval(int v) { return (double) v + 1 }\n"

+                        + "    }, THREE\n"

+                        + "}");

+        parse("testParseCompleteEnum", reader);

+    }

+}

diff --git a/groovy/src/test/org/codehaus/groovy/antlr/GroovySourceASTTest.java b/groovy/src/test/org/codehaus/groovy/antlr/GroovySourceASTTest.java
new file mode 100644
index 0000000..c985c42
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/GroovySourceASTTest.java
@@ -0,0 +1,31 @@
+package org.codehaus.groovy.antlr;
+
+import groovy.util.GroovyTestCase;
+
+public class GroovySourceASTTest extends GroovyTestCase {
+    GroovySourceAST a;
+    GroovySourceAST b;
+
+    protected void setUp() throws Exception {
+        a = new GroovySourceAST();
+        a.setLine(3);
+        a.setColumn(3);
+
+        b = new GroovySourceAST();
+        b.setLine(4);
+        b.setColumn(2);
+    }
+
+    public void testLessThan() throws Exception {
+        assertTrue(a.compareTo(b) < 0);
+    }
+
+    public void testEquality() throws Exception {
+        assertTrue(a.equals(a));
+        assertTrue(a.compareTo(a) == 0);
+    }
+
+    public void testGreaterThan() throws Exception {
+        assertTrue(b.compareTo(a) > 0);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/SourceBufferTest.java b/groovy/src/test/org/codehaus/groovy/antlr/SourceBufferTest.java
new file mode 100644
index 0000000..4d407b4
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/SourceBufferTest.java
@@ -0,0 +1,119 @@
+/*
+ $Id$
+
+ Copyright 2005 (C) Jeremy Rayner. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.antlr;
+
+import groovy.util.GroovyTestCase;
+
+import java.io.Reader;
+import java.io.StringReader;
+
+/**
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+public class SourceBufferTest extends GroovyTestCase {
+
+    public void testEmptyBuffer() throws Exception {
+        SourceBuffer buffer = getSourceBuffer("");
+        assertNull(buffer.getSnippet(new LineColumn(1, 1), new LineColumn(1, 1)));
+    }
+
+    public void testSimpleUsage() throws Exception {
+        SourceBuffer buffer = getSourceBuffer("println 'hello world'");
+        assertEquals("hello", buffer.getSnippet(new LineColumn(1, 10), new LineColumn(1, 15)));
+    }
+
+    public void testUnixLineUsage() throws Exception {
+        String endOfLine = "\n";
+        StringBuffer src = new StringBuffer();
+        src.append("println 'hello world'").append(endOfLine);
+        src.append("println 'oh not, not that again'").append(endOfLine);
+        SourceBuffer buffer = getSourceBuffer(src.toString());
+        assertEquals("hello", buffer.getSnippet(new LineColumn(1, 10), new LineColumn(1, 15)));
+        assertEquals("world'" + endOfLine + "print", buffer.getSnippet(new LineColumn(1, 16), new LineColumn(2, 6)));
+        assertEquals(endOfLine, buffer.getSnippet(new LineColumn(1, 22), new LineColumn(1, 23)));
+        assertEquals(endOfLine, buffer.getSnippet(new LineColumn(2, 33), new LineColumn(2, 34)));
+    }
+
+    public void testDOSLineUsage() throws Exception {
+        String endOfLine = "\r\n";
+        StringBuffer src = new StringBuffer();
+        src.append("println 'hello world'").append(endOfLine);
+        src.append("println 'oh not, not that again'").append(endOfLine);
+        SourceBuffer buffer = getSourceBuffer(src.toString());
+        assertEquals("hello", buffer.getSnippet(new LineColumn(1, 10), new LineColumn(1, 15)));
+        assertEquals("oh not", buffer.getSnippet(new LineColumn(2, 10), new LineColumn(2, 16)));
+        assertEquals("world'" + endOfLine + "print", buffer.getSnippet(new LineColumn(1, 16), new LineColumn(2, 6)));
+        assertEquals(endOfLine, buffer.getSnippet(new LineColumn(1, 22), new LineColumn(1, 24)));
+        assertEquals(endOfLine, buffer.getSnippet(new LineColumn(2, 33), new LineColumn(2, 35)));
+    }
+
+    public void testOutOfBounds() throws Exception {
+        String endOfLine = "\n";
+        StringBuffer src = new StringBuffer();
+        src.append("println 'hello world'").append(endOfLine);
+        src.append("println 'oh not, not that again'").append(endOfLine);
+        SourceBuffer buffer = getSourceBuffer(src.toString());
+        assertEquals("println", buffer.getSnippet(new LineColumn(0, 0), new LineColumn(1, 8)));
+        assertEquals("println", buffer.getSnippet(new LineColumn(-10, -1), new LineColumn(1, 8)));
+        assertEquals(endOfLine, buffer.getSnippet(new LineColumn(2, 33), new LineColumn(2, 40)));
+        assertEquals("", buffer.getSnippet(new LineColumn(3, 33), new LineColumn(6, 40)));
+    }
+
+    private SourceBuffer getSourceBuffer(String text) throws Exception {
+        SourceBuffer buffer = new SourceBuffer();
+        Reader reader = new UnicodeEscapingReader(new StringReader(text), buffer);
+
+        while (reader.read() != -1) {
+            // empty loop
+            // - read all characters till the end of the reader
+            // UnicodeEscapingReader has side effects of
+            // filling the buffer
+        }
+        return buffer;
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/SourceParserTest.java b/groovy/src/test/org/codehaus/groovy/antlr/SourceParserTest.java
new file mode 100644
index 0000000..9614ead
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/SourceParserTest.java
@@ -0,0 +1,34 @@
+package org.codehaus.groovy.antlr;

+

+import groovy.util.GroovyTestCase;

+

+import java.io.PrintWriter;

+import java.io.Reader;

+import java.io.StringWriter;

+

+import org.codehaus.groovy.antlr.parser.GroovyLexer;

+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;

+

+public class SourceParserTest extends GroovyTestCase {

+    protected void parse(String name, Reader reader) {

+        SourceBuffer sourceBuffer = new SourceBuffer();

+        UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(reader, sourceBuffer);

+        GroovyLexer lexer = new GroovyLexer(unicodeReader);

+        unicodeReader.setLexer(lexer);

+        GroovyRecognizer parser = GroovyRecognizer.make(lexer);

+        parser.setSourceBuffer(sourceBuffer);

+        parser.setFilename(name);

+

+        // start parsing at the compilationUnit rule

+        try {

+            parser.compilationUnit();

+        }

+        catch (Exception ex) {

+            StringWriter out = new StringWriter();

+            out.write(ex.getMessage());

+            out.write("\n");

+            ex.printStackTrace(new PrintWriter(out));

+            fail(out.toString());

+        }

+    }

+}

diff --git a/groovy/src/test/org/codehaus/groovy/antlr/treewalker/CompositeVisitorTest.java b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/CompositeVisitorTest.java
new file mode 100644
index 0000000..82fdde3
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/CompositeVisitorTest.java
@@ -0,0 +1,95 @@
+package org.codehaus.groovy.antlr.treewalker;
+
+import antlr.collections.AST;
+import junit.framework.TestCase;
+import org.codehaus.groovy.antlr.AntlrASTProcessor;
+import org.codehaus.groovy.antlr.SourceBuffer;
+import org.codehaus.groovy.antlr.UnicodeEscapingReader;
+import org.codehaus.groovy.antlr.parser.GroovyLexer;
+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
+import org.custommonkey.xmlunit.XMLUnit;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Testcases for the composite visitor.
+ */
+public class CompositeVisitorTest extends TestCase {
+
+    protected void setUp() throws Exception {
+        XMLUnit.setIgnoreWhitespace(true);
+    }
+
+    public void testCompositeTransparency() throws Exception {
+        // TODO: add more tests in here (is there a way to share snippets with SourcePrinterTest?)
+        assertCompositeTransparency("public abstract class Foo{}");
+        assertCompositeTransparency("int[] primes = new int[5]");
+        assertCompositeTransparency("if (foo==~\"bar\"){}");
+        assertCompositeTransparency("a=12");
+        assertCompositeTransparency("def x=1&2");
+        assertCompositeTransparency("x&=2");
+        assertCompositeTransparency("def z = ~123");
+        assertCompositeTransparency("def y = 1 | 2");
+        assertCompositeTransparency("y|=2");
+        assertCompositeTransparency("def q = 1 >>> 2");
+        assertCompositeTransparency("y>>>=2");
+        assertCompositeTransparency("def y = true ^ false");
+        assertCompositeTransparency("y^=false");
+        assertCompositeTransparency("switch(foo){case bar:x=1}");
+        assertCompositeTransparency("class Foo{def bar}");
+        assertCompositeTransparency("[1,2,3].each{println it}");
+        assertCompositeTransparency("def x = foo.bar(mooky) {x,y-> wibble(y,x)}");
+        assertCompositeTransparency("1<=>2");
+        assertCompositeTransparency("class Foo{Foo(int x) {this()}}");
+        assertCompositeTransparency("class Foo{Foo(x) {this()}}");
+        assertCompositeTransparency("class Foo {private Foo() {}}");
+        assertCompositeTransparency("--b");
+        assertCompositeTransparency("1/2");
+        assertCompositeTransparency("x/=2");
+        assertCompositeTransparency("java.util.Date d = new java.util.Date()");
+        assertCompositeTransparency("class Foo extends java.util.Date {}");
+        assertCompositeTransparency("foo.bar.mooky()");
+        assertCompositeTransparency("package foo.bar");
+        assertCompositeTransparency("import java.util.Date");
+        assertCompositeTransparency("import java.io.*");
+        assertCompositeTransparency("@foo.Bar mooky");
+        assertCompositeTransparency("def foo() throws bar.MookyException{}");
+        assertCompositeTransparency("def x = \"${foo.bar}\"");
+    }
+
+    private void assertCompositeTransparency(String input) throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        GroovyRecognizer parser;
+        SourceBuffer sourceBuffer = new SourceBuffer();
+        UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(new StringReader(input), sourceBuffer);
+        GroovyLexer lexer = new GroovyLexer(unicodeReader);
+        unicodeReader.setLexer(lexer);
+        parser = GroovyRecognizer.make(lexer);
+        parser.setSourceBuffer(sourceBuffer);
+        String[] tokenNames = parser.getTokenNames();
+        parser.compilationUnit();
+        AST ast = parser.getAST();
+
+        // determine direct result
+        Visitor directVisitor = new SourcePrinter(new PrintStream(baos), tokenNames, false);
+        AntlrASTProcessor traverser = new SourceCodeTraversal(directVisitor);
+        traverser.process(ast);
+        String directResult = new String(baos.toByteArray());
+
+        // determine composite result
+        baos.reset();
+        List wrappedVisitors = new ArrayList();
+        wrappedVisitors.add(directVisitor);
+        Visitor compositeVisitor = new CompositeVisitor(wrappedVisitors);
+        traverser = new SourceCodeTraversal(compositeVisitor);
+        traverser.process(ast);
+        String compositeResult = new String(baos.toByteArray());
+
+        assertEquals(directResult, compositeResult);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/treewalker/Java2GroovyTest.java b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/Java2GroovyTest.java
new file mode 100644
index 0000000..8525661
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/Java2GroovyTest.java
@@ -0,0 +1,46 @@
+/**
+ *
+ * Copyright 2005 Jeremy Rayner
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **/
+package org.codehaus.groovy.antlr.treewalker;
+
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.antlr.java.Java2GroovyMain;
+
+public class Java2GroovyTest extends GroovyTestCase {
+
+    public void testSimpleClass() throws Exception {
+        assertEquals("private class Foo {int x = 1}", convert("private class Foo{int x=1;}"));
+    }
+
+    public void testStringLiteral() throws Exception {
+        assertEquals("class Foo {String x = \"mooky\"}", convert("public class Foo{String x = \"mooky\";}"));
+        assertEquals("class C {void m(String s) {File f = new File(\"sl\" + s)}}", convert("public class C{void m(String s) {File f=new File(\"sl\" + s);}}"));
+    }
+
+    private String convert(String input) throws Exception {
+        return Java2GroovyMain.convert("Java2GroovyTest.java", input);
+    }
+
+    private String mindmap(String input) throws Exception {
+        return Java2GroovyMain.mindmap(input);
+    }
+
+    private String nodePrinter(String input) throws Exception {
+        return Java2GroovyMain.nodePrinter(input);
+    }
+}
+
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/treewalker/LineColumnChecker.java b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/LineColumnChecker.java
new file mode 100644
index 0000000..5b060f6
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/LineColumnChecker.java
@@ -0,0 +1,58 @@
+/**
+ *
+ * Copyright 2005 Jeremy Rayner
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **/
+
+package org.codehaus.groovy.antlr.treewalker;
+
+import org.codehaus.groovy.antlr.GroovySourceAST;
+import org.codehaus.groovy.antlr.LineColumn;
+import org.codehaus.groovy.antlr.SourceBuffer;
+
+/**
+ * Source AST Visitor that will assert each node has a correct line/column info
+ * given a SourceBuffer
+ *
+ * @author Jeremy Rayner
+ */
+public class LineColumnChecker extends VisitorAdapter {
+    private SourceBuffer sourceBuffer;
+    private String[] tokenNames;
+
+    public LineColumnChecker(SourceBuffer sourceBuffer, String[] tokenNames) {
+        this.sourceBuffer = sourceBuffer;
+        this.tokenNames = tokenNames;
+    }
+    public void visitDefault(GroovySourceAST t,int visit) {
+        if (visit == OPENING_VISIT ) {
+            System.out.println("[" + tokenNames[t.getType()] + "]");
+            int line = t.getLine();
+            int column = t.getColumn();
+            int lineLast = t.getLineLast();
+            int columnLast = t.getColumnLast();
+
+            System.out.println("" + line + " / " +  column + " - " + lineLast + " / " + columnLast);
+            if (line > 0 && column > 0 && lineLast > 0 && columnLast > 0) {
+                System.out.println("" + sourceBuffer.getSnippet(new LineColumn(line, column), new LineColumn(lineLast, columnLast)));
+            } else {
+                System.out.println("ZERO");
+            }
+        } else if (visit == CLOSING_VISIT) {
+            System.out.println();
+        }
+
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/treewalker/LineColumnTest.java b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/LineColumnTest.java
new file mode 100644
index 0000000..0869c6c
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/LineColumnTest.java
@@ -0,0 +1,63 @@
+/**
+ *
+ * Copyright 2005 Jeremy Rayner
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **/
+
+package org.codehaus.groovy.antlr.treewalker;
+
+import antlr.collections.AST;
+import junit.framework.TestCase;
+import org.codehaus.groovy.antlr.AntlrASTProcessor;
+import org.codehaus.groovy.antlr.SourceBuffer;
+import org.codehaus.groovy.antlr.UnicodeEscapingReader;
+import org.codehaus.groovy.antlr.AntlrASTProcessSnippets;
+import org.codehaus.groovy.antlr.parser.GroovyLexer;
+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
+
+import java.io.StringReader;
+
+/**
+ * Test that will assert each node of Source AST has a correct line/column info
+ * given a SourceBuffer
+ *
+ * @author Jeremy Rayner
+ */
+public class LineColumnTest extends TestCase {
+    public void testSimpleGroovySource() throws Exception {
+        doStuff("def foo()\n" +
+                "{ print \"Hello World\" }\n" +
+                "\n" +
+                "foo()");
+    }
+
+    public void doStuff(String input) throws Exception {
+        GroovyRecognizer parser;
+        SourceBuffer sourceBuffer = new SourceBuffer();
+        UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(new StringReader(input), sourceBuffer);
+        GroovyLexer lexer = new GroovyLexer(unicodeReader);
+        unicodeReader.setLexer(lexer);
+        parser = GroovyRecognizer.make(lexer);
+        parser.setSourceBuffer(sourceBuffer);
+        String[] tokenNames = parser.getTokenNames();
+        parser.compilationUnit();
+        AST ast = parser.getAST();
+        AntlrASTProcessor snippets = new AntlrASTProcessSnippets(sourceBuffer);
+        ast = snippets.process(ast);
+        Visitor visitor = new LineColumnChecker(sourceBuffer, tokenNames);
+        AntlrASTProcessor traverser = new SourceCodeTraversal(visitor);
+        traverser.process(ast);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/treewalker/MindMapPrinterTest.java b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/MindMapPrinterTest.java
new file mode 100644
index 0000000..061f5e5
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/MindMapPrinterTest.java
@@ -0,0 +1,84 @@
+package org.codehaus.groovy.antlr.treewalker;
+
+import junit.framework.TestCase;
+
+import org.codehaus.groovy.antlr.parser.GroovyTokenTypes;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
+
+/**
+ * Testcases for the antlr AST visitor that prints groovy source code nodes as Freemind compatible XML.
+ */
+public class MindMapPrinterTest extends TestCase implements GroovyTokenTypes{
+
+    private static final String HEADER = "<map version='0.7.1'><node TEXT='AST'>";
+    private static final String FOOTER = "</node></map>";
+
+    protected void setUp() throws Exception {
+        XMLUnit.setIgnoreWhitespace(true);
+    }
+
+    public void testAbstract() throws Exception {
+        assertXmlEquals(
+                HEADER +
+                        "<node TEXT='MODIFIERS &lt;" + MODIFIERS +"&gt;' POSITION='right' COLOR=\"#000000\">" +
+                        "  <node TEXT='public  &lt;" + LITERAL_public +"&gt;' POSITION='right' COLOR=\"#17178B\"></node>" +
+                        "</node>" +
+                        "<node TEXT='abstract  &lt;" + ABSTRACT +"&gt;' POSITION='right' COLOR=\"#006699\"></node>" +
+                        "<node TEXT='CLASS_DEF &lt;" + CLASS_DEF +"&gt;' POSITION='right' COLOR=\"#17178B\"></node>" +
+                        "<node TEXT='an identifier &lt;" + IDENT +"&gt; : Foo' POSITION='right' COLOR=\"#006699\"></node>" +
+                        "<node TEXT='EXTENDS_CLAUSE &lt;" + EXTENDS_CLAUSE +"&gt;' POSITION='right' COLOR=\"#17178B\"></node>" +
+                        "<node TEXT='IMPLEMENTS_CLAUSE &lt;" + IMPLEMENTS_CLAUSE +"&gt;' POSITION='right' COLOR=\"#17178B\"></node>" +
+                        "<node TEXT='OBJBLOCK &lt;" + OBJBLOCK +"&gt;' POSITION='right' COLOR=\"#006699\"></node>" +
+                        FOOTER,
+                pretty("public abstract class Foo{}"));
+
+    }
+
+    public void testArrayDeclarator() throws Exception {
+        assertXmlEquals(
+                HEADER +
+                        "<node TEXT='VARIABLE_DEF &lt;" + VARIABLE_DEF +"&gt; : primes' POSITION='right' COLOR=\"#000000\">" +
+                        "  <node TEXT='TYPE &lt;" + TYPE +"&gt;' POSITION='right' COLOR=\"#17178B\">" +
+                        "    <node TEXT='ARRAY_DECLARATOR &lt;" + ARRAY_DECLARATOR +"&gt; : [' POSITION='right' COLOR=\"#000000\">" +
+                        "      <node TEXT='int  &lt;" + LITERAL_int +"&gt;' POSITION='right' COLOR=\"#17178B\"></node>" +
+                        "    </node>" +
+                        "  </node>" +
+                        "  <node TEXT='an identifier &lt;" + IDENT +"&gt; : primes' POSITION='right' COLOR=\"#006699\"></node>" +
+                        "  <node TEXT='=  &lt;" + ASSIGN +"&gt;' POSITION='right' COLOR=\"#000000\">" +
+                        "    <node TEXT='new  &lt;" + LITERAL_new +"&gt;' POSITION='right' COLOR=\"#17178B\">" +
+                        "      <node TEXT='int  &lt;" + LITERAL_int +"&gt;' POSITION='right' COLOR=\"#17178B\"></node>" +
+                        "      <node TEXT='ARRAY_DECLARATOR &lt;" + ARRAY_DECLARATOR +"&gt; : [' POSITION='right' COLOR=\"#000000\">" +
+                        "        <node TEXT='a numeric literal &lt;" + NUM_INT +"&gt; : 5' POSITION='right' COLOR=\"#006699\"></node>" +
+                        "      </node>" +
+                        "    </node>" +
+                        "  </node>" +
+                        "</node>" +
+                        FOOTER,
+                pretty("int[] primes = new int[5]"));
+    }
+
+    public void testRegexMatch() throws Exception {
+        assertXmlEquals(
+                HEADER +
+                        "<node TEXT='if  &lt;" + LITERAL_if +"&gt;' POSITION='right' COLOR=\"#17178B\">" +
+                        "  <node TEXT='an identifier &lt;" + IDENT +"&gt; : foo' POSITION='right' COLOR=\"#006699\"></node>" +
+                        "  <node TEXT='==~  &lt;" + REGEX_MATCH +"&gt;' POSITION='right' COLOR=\"#000000\"></node>" +
+                        "  <node TEXT='a string literal &lt;" + STRING_LITERAL +"&gt; : bar' POSITION='right' COLOR=\"#008000\"></node>" +
+                        "</node>" +
+                        "<node TEXT='SLIST &lt;" + SLIST +"&gt; : {' POSITION='right' COLOR=\"#006699\"></node>" +
+                        FOOTER,
+                pretty("if (foo==~\"bar\"){}"));
+    }
+
+    private void assertXmlEquals(String expected, String actual) throws Exception {
+        Diff diff = new Diff(expected, actual);
+        assertTrue(diff.toString(), diff.similar());
+    }
+
+    private String pretty(String input) throws Exception {
+        TraversalTestHelper traverser = new TraversalTestHelper();
+        return traverser.traverse(input, MindMapPrinter.class);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/treewalker/NodeAsHTMLPrinterTest.java b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/NodeAsHTMLPrinterTest.java
new file mode 100644
index 0000000..da8296e
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/NodeAsHTMLPrinterTest.java
@@ -0,0 +1,82 @@
+package org.codehaus.groovy.antlr.treewalker;
+
+import junit.framework.TestCase;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.XMLUnit;
+
+/**
+ * Testcases for the antlr AST visitor that prints groovy source code nodes as HTML.
+ */
+public class NodeAsHTMLPrinterTest extends TestCase {
+
+    private static final String HEADER = "<html><head></head><body><pre>";
+    private static final String FOOTER = "</pre></body></html>";
+
+    protected void setUp() throws Exception {
+        XMLUnit.setIgnoreWhitespace(true);
+    }
+
+    public void testAbstract() throws Exception {
+        assertXmlEquals(
+                HEADER +
+                        "<code title='MODIFIERS'><font color='#000000'>" +
+                        "  <code title='\"public\"'><font color='#17178B'></font></code>" +
+                        "</font></code>" +
+                        "<code title='\"abstract\"'><font color='#000000'></font></code>" +
+                        "<code title='CLASS_DEF'><font color='#17178B'></font></code>" +
+                        "<code title='an identifier'><font color='#000000'></font></code>" +
+                        "<code title='EXTENDS_CLAUSE'><font color='#17178B'></font></code>" +
+                        "<code title='IMPLEMENTS_CLAUSE'><font color='#17178B'></font></code>" +
+                        "<code title='OBJBLOCK'><font color='#000000'></font></code>" +
+                        FOOTER,
+                pretty("public abstract class Foo{}"));
+
+    }
+
+    public void testArrayDeclarator() throws Exception {
+        assertXmlEquals(
+                HEADER +
+                        "<code title='VARIABLE_DEF'><font color='#000000'>" +
+                        "  <code title='TYPE'><font color='#17178B'>" +
+                        "    <code title='ARRAY_DECLARATOR'><font color='#000000'>" +
+                        "      <code title='\"int\"'><font color='#17178B'></font></code>" +
+                        "    </font></code>" +
+                        "  </font></code>" +
+                        "  <code title='an identifier'><font color='#000000'></font></code>" +
+                        "  <code title=\"'='\"><font color='#000000'>" +
+                        "    <code title='\"new\"'><font color='#17178B'>" +
+                        "      <code title='\"int\"'><font color='#17178B'></font></code>" +
+                        "      <code title='ARRAY_DECLARATOR'><font color='#000000'>" +
+                        "        <code title='a numeric literal'><font color='#000000'></font></code>" +
+                        "      </font></code>" +
+                        "    </font></code>" +
+                        "  </font></code>" +
+                        "</font></code>" +
+                        FOOTER,
+                pretty("int[] primes = new int[5]"));
+    }
+
+    public void testRegexMatch() throws Exception {
+        assertXmlEquals(
+                HEADER +
+                        "<code title='\"if\"'><font color='#17178B'>" +
+                        "  <code title='an identifier'><font color='#000000'></font></code>" +
+                        "  <code title=\"'==~'\"><font color='#000000'></font></code>" +
+                        "  <code title='a string literal'><font color='#008000'></font></code>" +
+                        "</font></code>" +
+                        "<code title='SLIST'><font color='#000000'></font></code>" +
+                        FOOTER,
+                pretty("if (foo==~\"bar\"){}"));
+    }
+
+    private void assertXmlEquals(String expected, String actual) throws Exception {
+        Diff diff = new Diff(expected, actual);
+        assertTrue(diff.toString(), diff.similar());
+    }
+
+    private String pretty(String input) throws Exception {
+        TraversalTestHelper traverser = new TraversalTestHelper();
+        return traverser.traverse(input, NodeAsHTMLPrinter.class);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/treewalker/NodePrinterTest.java b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/NodePrinterTest.java
new file mode 100644
index 0000000..61a86e2
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/NodePrinterTest.java
@@ -0,0 +1,41 @@
+package org.codehaus.groovy.antlr.treewalker;
+
+import junit.framework.TestCase;
+import org.custommonkey.xmlunit.XMLUnit;
+
+/**
+ * Testcases for the antlr AST visitor that prints groovy source code nodes as Pseudo XML.
+ */
+public class NodePrinterTest extends TestCase {
+
+    protected void setUp() throws Exception {
+        XMLUnit.setIgnoreWhitespace(true);
+    }
+
+    public void testAbstract() throws Exception {
+        assertPseudoXmlEquals("<MODIFIERS><\"public\"></\"public\"></MODIFIERS><\"abstract\"></\"abstract\"><CLASS_DEF></CLASS_DEF><an identifier></an identifier><EXTENDS_CLAUSE></EXTENDS_CLAUSE><IMPLEMENTS_CLAUSE></IMPLEMENTS_CLAUSE><OBJBLOCK></OBJBLOCK>",
+                nodify("public abstract class Foo{}"));
+    }
+
+    public void testArrayDeclarator() throws Exception {
+        assertPseudoXmlEquals(
+                "<VARIABLE_DEF><TYPE><ARRAY_DECLARATOR><\"int\"></\"int\"></ARRAY_DECLARATOR></TYPE><an identifier></an identifier><'='><\"new\"><\"int\"></\"int\"></\"new\"><ARRAY_DECLARATOR><a numeric literal></a numeric literal></ARRAY_DECLARATOR></\"new\"></'='></VARIABLE_DEF>",
+                nodify("int[] primes = new int[5]"));
+    }
+
+    public void testRegexMatch() throws Exception {
+        assertPseudoXmlEquals(
+                "<\"if\"><an identifier></an identifier><'==~'></'==~'><a string literal></a string literal></\"if\"><SLIST></SLIST>",
+                nodify("if (foo==~\"bar\"){}"));
+    }
+
+    private void assertPseudoXmlEquals(String expected, String actual) throws Exception {
+        assertEquals(expected, actual);
+    }
+
+    private String nodify(String input) throws Exception {
+        TraversalTestHelper traverser = new TraversalTestHelper();
+        return traverser.traverse(input, NodePrinter.class);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/treewalker/SourcePrinterTest.java b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/SourcePrinterTest.java
new file mode 100644
index 0000000..01e50c3
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/SourcePrinterTest.java
@@ -0,0 +1,889 @@
+/**
+ *
+ * Copyright 2005 Jeremy Rayner
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **/
+package org.codehaus.groovy.antlr.treewalker;
+
+import groovy.util.GroovyTestCase;
+
+/**
+ * Testcases for the antlr AST visitor that prints groovy source code.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision$
+ */
+public class SourcePrinterTest extends GroovyTestCase {
+
+    public void testAbstract() throws Exception {
+        assertEquals("public abstract class Foo {}", pretty("public abstract class Foo{}"));
+    }
+
+    public void testAnnotation() throws Exception {
+        assertEquals("@Crimson foo", pretty("@Crimson foo"));
+        assertEquals("@Override int hashCode() {return 0}", pretty("@Override int hashCode() {return 0}"));
+    }
+
+    public void testAnnotations() throws Exception {
+        assertEquals("@Important @Blue package foo.bar", pretty("@Important @Blue package foo.bar"));
+    }
+
+    public void testAnnotationArrayInit() throws Exception {
+        // obsolete java syntax
+    }
+
+    public void testAnnotationDef() throws Exception {
+        // todo - 17 July 2006 - test fine, however this parses but causes error in AntlrParserPlugin
+        assertEquals("public @interface Foo{}", pretty("public @interface Foo{}")); // fails after parser
+    }
+
+    public void testAnnotationFieldDef() throws Exception {
+        assertEquals("public @interface Foo{int bar() default 123}", pretty("public @interface Foo{int bar() default 123}")); // fails after parser
+    }
+
+    public void testAnnotationMemberValuePair() throws Exception {
+        assertEquals("@Prime(value = 17) int foo", pretty("@Prime(value=17) int foo"));
+        assertEquals("@Blue(v = 3, v = 5) int bar", pretty("@Blue(v = 3, v = 5) int bar"));
+    }
+
+    public void testArrayDeclarator() throws Exception {
+        assertEquals("int[] primes = new int[5]", pretty("int[] primes = new int[5]"));
+    }
+
+    public void testAssign() throws Exception {
+        assertEquals("a = 12", pretty("a=12"));
+    }
+
+    public void testBand() throws Exception {
+        assertEquals("def x = 1 & 2", pretty("def x=1&2"));
+    }
+
+    public void testBandAssign() throws Exception {
+        assertEquals("x &= 2", pretty("x&=2"));
+    }
+
+    public void testBnot() throws Exception {
+        assertEquals("def z = ~123", pretty("def z = ~123"));
+    }
+
+    public void testBor() throws Exception {
+        assertEquals("def y = 1 | 2", pretty("def y = 1 | 2"));
+    }
+
+    public void testBorAssign() throws Exception {
+        assertEquals("y |= 2", pretty("y|=2"));
+    }
+
+    public void testBsr() throws Exception {
+        // unsigned right shift
+        assertEquals("def q = 1 >>> 2", pretty("def q = 1 >>> 2"));
+    }
+
+    public void testBsrAssign() throws Exception {
+        assertEquals("y >>>= 2", pretty("y>>>=2"));
+    }
+
+    public void testBxor() throws Exception {
+        assertEquals("def y = true ^ false", pretty("def y = true ^ false"));
+    }
+
+    public void testBxorAssign() throws Exception {
+        assertEquals("y ^= false", pretty("y^=false"));
+    }
+
+    public void testCaseGroup() throws Exception {
+        assertEquals("switch (foo) {case bar:x = 1}", pretty("switch(foo){case bar:x=1}"));
+    }
+
+    public void testClassDef() throws Exception {
+        assertEquals("class Foo {def bar}", pretty("class Foo{def bar}"));
+    }
+
+    public void testClosedBlock() throws Exception { // not in java
+        assertEquals("[1, 2, 3].each {println it}", pretty("[1,2,3].each{println it}")); // not in java
+        assertEquals("def x = foo.bar(mooky){x,y -> wibble(y, x)}", pretty("def x = foo.bar(mooky) {x,y-> wibble(y,x)}")); // not in java
+        // todo: above is not quite the spacing I would expect, but good enough for now...
+    }
+
+    public void testClosureList() throws Exception { // not in java
+        assertEquals("for (int i = 0; i++; i < 10){}", pretty("for (int i=0;i++;i<10){}")); // not in java
+        assertEquals("def x = (int i = 0; i++; i < 10)", pretty("def x = (int i=0;i++;i<10)")); // not in java
+//todo        assertEquals("myMethod(int i = 0; i++; i < 10)", pretty("myMethod(int i=0;i++;i<10)")); // not in java
+    }
+
+    public void testCompareTo() throws Exception { // not in java
+        assertEquals("1 <=> 2", pretty("1<=>2")); // not in java
+    }
+
+    public void testCtorCall() throws Exception {
+        assertEquals("class Foo {Foo(int x) {this()}}", pretty("class Foo{Foo(int x) {this()}}"));
+        assertEquals("class Foo {Foo(x) {this()}}", pretty("class Foo{Foo(x) {this()}}"));
+    }
+
+    public void testCtorIdent() throws Exception {
+        assertEquals("class Foo {private Foo() {}}", pretty("class Foo {private Foo() {}}"));
+    }
+
+    public void testDec() throws Exception {
+        assertEquals("--b", pretty("--b"));
+    }
+
+    public void testDiv() throws Exception {
+        assertEquals("1 / 2", pretty("1/2"));
+    }
+
+    public void testDivAssign() throws Exception {
+        assertEquals("x /= 2", pretty("x/=2"));
+    }
+
+    public void testDot() throws Exception {
+        assertEquals("java.util.Date d = new java.util.Date()", pretty("java.util.Date d = new java.util.Date()"));
+        assertEquals("class Foo extends java.util.Date {}", pretty("class Foo extends java.util.Date {}"));
+        assertEquals("foo.bar.mooky()", pretty("foo.bar.mooky()"));
+        assertEquals("package foo.bar", pretty("package foo.bar"));
+        assertEquals("import java.util.Date", pretty("import java.util.Date"));
+        assertEquals("import java.io.*", pretty("import java.io.*"));
+        assertEquals("@foo.Bar mooky", pretty("@foo.Bar mooky"));
+        assertEquals("def foo() throws bar.MookyException{}", pretty("def foo() throws bar.MookyException{}")); // fails after parser
+        assertEquals("def x = \"${foo.bar}\"", pretty("def x = \"${foo.bar}\""));
+    }
+
+    public void testDynamicMember() throws Exception { // not in java
+        assertEquals("foo.(bar)", pretty("foo.(bar)")); // not in java
+        assertEquals("foo.\"${bar}\"", pretty("foo.\"${bar}\"")); // not in java
+    }
+
+    public void testElist() throws Exception {
+        assertEquals("println 2 + 2", pretty("println 2 + 2"));
+        assertEquals("for (i = 0; j = 2; i < 10; i++; j--){print i}", pretty("for (i = 0;j = 2;i < 10; i++; j--) {print i}")); // fails after parser
+        assertEquals("foo()", pretty("foo()")); // empty ELIST
+        assertEquals("foo(bar, mooky)", pretty("foo( bar , mooky )"));
+    }
+
+    public void testEnumConstantDef() throws Exception {
+        assertEquals("enum Coin {PENNY(1), DIME(10), QUARTER(25)}", pretty("enum Coin {PENNY(1), DIME(10), QUARTER(25)}")); // fails after parser
+    }
+
+    public void testEnumDef() throws Exception {
+        assertEquals("enum Season {WINTER, SPRING, SUMMER, AUTUMN}", pretty("enum Season{WINTER,SPRING,SUMMER,AUTUMN}")); // fails after parser
+        assertEquals("enum Operation {ADDITION{double eval(x, y) {return x + y}}}", pretty("enum Operation {ADDITION {double eval(x,y) {return x + y}}}")); // fails after parser
+    }
+
+    public void testEqual() throws Exception {
+        assertEquals("a == b", pretty("a==b"));
+    }
+
+    public void testEsc_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // dquote-tab-dquote
+        assertEquals("println \"\\\"\t\\\"\"", pretty("println \"\\\"\t\\\"\""));
+    }
+
+    public void testExponent() throws Exception {
+        assertEquals("println 1.2e-10", pretty("println 1.2e-10"));
+    }
+
+    public void testExpr_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        assertEquals("System.out.println(x)", pretty("System.out.println(x)"));
+        assertEquals("return f", pretty("return f"));
+        assertEquals("foo(bar);mooky(bar)", pretty("foo(bar);mooky(bar)"));
+    }
+
+    public void testExtendsClause() throws Exception {
+        assertEquals("class Foo extends Bar {}", pretty("class Foo extends Bar {}"));
+        assertEquals("interface Wibble extends Mooky{}", pretty("interface Wibble extends Mooky {}"));
+        //todo spacing is odd, c.f. last space in class vs interface above
+    }
+
+    public void testFinal() throws Exception {
+        assertEquals("public final int getX() {return 0}", pretty("public final int getX() {return 0}"));
+    }
+
+    public void testForCondition() throws Exception {
+        assertEquals("for (i = 0; i < 10; i++){println i}", pretty("for (i=0;i<10;i++) {println i}")); // fails after parser
+    }
+
+    // testForInit() covered by testForCondition()
+
+    public void testForInIterable() throws Exception { // not in java
+        assertEquals("for (i in [1, 2]) {}", pretty("for (i in [1,2]) {}")); // not in java
+    }
+
+    // testForIterator() covered by testForCondition()
+
+    public void testGe() throws Exception {
+        assertEquals("if (60 >= 70) {}", pretty("if (60>=70) {}"));
+    }
+
+    public void testGt() throws Exception {
+        assertEquals("if (2070 > 354) {}", pretty("if (2070 > 354) {}"));
+    }
+
+    public void testHexDigit() throws Exception {
+        assertEquals("def bar = 0xCaFe", pretty("def bar = 0xCaFe"));
+    }
+
+    public void testHexDigitInUnicodeEscape_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        assertEquals("def foo = '\\ubabe'", pretty("def foo = '\\ubabe'"));
+    }
+
+    public void testIdent() throws Exception {
+        // used _everywhere_ , lets assume that the other specific
+        // testcases include enough ident usage for now.
+        assertEquals("foo.bar", pretty("foo.bar"));
+    }
+
+    public void testImplementsClause() throws Exception {
+        assertEquals("class Foo implements Bar {}", pretty("class Foo implements Bar {}"));
+    }
+
+    public void testImplicitParameters() throws Exception { // not in java
+        assertEquals("[1, 2, 3].each {println it}", pretty("[1,2,3].each{println it}")); // not in java
+    }
+
+    public void testImport() throws Exception {
+        assertEquals("import foo.bar.Wibble", pretty("import foo.bar.Wibble"));
+    }
+
+    public void testInc() throws Exception {
+        assertEquals("++x", pretty("++x"));
+    }
+
+    public void testIndexOp() throws Exception {
+        assertEquals("foo.bar()[fred.wilma()]", pretty("foo.bar()[fred.wilma()]"));
+    }
+
+    public void testInterfaceDef() throws Exception {
+        //todo, the spacing here is... unusual
+        assertEquals("interface Foo{void blah() }", pretty("interface Foo{void blah()}"));
+    }
+
+    public void testInstanceInit() throws Exception {
+        assertEquals("class Foo {{x = 1}}", pretty("class Foo {{x=1}}"));
+    }
+
+    public void testLabeledArg() throws Exception { // not in java
+        assertEquals("myMethod(argOne:123, argTwo:123)", pretty("myMethod(argOne:123,argTwo:123)")); // not in java
+        assertEquals("myMap = [keyOne:123, keyTwo:234]", pretty("myMap = [keyOne:123,keyTwo:234]")); // not in java
+    }
+
+    public void testLabeledStat() throws Exception {
+        assertEquals("foo:x = 1", pretty("foo:x = 1"));
+    }
+
+    public void testLand() throws Exception {
+        assertEquals("true && false", pretty("true && false"));
+    }
+
+    public void testLe() throws Exception {
+        assertEquals("if (60 <= 70) {}", pretty("if (60<=70) {}"));
+    }
+
+    public void testListConstructor() throws Exception { // not in java
+        assertEquals("[a, b]", pretty("[a,b]")); // not in java
+    }
+
+    public void testLiteralAs() throws Exception { // not in java
+        assertEquals("import java.util.Date as MyDate", pretty("import java.util.Date as MyDate")); // not in java
+        // todo suspicious spacing in the following assertion
+        assertEquals("x = 12 as Long", pretty("x = 12 as Long")); // not in java
+    }
+
+    public void testLiteralAssert() throws Exception {
+        assertEquals("assert a == true", pretty("assert a== true"));
+        assertEquals("assert b == true : 99", pretty("assert b==true:99"));
+        // todo is ',' deprecated now?
+        //assertEquals("assert b == true , 99", pretty("assert b==true,99"));
+    }
+
+    public void testLiteralBoolean() throws Exception {
+        assertEquals("boolean b = true", pretty("boolean b =true"));
+    }
+
+    public void testLiteralBreak() throws Exception {
+        assertEquals("for (i in 1..100) {break }", pretty("for (i in 1..100) {break}"));
+        assertEquals("switch (foo) {default:break }", pretty("switch(foo){default:break}"));
+        assertEquals("def myMethod() {break }", pretty("def myMethod(){break}")); // fails after parser
+        // deprecated -> assertEquals("for (i in 1..100) {break 2}", pretty("for (i in 1..100) {break 2}")); // fails after parser
+
+        //todo should the colon be postfixed to the label?
+        // deprecated -> assertEquals("for (i in 1..100) {break label1:}", pretty("for (i in 1..100) {break label1:}")); // fails after parser
+        assertEquals("for (i in 1..100) {break label1}", pretty("for (i in 1..100) {break label1}"));
+    }
+
+    public void testLiteralByte() throws Exception {
+        assertEquals("byte b = 1", pretty("byte b=1"));
+    }
+
+    public void testLiteralCase() throws Exception {
+        assertEquals("switch (foo) {case 1:x = 3}", pretty("switch(foo){case 1:x=3}"));
+    }
+
+    public void testLiteralCatch() throws Exception {
+        assertEquals("try {} catch (Exception e) {}", pretty("try {} catch (Exception e) {}"));
+        assertEquals("try {} catch (Exception e1) {} catch (Exception2 e2) {}", pretty("try {} catch (Exception e1) {} catch (Exception2 e2) {}"));
+    }
+
+    public void testLiteralChar() throws Exception {
+        assertEquals("char c = \"a\"", pretty("char c = \"a\""));
+    }
+
+    public void testLiteralClass() throws Exception {
+        assertEquals("public class Foo {int bar}", pretty("public class Foo{int bar}"));
+    }
+
+    public void testLiteralContinue() throws Exception {
+        assertEquals("for (i in 1..100) {continue }", pretty("for (i in 1..100) {continue}"));
+        // deprecated -> assertEquals("for (i in 1..100) {continue 2}", pretty("for (i in 1..100) {continue 2}")); // fails after parser
+
+        //todo should the colon be postfixed to the label?
+        // deprecate -> assertEquals("for (i in 1..100) {continue label1:}", pretty("for (i in 1..100) {continue label1:}")); // fails after parser
+        assertEquals("for (i in 1..100) {continue label1}", pretty("for (i in 1..100) {continue label1}")); // fails after parser
+        
+        assertEquals("[1, 2, 3].each {continue }", pretty("[1,2,3].each{continue}")); // fails after parser
+    }
+
+    public void testLiteralDef() throws Exception { // not in java
+        assertEquals("def x = 123", pretty("def x=123")); // not in java
+        assertEquals("def myMethod() {return 0}", pretty("def myMethod(){return 0}")); // not in java
+        // note: def not needed in parameter declarations, but it is valid
+        //todo: is it ok to strip out 'def' from parameter declarations?
+        assertEquals("def foo(bar) {}", pretty("def foo(def bar){}")); // not in java
+    }
+
+    public void testLiteralDefault() throws Exception {
+        assertEquals("switch (foo) {default:x = 2}", pretty("switch(foo){default:x=2}"));
+        assertEquals("public @interface Foo{int bar() default 123}", pretty("public @interface Foo{int bar() default 123}")); // fails after parser
+    }
+
+    public void testLiteralDouble() throws Exception {
+        assertEquals("double d = 1.0", pretty("double d = 1.0"));
+    }
+
+    public void testLiteralElse() throws Exception {
+        assertEquals("if (false) {a = 1} else {a = 2}", pretty("if (false) {a=1} else {a=2}"));
+    }
+
+    public void testLiteralEnum() throws Exception {
+        assertEquals("enum Season {WINTER, SPRING, SUMMER, AUTUMN}", pretty("enum Season{WINTER,SPRING,SUMMER,AUTUMN}")); // fails after parser
+    }
+
+    public void testLiteralExtends() throws Exception {
+        assertEquals("class Foo extends java.util.Date {}", pretty("class Foo extends java.util.Date {}"));
+        assertEquals("class Foo extends Bar {}", pretty("class Foo extends Bar {}"));
+        assertEquals("interface Wibble extends Mooky{}", pretty("interface Wibble extends Mooky {}"));
+        //todo spacing is odd, c.f. last space in class vs interface above
+        assertEquals("public boolean process(Set<? extends TypeElement> annotations) {println annotations}", pretty("public boolean process(Set<? extends TypeElement> annotations) {println annotations}"));
+    }
+
+    public void testLiteralFalse() throws Exception {
+        assertEquals("if (false) {}", pretty("if (false) {}"));
+    }
+
+    public void testLiteralFinally() throws Exception {
+        assertEquals("try {}finally {}", pretty("try {}finally {}"));
+    }
+
+    public void testLiteralFloat() throws Exception {
+        assertEquals("float x", pretty("float x"));
+    }
+
+    public void testLiteralFor() throws Exception {
+        assertEquals("for (i in [1, 2, 3]) {}", pretty("for (i in [1,2,3]) {}"));
+        // check non-braced single statement
+        assertEquals("for (i in 1..100) rotateAntiClockwise()", pretty("for (i in 1..100) rotateAntiClockwise()"));
+    }
+
+    public void testLiteralIf() throws Exception {
+        assertEquals("if (a == b) return false", pretty("if (a==b) return false"));
+        assertEquals("if (a == b) {}", pretty("if (a==b) {}"));
+    }
+
+    public void testLiteralImplements() throws Exception {
+        assertEquals("class Foo implements Bar {}", pretty("class Foo implements Bar {}"));
+
+        //todo the following is legal Java, but pretty strange...?
+        assertEquals("enum EarthSeason implements Season {SPRING}", pretty("enum EarthSeason implements Season{SPRING}")); // fails after parser
+    }
+
+    public void testLiteralImport() throws Exception {
+        assertEquals("import foo.Bar", pretty("import foo.Bar"));
+        assertEquals("import mooky.*", pretty("import mooky.*"));
+    }
+
+    public void testLiteralIn() throws Exception { // not in java
+        assertEquals("for (i in 1..10) {}", pretty("for (i in 1..10) {}")); // not in java
+        assertEquals("if (i in myList) {}", pretty("if (i in myList) {}")); // not in java
+    }
+
+    public void testLiteralInstanceOf() throws Exception {
+        assertEquals("if (a instanceof String) {}", pretty("if (a instanceof String) {}"));
+    }
+
+    public void testLiteralInt() throws Exception {
+        assertEquals("int a", pretty("int a"));
+    }
+
+    public void testLiteralInterface() throws Exception {
+        assertEquals("interface Foo{}", pretty("interface Foo{}")); // fails after parser
+    }
+
+    public void testLiteralLong() throws Exception {
+        assertEquals("long a = 1", pretty("long a = 1"));
+    }
+
+    public void testLiteralNative() throws Exception {
+        assertEquals("public class R {public native void seek(long pos) }", pretty("public class R{public native void seek(long pos)}")); // fails after parser
+        assertEquals("native foo() ", pretty("native foo()")); // fails after parser
+    }
+
+    public void testLiteralNew() throws Exception {
+        assertEquals("new Foo()", pretty("new Foo()"));
+        assertEquals("def x = new int[5]", pretty("def x = new int[5]"));
+    }
+
+    public void testLiteralNull() throws Exception {
+        assertEquals("def foo = null", pretty("def foo=null"));
+    }
+
+    public void testLiteralPackage() throws Exception {
+        assertEquals("package foo.bar", pretty("package foo.bar"));
+    }
+
+    public void testLiteralPrivate() throws Exception {
+        assertEquals("private bar", pretty("private bar"));
+    }
+
+    public void testLiteralProtected() throws Exception {
+        assertEquals("protected mooky", pretty("protected mooky"));
+    }
+
+    public void testLiteralPublic() throws Exception {
+        assertEquals("public foo", pretty("public foo"));
+    }
+
+    public void testLiteralReturn() throws Exception {
+        assertEquals("def foo() {return false}", pretty("def  foo() { return false }"));
+        assertEquals("void bar() {return }", pretty("void bar() {return}"));
+    }
+
+    public void testLiteralShort() throws Exception {
+        assertEquals("short a = 1", pretty("short a = 1"));
+    }
+
+    public void testLiteralStatic() throws Exception {
+        assertEquals("static void foo() {}", pretty("static void foo() {}"));
+        //classes, interfaces, class/instance vars and methods
+        assertEquals("static int bar = 1", pretty("static int bar = 1"));
+        //todo: this should parse... assertEquals("private static <T> void foo(List<T> list){}", pretty("private static <T> void foo(List<T> list){}"));
+
+        assertEquals("class Foo {static {bar = 1}}", pretty("class Foo{static {bar=1}}"));
+    }
+
+    public void testLiteralSuper() throws Exception {
+        assertEquals("class Foo {public Foo() {super()}}", pretty("class Foo{public Foo(){super()}}"));
+
+        // todo will 'super' be allowed in non-parentheses method call styles?
+        assertEquals("class Bar {public Bar() {super 99}}", pretty("class Bar{public Bar(){super 99}}"));
+        assertEquals("class Bar {public Bar() {super(1, 2, 3)}}", pretty("class Bar{public Bar(){super(1,2,3)}}"));
+        assertEquals("println(super.toString())", pretty("println(super.toString())"));
+        //todo: doesn't parse correctly...   assertEquals("class Foo<T super C> {T t}",pretty("class Foo<T super C> {T t}"));
+    }
+
+    public void testLiteralSwitch() throws Exception {
+        assertEquals("switch (foo) {case bar:x = 2}", pretty("switch(foo){case bar:x=2}"));
+    }
+
+    public void testLiteralSynchronized() throws Exception {
+        assertEquals("synchronized foo() {}", pretty("synchronized foo(){}"));
+        assertEquals("synchronized (t) {doStuff(t)}", pretty("synchronized (t) {doStuff(t)}"));
+    }
+
+    public void testLiteralThis() throws Exception {
+        assertEquals("this", pretty("this"));
+        assertEquals("this 2", pretty("this 2"));
+        assertEquals("this()", pretty("this()"));
+        assertEquals("this(1, 2, 3)", pretty("this(1,2,3)"));
+        assertEquals("this.x = this.y", pretty("this.x=this.y"));
+    }
+
+    public void testLiteralThreadsafe() throws Exception {
+        assertEquals("threadsafe foo() {}", pretty("threadsafe foo() {}")); // fails after parser
+    }
+
+    public void testLiteralThrow() throws Exception {
+        assertEquals("def foo() {if (false) throw new RuntimeException()}", pretty("def foo() {if (false) throw new RuntimeException()}"));
+    }
+
+    public void testLiteralThrows() throws Exception {
+        //todo AntlrParserPlugin: Unexpected node type: '.' found when expecting type: an identifier
+        assertEquals("def foo() throws java.io.IOException{}", pretty("def foo() throws java.io.IOException{}")); // fails after parser
+    }
+
+    public void testLiteralTransient() throws Exception {
+        assertEquals("transient bar", pretty("transient bar"));
+    }
+
+    public void testLiteralTrue() throws Exception {
+        assertEquals("foo = true", pretty("foo = true"));
+    }
+
+    public void testLiteralTry() throws Exception {
+        assertEquals("try {} catch (Exception e) {}", pretty("try {} catch (Exception e) {}"));
+    }
+
+    public void testLiteralVoid() throws Exception {
+        assertEquals("void foo() {}", pretty("void foo(){}"));
+    }
+
+    public void testLiteralVolatile() throws Exception {
+        assertEquals("volatile mooky", pretty("volatile mooky"));
+    }
+
+    public void testLiteralWhile() throws Exception {
+        assertEquals("while (true) {}", pretty("while(true){}"));
+    }
+
+// deprecated
+//    public void testLiteralWith() throws Exception { // not in java
+//        assertEquals("with (myObject) {x = 1}", pretty("with(myObject) {x = 1}")); // fails after parser // not in java
+//    }
+
+    public void testLnot() throws Exception {
+        assertEquals("if (!isRaining) {}", pretty("if (!isRaining) {}"));
+    }
+
+    public void testLor() throws Exception {
+        assertEquals("true || false", pretty("true || false"));
+    }
+
+    public void testLparen_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        assertEquals("for (i in (history.size() - 1)..0) {}", pretty("for (i in (history.size() - 1)..0) {}"));
+    }
+
+    public void testLt() throws Exception {
+        assertEquals("if (3.4f < 12f) {}", pretty("if (3.4f < 12f) {}"));
+    }
+
+    public void testMapConstructor() throws Exception { // not in java
+        assertEquals("Map foo = [:]", pretty("Map foo = [:]")); // not in java
+        assertEquals("[a:1, b:2]", pretty("[a:1,b:2]")); // not in java
+    }
+
+    public void testMemberPointer() throws Exception { // not in java
+        assertEquals("def x = foo.&bar()", pretty("def x=foo.&bar()")); // not in java
+    }
+
+    public void testMethodCall() throws Exception {
+        assertEquals("foo(bar)", pretty("foo(bar)"));
+        assertEquals("[1, 2, 3].each {println it}", pretty("[1,2,3].each{println it}"));
+        assertEquals("foo(bar){mooky()}", pretty("foo(bar){mooky()}"));
+    }
+
+    public void testMethodDef() throws Exception {
+        assertEquals("def foo(int bar, boolean boo) {}", pretty("def foo(int bar,boolean boo) {}"));
+    }
+
+    public void testMinus() throws Exception {
+        assertEquals("def bar = 4 - foo", pretty("def bar=4-foo"));
+    }
+
+    public void testMinusAssign() throws Exception {
+        assertEquals("x -= 23", pretty("x -= 23"));
+    }
+
+    public void testMod() throws Exception {
+        assertEquals("x = 4 % 3", pretty("x = 4 % 3"));
+    }
+
+    public void testModifiers() throws Exception {
+        assertEquals("public static transient final native threadsafe synchronized volatile strictfp foo() {}", pretty("public static transient final native threadsafe synchronized volatile strictfp foo() {}")); // fails after parser
+    }
+
+    public void testModAssign() throws Exception {
+        assertEquals("x %= 23", pretty("x %= 23"));
+    }
+
+    public void testNotEqual() throws Exception {
+        assertEquals("a != b", pretty("a!=b"));
+    }
+
+    public void testNumBigDecimal() throws Exception { // not in java
+        assertEquals("a = 9.8g", pretty("a  =9.8g")); // not in java
+    }
+
+    public void testNumBigInt() throws Exception { // not in java
+        assertEquals("a = 12g", pretty("a=   12g")); // not in java
+    }
+
+    public void testNumDouble() throws Exception {
+        assertEquals("b = 34.4d", pretty("b=34.4d"));
+        assertEquals("b = 34.4D", pretty("b=34.4D"));
+    }
+
+    public void testNumFloat() throws Exception {
+        assertEquals("b = 34.4f", pretty("b=34.4f"));
+        assertEquals("b = 34.4F", pretty("b=34.4F"));
+    }
+
+    public void testNumInt() throws Exception {
+        assertEquals("a = 12", pretty("a=12"));
+    }
+
+    public void testNumLong() throws Exception {
+        assertEquals("a = 12l", pretty("a=12l"));
+    }
+
+    public void testObjblock() throws Exception {
+        assertEquals("class Foo {def bar}", pretty("class Foo {def bar}"));
+    }
+
+    public void testOptionalDot() throws Exception { // not in java
+        assertEquals("foo = england.london.kings?.head", pretty("foo = england.london.kings?.head")); // not in java
+    }
+
+    public void testPackageDef() throws Exception {
+        assertEquals("package foo.bar", pretty("package foo.bar"));
+    }
+
+    public void testParameterDef() throws Exception {
+        assertEquals("def foo(bar) {}", pretty("def foo(bar){}"));
+        assertEquals("def foo(String bar, String mooky) {}", pretty("def foo(String bar, String mooky){}"));
+        assertEquals("def c = {x -> println x}", pretty("def c = {x->println x}"));
+        assertEquals("def c = {x,y -> println(x + y)}", pretty("def c={x,y->println(x+y)}"));
+        assertEquals("def c = {int x,int y -> println(x + y)}", pretty("def c={int x, int y->println(x+y)}"));
+    }
+
+    public void testParameters() throws Exception {
+        assertEquals("def foo(String bar, String mooky) {}", pretty("def foo(String bar, String mooky){}"));
+    }
+
+    public void testPlus() throws Exception {
+        assertEquals("a + b", pretty("a+b"));
+    }
+
+    public void testPlusAssign() throws Exception {
+        assertEquals("x += 23", pretty("x += 23"));
+    }
+
+    public void testPostDec() throws Exception {
+        assertEquals("a--", pretty("a--"));
+    }
+
+    public void testPostInc() throws Exception {
+        assertEquals("a++", pretty("a++"));
+    }
+
+    public void testQuestion() throws Exception {
+        assertEquals("foo == bar?10:20", pretty("foo==bar?10:20"));
+        assertEquals("public boolean process(Set<? extends B> a) {println a}", pretty("public boolean process(Set<? extends B> a) {println a}"));
+        assertEquals("public boolean process(Set<? extends B, ? super C> a) {println a}", pretty("public boolean process(Set<? extends B, ? super C> a) {println a}"));
+    }
+
+    public void testRangeExclusive() throws Exception { // not in java
+        assertEquals("foo[45..<89]", pretty("foo[45 ..< 89]")); // not in java
+    }
+
+    public void testRangeInclusive() throws Exception { // not in java
+        assertEquals("foo[bar..12]", pretty("foo[bar .. 12]")); // not in java
+    }
+
+    public void testRegexpLiteral() throws Exception { // not in java
+        assertEquals("println", pretty("println //")); // empty regexp_literal should be treated as single line comment // not in java
+    }
+
+    public void testRegexpLiteral_FAILS() throws Exception {
+        if (notYetImplemented()) return; // not in java
+        //todo: these fail because regexp_literals are converted into string_literals on the antlr AST
+        assertEquals("def x = /./", pretty("def x = /./")); // not in java
+        assertEquals("def z = /blah\\s/", pretty("def z = /blah\\s/")); // actually: def z = /blah\s/ // not in java
+    }
+
+    public void testRegexFind() throws Exception {
+        assertEquals("def m = foo =~ \"bar\"", pretty("def m = foo =~ \"bar\""));
+        assertEquals("if (foo =~ \"bar\") {}", pretty("if (foo=~\"bar\"){}"));
+    }
+
+    public void testRegexMatch() throws Exception {
+        assertEquals("if (foo ==~ \"bar\") {}", pretty("if (foo==~\"bar\"){}"));
+    }
+
+// deprecated
+//    public void testScopeEscape() throws Exception { // not in java
+//        assertEquals("println([$x, x, y])", pretty("println([$x, x, y])")); // fails after parser // not in java
+//    }
+
+    public void testSelectSlot() throws Exception { // not in java
+        assertEquals("def x = foo.@bar", pretty("def x = foo . @ bar")); // not in java
+    }
+
+    public void testSl() throws Exception {
+        assertEquals("foo << 123", pretty("foo << 123"));
+    }
+
+    public void testSlAssign() throws Exception {
+        assertEquals("foo <<= 123", pretty("foo <<= 123")); // does this operator make any sense?
+    }
+
+    public void testSlist() throws Exception {
+        assertEquals("class Foo {private Foo() {println bar}}", pretty("class Foo {private Foo() {println bar}}"));
+
+        assertEquals("if (true) {foo}", pretty("if (true) {foo}"));
+        assertEquals("def x = foo.{bar}", pretty("def x = foo.{bar}")); // todo - inline open block is great, but it doesn't work as one would expect (yet). (c.f. with)
+        assertEquals("def foo() {l:{x = 2}}", pretty("def foo(){l:{x=2}}")); // slist inside a method body (needed label to distinguish from a closure)
+        assertEquals("switch (f) {case 1:break }", pretty("switch(f){case 1:break}")); // slist inside each case body...
+    }
+
+    public void testSpreadArg() throws Exception { // not in java
+        assertEquals("f(*[a, b, c])", pretty("f(*[a,b,c])")); // not in java
+        assertEquals("f(*null)", pretty("f(*null)")); // equiv to f() // not in java
+    }
+
+    public void testSpreadDot() throws Exception { // not in java
+        assertEquals("println([[a:1, b:2], [a:3, b:4]]*.a)", pretty("println([[a:1,b:2],[a:3,b:4]]*.a)"));  // not in java
+    }
+
+    public void testSpreadMapArg() throws Exception { // not in java
+        assertEquals("f(*:myMap)", pretty("f(*:myMap)")); // not in java
+    }
+
+    public void testSr() throws Exception {
+        assertEquals("foo >> 123", pretty("foo >> 123"));
+    }
+
+    public void testSrAssign() throws Exception {
+        assertEquals("foo >>= 123", pretty("foo >>= 123")); // does this operator make any sense?
+    }
+
+    public void testStar() throws Exception {
+        assertEquals("import foo.*", pretty("import foo.*"));
+        assertEquals("a*b", pretty("a*b"));
+    }
+
+    public void testStarAssign() throws Exception {
+        assertEquals("foo *= 123", pretty("foo *= 123"));
+    }
+
+    public void testStarStar() throws Exception { // not in java
+        assertEquals("def square = +5**2", pretty("def square=+5**2")); // not in java
+        assertEquals("def cube = 5**3", pretty("def cube = 5**3")); // not in java
+    }
+
+    public void testStarStarAssign() throws Exception { // not in java
+        assertEquals("cubeMe **= 3", pretty("cubeMe **= 3")); // not in java
+    }
+
+    public void testStaticImport() throws Exception {
+        assertEquals("import static foo.Bar.mooky", pretty("import static foo.Bar.mooky")); // fails after parser
+        assertEquals("import static foo.Bar.*", pretty("import static foo.Bar.*")); // fails after parser
+    }
+
+    public void testStaticInit() throws Exception {
+        assertEquals("class Foo {static {println(1 + 1)}}", pretty("class Foo{static{println(1+1)}}"));
+    }
+
+    public void testStrictFp() throws Exception {
+        assertEquals("private strictfp flibble = 1.2", pretty("private strictfp flibble = 1.2"));
+    }
+
+    public void testStringConstructor() throws Exception { // not in java
+        assertEquals("def x = \"foo$bar\"", pretty("def x=\"foo$bar\"")); // not in java
+        assertEquals("def y = \"foo${mooky}\"", pretty("def y = \"foo${mooky}\"")); // not in java
+    }
+
+    public void testStringLiteral_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        assertEquals("\"mooky\"", pretty("\"mooky\""));
+        assertEquals("'mooky'", pretty("'mooky'"));
+        assertEquals("def x = '''can go over newline'''", pretty("def x = '''can go over newline'''"));
+        assertEquals("def x = \"\"\"can go over newline\"\"\"", pretty("def x = \"\"\"can go over newline\"\"\""));
+        //todo test newlines inside strings somehow...
+    }
+
+    public void testSuperCtorCall() throws Exception {
+        assertEquals("class Foo {Foo(int x) {super(12, 3)}}", pretty("class Foo{Foo(int x) {super(12, 3)}}"));
+        assertEquals("class Foo {Foo(x) {super()}}", pretty("class Foo{Foo(x) {super()}}"));
+        // todo: above is not quite the spacing I would expect, but good enough for now...
+        // todo not yet implemented in parser: assertEquals("(new Outer()).super()", pretty("(new Outer()).super()"));
+    }    
+
+    public void testType() throws Exception {
+        assertEquals("def bar", pretty("def bar"));
+        assertEquals("public bar", pretty("public bar"));
+        assertEquals("public String bar", pretty("public String bar"));
+        assertEquals("String bar", pretty("String bar"));
+    }
+
+    public void testTypecast() throws Exception {
+        assertEquals("foo = (int)bar", pretty("foo = (int)bar"));
+        assertEquals("foo = (int[])bar", pretty("foo = (int[])bar"));
+        assertEquals("foo = (String)bar", pretty("foo = (String)bar"));
+        assertEquals("foo = (String[])bar", pretty("foo = (String[])bar"));
+    }
+
+    public void testTypeArguments() throws Exception {
+        assertEquals("void printCollection(Collection<?> c) {}", pretty("void printCollection(Collection<?> c) {}"));
+    }
+
+    public void testTypeLowerBounds() throws Exception {
+        assertEquals("void printCollection(Collection<? super X> c) {}", pretty("void printCollection(Collection<? super X> c) {}"));
+    }
+
+    public void testTypeUpperBounds() throws Exception {
+        assertEquals("void printCollection(Collection<? extends X> c) {}", pretty("void printCollection(Collection<? extends X> c) {}"));
+    }
+
+    public void testTypeParameters() throws Exception {
+        assertEquals("class Foo<T extends C & I> {T t}", pretty("class Foo<T extends C & I> {T t}")); // fails after parser
+    }
+
+    public void testUnaryMinus() throws Exception {
+        assertEquals("def x = -3", pretty("def x= -3"));
+    }
+
+    public void testUnaryPlus() throws Exception {
+        assertEquals("def x = +2", pretty("def x= +2"));
+    }
+
+    public void testVariableDef() throws Exception {
+        assertEquals("void myMethod(String param1, String... others) {}", pretty("void myMethod(String param1, String ... others) {}"));
+        assertEquals("void myMethod(final int ... others) {}", pretty("void myMethod(final int ... others) {}"));
+        assertEquals("void myMethod(def... others) {}", pretty("void myMethod(def ... others) {}"));
+	}
+
+    public void testVariableDef_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        assertEquals("boolean x, y, z = false", pretty("boolean x,y,z = false"));
+    }
+
+    public void testVaribleParameterDef() throws Exception {
+        assertEquals("void myMethod(String param1, String... others) {}", pretty("void myMethod(String param1, String ... others) {}"));
+        assertEquals("void myMethod(final int ... others) {}", pretty("void myMethod(final int ... others) {}"));
+        assertEquals("void myMethod(def... others) {}", pretty("void myMethod(def ... others) {}"));
+    }
+
+    public void testWildcardType() throws Exception {
+        assertEquals("public boolean process(Set<? extends TypeElement> annotations) {println annotations}", pretty("public boolean process(Set<? extends TypeElement> annotations) {println annotations}"));
+    }
+
+    public String pretty(String input) throws Exception {
+        TraversalTestHelper traverser = new TraversalTestHelper();
+        return traverser.traverse(input, SourcePrinter.class);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/treewalker/TraversalTestHelper.java b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/TraversalTestHelper.java
new file mode 100644
index 0000000..fc37b24
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/TraversalTestHelper.java
@@ -0,0 +1,52 @@
+package org.codehaus.groovy.antlr.treewalker;
+
+import antlr.collections.AST;
+import org.codehaus.groovy.antlr.AntlrASTProcessor;
+import org.codehaus.groovy.antlr.SourceBuffer;
+import org.codehaus.groovy.antlr.UnicodeEscapingReader;
+import org.codehaus.groovy.antlr.parser.GroovyLexer;
+import org.codehaus.groovy.antlr.parser.GroovyRecognizer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.lang.reflect.Constructor;
+
+public class TraversalTestHelper {
+    public String traverse(String input, Class visitorClass) throws Exception {
+        return traverse(input, visitorClass, null);
+    }
+
+
+    // todo - the visitor doesn't always take PrintStreams as constructor params!  Could be a more reusable implementation than this...
+    public String traverse(String input, Class visitorClass, Boolean extraParam) throws Exception {
+        if (!Visitor.class.isAssignableFrom(visitorClass)) {
+            throw new RuntimeException("Invalid class for traversal: " + visitorClass.getName());
+        }
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        GroovyRecognizer parser;
+        SourceBuffer sourceBuffer = new SourceBuffer();
+        UnicodeEscapingReader unicodeReader = new UnicodeEscapingReader(new StringReader(input), sourceBuffer);
+        GroovyLexer lexer = new GroovyLexer(unicodeReader);
+        unicodeReader.setLexer(lexer);
+        parser = GroovyRecognizer.make(lexer);
+        parser.setSourceBuffer(sourceBuffer);
+        String[] tokenNames = parser.getTokenNames();
+        parser.compilationUnit();
+        AST ast = parser.getAST();
+        Class[] paramTypes;
+        Object[] params;
+        if (extraParam == null) {
+            paramTypes = new Class[]{PrintStream.class, String[].class};
+            params = new Object[]{new PrintStream(baos), tokenNames};
+        } else {
+            paramTypes = new Class[]{PrintStream.class, String[].class, Boolean.TYPE};
+            params = new Object[]{new PrintStream(baos), tokenNames, extraParam};
+        }
+        Constructor constructor = visitorClass.getConstructor(paramTypes);
+        Visitor visitor = (Visitor) constructor.newInstance(params);
+        AntlrASTProcessor traverser = new SourceCodeTraversal(visitor);
+        traverser.process(ast);
+        return new String(baos.toByteArray());
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/antlr/treewalker/UnimplementedSyntaxTest.java b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/UnimplementedSyntaxTest.java
new file mode 100644
index 0000000..459dde8
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/antlr/treewalker/UnimplementedSyntaxTest.java
@@ -0,0 +1,251 @@
+/**
+ *
+ * Copyright 2007 Jeremy Rayner
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **/
+package org.codehaus.groovy.antlr.treewalker;
+
+import groovy.lang.GroovyShell;
+import groovy.lang.Script;
+import groovy.util.GroovyTestCase;
+
+/**
+ * This tests code that is valid in parser, but has issues further down the line.
+ *
+ * @author <a href="mailto:groovy@ross-rayner.com">Jeremy Rayner</a>
+ * @version $Revision: 4653 $
+ */
+public class UnimplementedSyntaxTest extends GroovyTestCase {
+    // ------------------------------
+    // feature: Annotation Definition
+    // ------------------------------
+
+    public void test_AnnotationDef1_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unknown type: ANNOTATION_DEF
+        assertNotNull(compile("public @interface Foo{}"));
+    }
+
+    public void test_AnnotationDef2_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unknown type: ANNOTATION_DEF
+        assertNotNull(compile("public @interface Foo{int bar() default 123}"));
+    }
+
+    // ------------------------------
+    // bug: Qualified Exception Types
+    // ------------------------------
+
+    public void test_QualifiedExceptionTypes_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unexpected node type: '.' found when expecting type: an identifier
+        assertNotNull(compile("def foo() throws bar.MookyException{}")); // fails after parser
+    }
+
+    // ------------------------------
+    // feature: classic Java for loop
+    // ------------------------------
+
+    public void test_ClassicJavaForLoop1_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // For statement contains unexpected tokens. Possible attempt to use unsupported Java-style for loop.
+        // This syntax now replaced with closure list i.e. for (i=0;j=2;i<10;i++;j--) {...
+        assertNotNull(compile("for (i = 0,j = 2;i < 10; i++, j--) {print i}")); // fails after parser
+    }
+
+    public void test_ClassicJavaForLoop2() throws Exception {
+        // For statement contains unexpected tokens. Possible attempt to use unsupported Java-style for loop.
+        assertNotNull(compile("for (i=0;i<10;i++) {println i}")); // fails after parser
+    }
+
+    // ------------------------------
+    // feature: Enum Definitions
+    // ------------------------------
+    public void test_EnumDef1_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unknown type: ENUM_DEF
+        assertNotNull(compile("enum Coin {PENNY(1), DIME(10), QUARTER(25)}")); // fails after parser
+    }
+
+    public void test_EnumDef2_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unknown type: ENUM_DEF
+        assertNotNull(compile("enum Season{WINTER,SPRING,SUMMER,AUTUMN}")); // fails after parser
+    }
+
+    public void test_EnumDef3_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unknown type: ENUM_DEF
+        assertNotNull(compile("enum Operation {ADDITION {double eval(x,y) {return x + y}}}")); // fails after parser
+    }
+
+    public void test_EnumDef4_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unknown type: ENUM_DEF
+        assertNotNull(compile("enum EarthSeason implements Season{SPRING}")); // fails after parser
+    }
+
+    // ------------------------------------------------
+    // deprecate in parser?: 'break' allowed in methods
+    // ------------------------------------------------
+    public void test_BreakAllowedInMethods_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // the break statement is only allowed inside loops or switches
+        assertNotNull(compile("def myMethod(){break}")); // fails after parser
+    }
+
+    // ----------------------------------------------------
+    // deprecate in parser?: 'continue' allowed in closures
+    // ----------------------------------------------------
+    public void test_ContinueAllowedInClosures_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // the continue statement is only allowed inside loops
+        assertNotNull(compile("[1,2,3].each{continue}")); // fails after parser
+    }
+
+    // ----------------------------------------------------
+    // feature?: allow break/continue with value from loops?
+    // ----------------------------------------------------
+    public void test_BreakWithValueAllowedInLoops_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unexpected node type: a numeric literal found when expecting type: an identifier
+        assertNotNull(compile("for (i in 1..100) {break 2}")); // fails after parser
+    }
+
+    public void test_ContinueWithValueAllowedInLoops_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unexpected node type: a numeric literal found when expecting type: an identifier
+        assertNotNull(compile("for (i in 1..100) {continue 2}")); // fails after parser
+    }
+
+    // ---------------------------------------------------------------
+    // feature?: allow break/continue to labeled statement from loops? (is this even right syntax, or parser bug???)
+    // ---------------------------------------------------------------
+    public void test_BreakToLabeledStatementAllowedInLoops_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unexpected node type: LABELED_STAT found when expecting type: an identifier
+        assertNotNull(compile("for (i in 1..100) {break label1:}")); // fails after parser
+    }
+
+    public void test_ContinueToLabeledStatementAllowedInLoops_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unexpected node type: LABELED_STAT found when expecting type: an identifier
+        assertNotNull(compile("for (i in 1..100) {continue label1:}")); // fails after parser
+    }
+
+    // -----------------------
+    // feature: Native Methods
+    // -----------------------
+    public void test_NativeMethods1_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // You defined a method without body. Try adding a body, or declare it abstract
+        assertNotNull(compile("public class R{public native void seek(long pos)}")); // fails after parser
+    }
+
+    public void test_NativeMethods2_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // You defined a method without body. Try adding a body, or declare it abstract
+        assertNotNull(compile("native foo()")); // fails after parser
+    }
+
+    // ---------------------
+    // feature: 'threadsafe'
+    // ---------------------
+    public void test_Threadsafe1_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unknown type: "threadsafe"
+        assertNotNull(compile("threadsafe foo() {}")); // fails after parser
+    }
+
+    public void test_Threadsafe2_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unknown type: "threadsafe"
+        assertNotNull(compile("public static transient final native threadsafe synchronized volatile strictfp foo() {}")); // fails after parser
+    }
+
+    // -------------------------------
+    // feature?: scope escape operator
+    // -------------------------------
+    public void test_ScopeEscape_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unknown type: SCOPE_ESCAPE
+        assertNotNull(compile("println([$x, x, y])")); // fails after parser
+    }
+
+    // --------------------------------------------------
+    // bugs?: spread expressions in closures and GStrings
+    // --------------------------------------------------
+    public void test_SpreadExpressionInClosure_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // BUG! exception in phase 'class generation' in source unit 'Script1.groovy'
+        // SpreadExpression should not be visited here
+        assertNotNull(compile("myList{*name}")); // fails after parser
+    }
+
+    public void test_SpreadExpressionInGString1_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // BUG! exception in phase 'conversion' in source unit 'Script1.groovy' null
+        assertNotNull(compile("\"foo$*bar\"")); // fails after parser
+    }
+
+    public void test_SpreadExpressionInGString2_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // BUG! exception in phase 'class generation' in source unit 'Script1.groovy'
+        // SpreadExpression should not be visited here
+        assertNotNull(compile("\"foo${*bar}\"")); // fails after parser
+    }
+
+    // -----------------------
+    // feature: static imports
+    // -----------------------
+    // TODO: move somewhere else
+    public void test_StaticImport1() throws Exception {
+        assertNotNull(compile("import static foo.Bar.mooky"));
+    }
+
+    public void test_StaticImport2() throws Exception {
+        assertNotNull(compile("import static foo.Bar.*"));
+    }
+
+    // TODO: move somewhere else GROOVY-1874
+    public void test_staticBlockWithNewLines() throws Exception {
+        assertNotNull(compile("class MyClass \n{\n\tstatic\n{\nprintln 2\n}\n}"));
+    }
+    
+    public void test_staticBlockWithNoStartNewLines() throws Exception {
+        assertNotNull(compile("class MyClass \n{\n\tstatic {\nprintln 2\n}\n}"));
+    }
+    
+    public void test_staticBlockWithNoNewLines() throws Exception {
+        assertNotNull(compile("class MyClass \n{\n\tstatic { println 2 }}"));
+    }
+    
+    // ------------------------
+    // feature: type parameters
+    // ------------------------
+    public void test_TypeParameters_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+        // Unexpected node type: TYPE_PARAMETERS found when expecting type: OBJBLOCK
+        assertNotNull(compile("class Foo<T extends C & I> {T t}")); // fails after parser
+    }
+
+    private Script compile(String input) throws Exception {
+        TraversalTestHelper traverser = new TraversalTestHelper();
+        traverser.traverse(input, SourcePrinter.class, Boolean.FALSE);
+        GroovyShell groovyShell = new GroovyShell();
+        return groovyShell.parse(input);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/ast/ClassNodeTest.java b/groovy/src/test/org/codehaus/groovy/ast/ClassNodeTest.java
new file mode 100644
index 0000000..c50da84
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/ast/ClassNodeTest.java
@@ -0,0 +1,82 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package org.codehaus.groovy.ast;
+
+import junit.framework.TestCase;
+import org.objectweb.asm.Opcodes;
+
+/**
+ * Tests the ClassNode
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ClassNodeTest extends TestCase implements Opcodes {
+
+    ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+    ClassNode innerClassNode = new InnerClassNode(classNode, "Foo$1", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+
+    protected void setUp() throws Exception {
+        classNode.addField("field", ACC_PUBLIC, ClassHelper.STRING_TYPE, null);
+    }
+
+    public void testOuterClass() {
+        assertNull(classNode.getOuterClass());
+        assertNotNull(innerClassNode.getOuterClass());
+    }
+
+    public void testOuterField() {
+        assertNull(classNode.getOuterField("field"));
+        assertNotNull(innerClassNode.getOuterField("field"));
+    }
+
+    public void testPackageName() {
+        assertEquals("Package", null, classNode.getPackageName());
+
+        ClassNode packageNode = new ClassNode("com.acme.Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        assertEquals("Package", "com.acme", packageNode.getPackageName());
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/ast/ModuleNodeTest.java b/groovy/src/test/org/codehaus/groovy/ast/ModuleNodeTest.java
new file mode 100644
index 0000000..26055bb
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/ast/ModuleNodeTest.java
@@ -0,0 +1,74 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package org.codehaus.groovy.ast;
+
+import org.codehaus.groovy.syntax.parser.TestParserSupport;
+
+import java.util.List;
+
+/**
+ * Tests the ClassNode
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ModuleNodeTest extends TestParserSupport {
+
+    public void testStatementClass_FAILS() throws Exception {
+        if (notYetImplemented()) return;
+
+        ModuleNode module = parse("x = [1, 2, 3]; println(x)", "Cheese.groovy");
+
+        assertTrue("Should have statements", !module.getStatementBlock().isEmpty());
+
+        List classes = module.getClasses();
+        assertEquals("Number of classes", 1, classes.size());
+
+        ClassNode classNode = (ClassNode) classes.get(0);
+
+        assertEquals("Class name", "Cheese", classNode.getName());
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/bsf/BSFTest.java b/groovy/src/test/org/codehaus/groovy/bsf/BSFTest.java
new file mode 100644
index 0000000..1094876
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/bsf/BSFTest.java
@@ -0,0 +1,211 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package org.codehaus.groovy.bsf;
+
+import junit.framework.TestCase;
+import org.apache.bsf.BSFEngine;
+import org.apache.bsf.BSFException;
+import org.apache.bsf.BSFManager;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.io.File;
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * Tests the BSF integration
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Paul King
+ * @version $Revision$
+ */
+public class BSFTest extends TestCase {
+
+    protected BSFManager manager;
+
+    protected void setUp() throws Exception {
+        manager = new BSFManager();
+    }
+
+    public void testInvalidName() throws Exception {
+        manager.exec("groovy", null, 0, 0, "assert bsf != null");
+        manager.exec("groovy", "", 0, 0, "assert bsf != null");
+        manager.exec("groovy", "-", 0, 0, "assert bsf != null");
+    }
+
+    public void testCompileErrorWithExec() throws Exception {
+        try {
+            manager.exec("groovy", "dummy", 0, 0, "assert assert");
+            fail("Should have caught compile exception");
+        } catch (BSFException e) {
+            assertTrue("e.getMessage() should contain CompilationError: " + e.getMessage(),
+                    e.getMessage().indexOf("CompilationError") != -1);
+        }
+    }
+
+    public void testCompileErrorWithEval() throws Exception {
+        try {
+            manager.eval("groovy", "dummy", 0, 0, "assert assert");
+            fail("Should have caught compile exception");
+        } catch (BSFException e) {
+            assertTrue("e.getMessage() should contain CompilationError: " + e.getMessage(),
+                    e.getMessage().indexOf("CompilationError") != -1);
+        }
+    }
+
+    public void testExec() throws Exception {
+        manager.exec("groovy", "Test1.groovy", 0, 0, "assert bsf != null , 'should have a bsf variable'");
+    }
+
+    public void testApplyWithClosure() throws Exception {
+        Vector ignoreParamNames = null;
+        Vector ignoreArgs = null;
+        Integer actual = (Integer) manager.apply("groovy", "applyTest", 0, 0,
+                "251", ignoreParamNames, ignoreArgs);
+        assertEquals(251, actual.intValue());
+    }
+
+    public void testApply() throws Exception {
+        Vector ignoreParamNames = null;
+        Vector args = new Vector();
+        args.add(new Integer(2));
+        args.add(new Integer(5));
+        args.add(new Integer(1));
+        Integer actual = (Integer) manager.apply("groovy", "applyTest", 0, 0,
+                "def summer = { a, b, c -> a * 100 + b * 10 + c }", ignoreParamNames, args);
+        assertEquals(251, actual.intValue());
+    }
+
+    public void testBracketName() throws Exception {
+        manager.exec("groovy", "Test1<groovy>", 0, 0, "assert bsf != null , 'should have a bsf variable'");
+    }
+
+    public void testEval() throws Exception {
+        Object answer = manager.eval("groovy", "Test1.groovy", 0, 0, "return [1, 2, 3]");
+        assertTrue("Should return a list: " + answer, answer instanceof List);
+        List list = (List) answer;
+        assertEquals("List should be of right size", 3, list.size());
+    }
+
+    public void testTwoEvalsWithSameName() throws Exception {
+        Object answer = manager.eval("groovy", "Test1.groovy", 0, 0, "return 'cheese'");
+        assertEquals("cheese", answer);
+        answer = manager.eval("groovy", "Test1.groovy", 0, 0, "return 'gromit'");
+        assertEquals("gromit", answer);
+    }
+
+    public void testExecBug() throws Exception {
+        for (int i = 0; i < 10; i++) {
+            manager.exec("groovy", "Test1.groovy", 0, 0, "assert true");
+            manager.exec("groovy", "Test1.groovy", 0, 0, "assert true");
+        }
+    }
+
+    public void testBsfVariables() throws Exception {
+        Object answer = manager.eval("groovy", "Test1.groovy", 0, 0,
+                "assert this.bsf != null\n  return this.bsf");
+        assertTrue("Should have an answer", answer != null);
+    }
+
+    public void testNotFoundVariables() throws Exception {
+        manager.registerBean("x", new Integer(4));
+        Object answer = manager.eval("groovy", "Test1.groovy", 0, 0,
+                "def valueOfX = this.bsf.lookupBean('y'); assert valueOfX == null");
+        assertNull("Undeclared beans should yield null", answer);
+    }
+
+    public void testRegisteredVariables() throws Exception {
+        manager.registerBean("x", new Integer(4));
+        Object answer = manager.eval("groovy", "Test1.groovy", 0, 0,
+                "def valueOfX = this.bsf.lookupBean('x'); assert valueOfX == 4; valueOfX + 1");
+        assertEquals("Incorrect return", new Integer(5), answer);
+    }
+
+    public void testUnregisteredVariables() throws Exception {
+        manager.registerBean("x", new Integer(4));
+        Object answer = manager.eval("groovy", "Test1.groovy", 0, 0,
+                "def valueOfX = this.bsf.lookupBean('x'); assert valueOfX == 4; valueOfX + 1");
+        assertEquals("Incorrect return", new Integer(5), answer);
+        manager.unregisterBean("x");
+        // have to lookup registered beans
+        answer = manager.eval("groovy", "Test1.groovy", 0, 0,
+                "def valueOfX = this.bsf.lookupBean('x'); assert valueOfX == null");
+        assertNull("Unregistered beans should yield null", answer);
+    }
+
+    public void testDeclaredVariables() throws Exception {
+        manager.declareBean("xyz", new Integer(4), Integer.class);
+        Object answer = manager.eval("groovy", "Test1.groovy", 0, 0, "xyz + 1");
+        assertEquals("Incorrect return", new Integer(5), answer);
+    }
+
+    public void testUndeclaredVariables() throws Exception {
+        manager.declareBean("abc", new Integer(4), Integer.class);
+        // declared beans should just be available
+        Object answer = manager.eval("groovy", "Test1.groovy", 0, 0, "abc + 1");
+        assertEquals("Incorrect return", new Integer(5), answer);
+        manager.undeclareBean("abc");
+        answer = manager.eval("groovy", "Test1.groovy", 0, 0, "abc");
+        assertNull("Undeclared beans should yield null", answer);
+    }
+
+    public void testCall() throws Exception {
+        BSFEngine bsfEngine = manager.loadScriptingEngine("groovy");
+        manager.declareBean("myvar", "hello", String.class);
+        Object myvar = manager.lookupBean("myvar");
+        String result = (String) bsfEngine.call(myvar, "reverse", new Object[]{});
+        assertEquals("olleh", result);
+    }
+
+    public void testExecFile() throws Exception {
+        execScript("src/test/groovy/script/MapFromList.groovy");
+        execScript("src/test/groovy/script/AtomTestScript.groovy");
+    }
+
+    protected void execScript(String fileName) throws Exception {
+        manager.exec("groovy", fileName, 0, 0, DefaultGroovyMethods.getText(new File(fileName)));
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/bsf/CacheBSFTest.java b/groovy/src/test/org/codehaus/groovy/bsf/CacheBSFTest.java
new file mode 100644
index 0000000..262808a
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/bsf/CacheBSFTest.java
@@ -0,0 +1,137 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+ * 
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met: 1. Redistributions of source code must retain
+ * copyright statements and notices. Redistributions must also contain a copy
+ * of this document. 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution. 3.
+ * The name "groovy" must not be used to endorse or promote products derived
+ * from this Software without prior written permission of The Codehaus. For
+ * written permission, please contact info@codehaus.org. 4. Products derived
+ * from this Software may not be called "groovy" nor may "groovy" appear in
+ * their names without prior written permission of The Codehaus. "groovy" is a
+ * registered trademark of The Codehaus. 5. Due credit should be given to The
+ * Codehaus - http://groovy.codehaus.org/
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *  
+ */
+package org.codehaus.groovy.bsf;
+
+import junit.framework.TestCase;
+import org.apache.bsf.BSFEngine;
+import org.apache.bsf.BSFException;
+import org.apache.bsf.BSFManager;
+
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * Tests the Caching BSF integration
+ *
+ * @author James Birchfield
+ * @version $Revision$
+ */
+public class CacheBSFTest extends TestCase {
+
+    protected BSFManager manager;
+    private static final Class CACHING_ENGINE = CachingGroovyEngine.class;
+
+    protected void setUp() throws Exception {
+        // override standard engine with caching one
+        BSFManager.registerScriptingEngine("groovy", CACHING_ENGINE.getName(), new String[]{"groovy", "gy"});
+        manager = new BSFManager();
+    }
+
+    public void testVersion() throws Exception {
+        //System.out.println("BSFManager.getVersion() = " + BSFManager.getVersion());
+        BSFEngine bsfEngine = manager.loadScriptingEngine("groovy");
+        assertEquals(CACHING_ENGINE, bsfEngine.getClass());
+    }
+
+    public void testExec() throws Exception {
+        manager.exec("groovy", "Test1.groovy", 0, 0, "println('testing Exec')");
+        //nothing to really test here...just looking for debug that says it
+        // used cache version
+        manager.exec("groovy", "Test1.groovy", 0, 0, "println('testing Exec')");
+    }
+
+    public void testCompileErrorWithExec() throws Exception {
+        try {
+            manager.exec("groovy", "dummy", 0, 0, "assert assert");
+            fail("Should have caught compile exception");
+        } catch (BSFException e) {
+            assertTrue("e.getMessage() should contain CompilationError: " + e.getMessage(),
+                    e.getMessage().indexOf("CompilationError") != -1);
+        }
+    }
+
+    public void testEval() throws Exception {
+        Object dontcare = manager.eval("groovy", "Test1.groovy", 0, 0, "return [1, 2, 3]");
+        // nothing to really test here...just looking for debug that says it
+        // used cache version
+        Object answer = manager.eval("groovy", "Test.groovy", 0, 0, "return [1, 2, 3]");
+        assertTrue("Should return a list: " + answer, answer instanceof List);
+        List list = (List) answer;
+        assertEquals("List should be of right size", 3, list.size());
+    }
+
+    public void testCompileErrorWithEval() throws Exception {
+        try {
+            manager.eval("groovy", "dummy", 0, 0, "assert assert");
+            fail("Should have caught compile exception");
+        } catch (BSFException e) {
+            assertTrue("e.getMessage() should contain CompilationError: " + e.getMessage(),
+                    e.getMessage().indexOf("CompilationError") != -1);
+        }
+    }
+
+    public void testBuiltInVariable() throws Exception {
+        Object answer = manager.eval("groovy", "Test1.groovy", 0, 0,
+                "assert this.bsf != null\n  return this.bsf");
+        assertTrue("Should have an answer", answer != null);
+    }
+
+    public void testVariables() throws Exception {
+        manager.registerBean("x", new Integer(4));
+        Object dontcare = manager.eval("groovy", "Test1.groovy", 0, 0,
+                "valueOfX = this.bsf.lookupBean('x'); assert valueOfX == 4; valueOfX + 1");
+        // nothing to really test here...just looking for debug that says it
+        // used cache version
+        Object answer = manager.eval("groovy", "Test2.groovy", 0, 0,
+                "valueOfX = this.bsf.lookupBean('x'); assert valueOfX == 4; valueOfX + 1");
+        assertEquals("Incorrect return", new Integer(5), answer);
+    }
+
+    public void testClassLoaderSet() throws BSFException {
+        CachingGroovyEngine cachingGroovyEngine = new CachingGroovyEngine();
+        manager.setClassLoader(null);
+        cachingGroovyEngine.initialize(manager, "dummy", new Vector());
+        // still working implies classloader set, coverage confirms this
+        assertEquals("hi", manager.eval("groovy", "dummy", 0, 0, "'hi'"));
+    }
+
+    public void testDeclaredVariables() throws Exception {
+        manager.declareBean("foo", new Integer(5), Integer.class);
+        Object answer = manager.eval("groovy", "Test1.groovy", 0, 0, "valueOfFoo = foo; return valueOfFoo");
+        assertEquals(new Integer(5), answer);
+        manager.declareBean("foo", new Integer(6), Integer.class);
+        answer = manager.eval("groovy", "Test2.groovy", 0, 0, "valueOfFoo = foo; return valueOfFoo");
+        assertEquals(new Integer(6), answer);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/BytecodeHelperTest.java b/groovy/src/test/org/codehaus/groovy/classgen/BytecodeHelperTest.java
new file mode 100644
index 0000000..bca3d81
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/BytecodeHelperTest.java
@@ -0,0 +1,72 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.ast.ClassHelper;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class BytecodeHelperTest extends GroovyTestCase {
+
+    public void testTypeName() {
+        assertEquals("[C", BytecodeHelper.getTypeDescription(ClassHelper.char_TYPE.makeArray()));
+    }
+
+    public void testMethodDescriptor() {
+        String answer = BytecodeHelper.getMethodDescriptor(char[].class, new Class[0]);
+        assertEquals("()[C", answer);
+
+        answer = BytecodeHelper.getMethodDescriptor(int.class, new Class[]{long.class});
+        assertEquals("(J)I", answer);
+
+        answer = BytecodeHelper.getMethodDescriptor(String[].class, new Class[]{String.class, int.class});
+        assertEquals("(Ljava/lang/String;I)[Ljava/lang/String;", answer);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/CallClosureFieldAsMethodTest.groovy b/groovy/src/test/org/codehaus/groovy/classgen/CallClosureFieldAsMethodTest.groovy
new file mode 100644
index 0000000..8e0a6a3
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/CallClosureFieldAsMethodTest.groovy
@@ -0,0 +1,36 @@
+package org.codehaus.groovy.classgen
+
+import groovy.util.GroovyTestCase
+
+/**
+ * @author Guillaume Laforge
+ */
+class CallClosureFieldAsMethodTest extends GroovyTestCase {
+
+    String firstname = "Guillaume"
+    def closureMethod = { greeting-> "${greeting} ${firstname}" }
+    public static staticClosureMethod = {it}
+
+    /**
+     * Check that we can call a closure defined as a field as if it were a normal method
+     */
+    void testCallToClosureAsMethod() {
+        def obj = new CallClosureFieldAsMethodTest()
+        assert obj.closureMethod("Hello") == "Hello Guillaume"
+    }
+    
+    void testCallToClosureAsMethodFromStaticField() {
+        assert CallClosureFieldAsMethodTest.staticClosureMethod("Hello") == "Hello"
+    }
+    
+    void testEnsureCallMethodIsUsed() {
+      assertScript """
+        class Dummy {
+            def call(Object arguments) {"1"}
+        }
+        def c = new Dummy()
+        assert c(2) == "1"      
+      """    
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/CapitalizeTest.java b/groovy/src/test/org/codehaus/groovy/classgen/CapitalizeTest.java
new file mode 100644
index 0000000..c79ef5e
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/CapitalizeTest.java
@@ -0,0 +1,65 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.util.GroovyTestCase;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class CapitalizeTest extends GroovyTestCase {
+
+    public void testCapitalize() {
+        assertEquals("Foo", Verifier.capitalize("foo"));
+        assertEquals("Foo", Verifier.capitalize("Foo"));
+        assertEquals("FOo", Verifier.capitalize("fOo"));
+        assertEquals("FOO", Verifier.capitalize("fOO"));
+        assertEquals("F", Verifier.capitalize("f"));
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/ClassCompletionVerifierTest.java b/groovy/src/test/org/codehaus/groovy/classgen/ClassCompletionVerifierTest.java
new file mode 100644
index 0000000..eb4ed24
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/ClassCompletionVerifierTest.java
@@ -0,0 +1,126 @@
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * @author Paul King
+ */
+public class ClassCompletionVerifierTest extends TestSupport {
+    private SourceUnit source;
+    private ClassCompletionVerifier verifier;
+    private static final String ABSTRACT_FINAL_CLASS = "AbstractFinalClass";
+    private static final String FINAL_INTERFACE = "FinalInterface";
+    private static final String EXPECTED_CLASS_MODIFIER_ERROR_MESSAGE =
+            "The class '" + ABSTRACT_FINAL_CLASS + "' must not be both final and abstract.";
+    private static final String EXPECTED_INTERFACE_MODIFIER_ERROR_MESSAGE =
+            "The interface '" + FINAL_INTERFACE + "' must not be final. It is by definition abstract.";
+    private static final String EXPECTED_INTERFACE_FINAL_METHOD_ERROR_MESSAGE =
+            "The method 'java.lang.Object xxx()' from interface 'zzz' must not be final. It is by definition abstract.";
+    private static final String EXPECTED_INTERFACE_STATIC_METHOD_ERROR_MESSAGE =
+            "The method 'java.lang.Object yyy()' from interface 'zzz' must not be static. Only fields may be static in an interface.";
+    private static final String EXPECTED_TRANSIENT_CLASS_ERROR_MESSAGE =
+            "The class 'DodgyClass' has an incorrect modifier transient.";
+    private static final String EXPECTED_VOLATILE_CLASS_ERROR_MESSAGE =
+            "The class 'DodgyClass' has an incorrect modifier volatile.";
+    private static final String EXPECTED_DUPLICATE_METHOD_ERROR_CLASS_MESSAGE =
+            "Repetitive method name/signature for method 'java.lang.Object xxx()' in class 'zzz'.";
+    private static final String EXPECTED_DUPLICATE_METHOD_ERROR_INTERFACE_MESSAGE =
+            "Repetitive method name/signature for method 'java.lang.Object xxx(java.lang.String)' in interface 'zzz'.";
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        source = SourceUnit.create("dummy.groovy", "");
+        verifier = new ClassCompletionVerifier(source);
+    }
+
+    public void testDetectsFinalAbstractClass() throws Exception {
+        checkVisitErrors("FinalClass", ACC_FINAL, false);
+        checkVisitErrors("AbstractClass", ACC_ABSTRACT, false);
+        checkVisitErrors(ABSTRACT_FINAL_CLASS, ACC_ABSTRACT | ACC_FINAL, true);
+        checkErrorMessage(EXPECTED_CLASS_MODIFIER_ERROR_MESSAGE);
+    }
+
+    public void testDetectsDuplicateMethodsForClassNoParams() throws Exception {
+        checkDetectsDuplicateMethods(0, EXPECTED_DUPLICATE_METHOD_ERROR_CLASS_MESSAGE, Parameter.EMPTY_ARRAY);
+    }
+
+    public void testDetectsDuplicateMethodsForInterfaceOneParam() throws Exception {
+        Parameter[] stringParam = {new Parameter(ClassHelper.STRING_TYPE, "x")};
+        checkDetectsDuplicateMethods(ACC_INTERFACE, EXPECTED_DUPLICATE_METHOD_ERROR_INTERFACE_MESSAGE, stringParam);
+    }
+
+    private void checkDetectsDuplicateMethods(int modifiers, String expectedErrorMessage, Parameter[] params) {
+        ClassNode node = new ClassNode("zzz", modifiers, ClassHelper.OBJECT_TYPE);
+        node.addMethod(new MethodNode("xxx", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, params, ClassNode.EMPTY_ARRAY, null));
+        node.addMethod(new MethodNode("xxx", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, params, ClassNode.EMPTY_ARRAY, null));
+        verifier.visitClass(node);
+        checkErrorCount(2);
+        checkErrorMessage(expectedErrorMessage);
+    }
+
+    public void testDetectsIncorrectOtherModifier() throws Exception {
+        checkVisitErrors("DodgyClass", ACC_TRANSIENT | ACC_VOLATILE, true);
+        checkErrorMessage(EXPECTED_TRANSIENT_CLASS_ERROR_MESSAGE);
+        checkErrorMessage(EXPECTED_VOLATILE_CLASS_ERROR_MESSAGE);
+    }
+
+    public void testDetectsFinalAbstractInterface() throws Exception {
+        checkVisitErrors(FINAL_INTERFACE, ACC_ABSTRACT | ACC_FINAL | ACC_INTERFACE, true);
+        checkErrorMessage(EXPECTED_INTERFACE_MODIFIER_ERROR_MESSAGE);
+    }
+
+    public void testDetectsFinalAndStaticMethodsInInterface() throws Exception {
+        ClassNode node = new ClassNode("zzz", ACC_ABSTRACT | ACC_INTERFACE, ClassHelper.OBJECT_TYPE);
+        node.addMethod(new MethodNode("xxx", ACC_PUBLIC | ACC_FINAL, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null));
+        node.addMethod(new MethodNode("yyy", ACC_PUBLIC | ACC_STATIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null));
+        // constructors should not be treated as errors (they have no real meaning for interfaces anyway)
+        node.addMethod(new MethodNode("<clinit>", ACC_PUBLIC | ACC_STATIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, null));
+        verifier.visitClass(node);
+        checkErrorCount(2);
+        checkErrorMessage(EXPECTED_INTERFACE_FINAL_METHOD_ERROR_MESSAGE);
+        checkErrorMessage(EXPECTED_INTERFACE_STATIC_METHOD_ERROR_MESSAGE);
+    }
+
+    private void checkErrorCount(int count) {
+        assertEquals(buildErrorMessage(count), count, source.getErrorCollector().getErrorCount());
+    }
+
+    private String buildErrorMessage(int count) {
+        StringBuffer sb = new StringBuffer();
+        sb.append("Expected ").append(count);
+        sb.append(" error messages but found ");
+        sb.append(source.getErrorCollector().getErrorCount()).append(":\n");
+        sb.append(flattenErrorMessage());
+        return sb.toString();
+    }
+
+    private void checkVisitErrors(String name, int modifiers, boolean expectedToFail) {
+        ClassNode node = new ClassNode(name, modifiers, ClassHelper.OBJECT_TYPE);
+        verifier.visitClass(node);
+        assertTrue(source.getErrorCollector().hasErrors() == expectedToFail);
+    }
+
+    private void checkErrorMessage(String expectedErrorMessage) {
+        assertTrue("Expected an error message but none found.", source.getErrorCollector().hasErrors());
+        assertTrue("Expected message to contain <" + expectedErrorMessage +
+                "> but was <" + flattenErrorMessage() + ">.",
+                flattenErrorMessage().indexOf(expectedErrorMessage) != -1);
+    }
+
+    private String flattenErrorMessage() {
+        StringWriter stringWriter = new StringWriter();
+        PrintWriter writer = new PrintWriter(stringWriter, true);
+        for (int i = source.getErrorCollector().getErrorCount() - 1; i >= 0; i--) {
+            source.getErrorCollector().getError(i).write(writer);
+        }
+        writer.close();
+        return stringWriter.toString();
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/ConstructorIssueTest.groovy b/groovy/src/test/org/codehaus/groovy/classgen/ConstructorIssueTest.groovy
new file mode 100644
index 0000000..14cd789
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/ConstructorIssueTest.groovy
@@ -0,0 +1,35 @@
+package org.codehaus.groovy.classgen
+
+import groovy.bugs.TestSupport
+
+class ConstructorIssueTest extends TestSupport {
+    
+    ConstructorIssueTest() {
+        //println("Created test case!")
+    }
+
+    ConstructorIssueTest(String[] args) {
+        //println("Created test case!")
+    }
+
+    static void main(args) {
+        //println("in main() - called with ${array}")
+        
+        def foo = new ConstructorIssueTest()
+        foo.done()
+
+        //System.out.println("Done");
+    }
+    
+    void done() {
+        println("Yeah, I've been made")
+    }
+    
+    void testConstructorIssue() {
+        def array = getMockArguments()
+
+        main(array)
+
+        new ConstructorIssueTest(array).done()
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/ConstructorTest.java b/groovy/src/test/org/codehaus/groovy/classgen/ConstructorTest.java
new file mode 100644
index 0000000..ad04658
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/ConstructorTest.java
@@ -0,0 +1,61 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyObject;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ConstructorTest extends TestSupport {
+
+    public void testConstructor() throws Exception {
+        GroovyObject object = compile("src/test/groovy/NewExpressionTest.groovy");
+        object.invokeMethod("testNewInstance", null);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/DerivedBean.java b/groovy/src/test/org/codehaus/groovy/classgen/DerivedBean.java
new file mode 100644
index 0000000..5ec6f4f
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/DerivedBean.java
@@ -0,0 +1,67 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+
+/**
+ * A simple bean
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DerivedBean extends org.codehaus.groovy.runtime.DummyBean {
+
+    private String bar;
+
+    public String getBar() {
+        return bar;
+    }
+
+    public void setBar(String value) {
+        this.bar = value;
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/DummyReflector.java b/groovy/src/test/org/codehaus/groovy/classgen/DummyReflector.java
new file mode 100644
index 0000000..620066f
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/DummyReflector.java
@@ -0,0 +1,102 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+ * 
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met:
+ *  1. Redistributions of source code must retain copyright statements and
+ * notices. Redistributions must also contain a copy of this document.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *  3. The name "groovy" must not be used to endorse or promote products
+ * derived from this Software without prior written permission of The Codehaus.
+ * For written permission, please contact info@codehaus.org.
+ *  4. Products derived from this Software may not be called "groovy" nor may
+ * "groovy" appear in their names without prior written permission of The
+ * Codehaus. "groovy" is a registered trademark of The Codehaus.
+ *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *  
+ */
+
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.runtime.Reflector;
+import org.codehaus.groovy.reflection.CachedMethod;
+
+/**
+ * This is a scratch class used to experiment with ASM to see what kind of
+ * stuff is output for normal Java code
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DummyReflector extends Reflector {
+
+    public DummyReflector() {
+    }
+
+    /*
+    public Object invoke(MetaMethod method, Object object, Object[] arguments) {
+        switch (method.getMethodIndex()) {
+            case 1 :
+                return InvokerHelper.toObject(object.hashCode());
+            case 2 :
+                return object.toString();
+            case 3 :
+                return InvokerHelper.toObject(object.equals(arguments[0]));
+            case 4 :
+                return new ObjectRange((Comparable) arguments[0], (Comparable) arguments[1]);
+            case 5 :
+                return ((String) object).toCharArray();
+            case 7 :
+                return new Character("hello".charAt(2));
+            case 8 :
+                return null;
+            default :
+                return noSuchMethod(method, object, arguments);
+        }
+    }
+    */
+
+    public Object invoke(CachedMethod method, Object object, Object[] arguments) {
+/*
+        switch (method.getMethodIndex()) {
+            case 1:
+                return ((String) object).toCharArray();
+            case 2:
+                return new Boolean(((List) object).contains(arguments[0]));
+            default:
+                return noSuchMethod(method, object, arguments);
+        }
+*/
+        return null;
+    }
+
+    public Object invokeConstructorAt(Object at, Object constructor, Object[] arguments) {
+        return null; // noSuchMethod(method, object, arguments);
+    }
+
+    public Object invokeConstructorOf(Object constructor, Object[] arguments) {
+        return null; // noSuchMethod(method, object, arguments);
+    }
+
+    char[] blah() {
+        return "foo".toCharArray();
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/DummyTestDerivation.java b/groovy/src/test/org/codehaus/groovy/classgen/DummyTestDerivation.java
new file mode 100644
index 0000000..0aa1499
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/DummyTestDerivation.java
@@ -0,0 +1,60 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.util.GroovyTestCase;
+
+/**
+ * A dummy test case
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DummyTestDerivation extends GroovyTestCase {
+    public DummyTestDerivation() {
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/DumpingClassLoader.java b/groovy/src/test/org/codehaus/groovy/classgen/DumpingClassLoader.java
new file mode 100644
index 0000000..a94e2ff
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/DumpingClassLoader.java
@@ -0,0 +1,112 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyClassLoader;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CompileUnit;
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.control.SourceUnit;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.util.ASMifierClassVisitor;
+import org.objectweb.asm.util.CheckClassAdapter;
+
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * A class loader used for debugging the bytecode generation.
+ * This will log all bytecode being loaded
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class DumpingClassLoader extends GroovyClassLoader implements Opcodes {
+
+    protected static boolean CHECK_CLASS = false;
+    protected static boolean DUMP_CLASS = true;
+
+    public DumpingClassLoader(ClassLoader parentLoader) {
+        super(parentLoader);
+    }
+
+
+    protected class DebugCollector extends ClassCollector {
+
+        DebugCollector(GroovyClassLoader cl, CompilationUnit unit, SourceUnit su) {
+            super(new GroovyClassLoader.InnerLoader(cl), unit, su);
+        }
+
+        public void call(ClassVisitor classWriter, ClassNode classNode) {
+            // lets test out the class verifier
+            if (DUMP_CLASS) {
+                dumper.visitClass(classNode);
+            }
+
+            if (CHECK_CLASS) {
+                checker.visitClass(classNode);
+            }
+
+            super.call(classWriter, classNode);
+        }
+    }
+
+    protected ClassCollector createCollector(CompilationUnit unit) {
+        return new DebugCollector(this, unit, null);
+    }
+
+    protected ASMifierClassVisitor dumpVisitor = new ASMifierClassVisitor(new PrintWriter(new OutputStreamWriter(System.out)));
+    protected ASMifierClassVisitor invisibleDumpVisitor = new ASMifierClassVisitor(new PrintWriter(new StringWriter()));
+    protected CompileUnit unit = new CompileUnit(this, new CompilerConfiguration());
+    protected ClassGenerator checker =
+            new AsmClassGenerator(new GeneratorContext(unit), new CheckClassAdapter(invisibleDumpVisitor), this, null);
+    protected ClassGenerator dumper = new AsmClassGenerator(new GeneratorContext(unit), dumpVisitor, this, null);
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/ForTest.java b/groovy/src/test/org/codehaus/groovy/classgen/ForTest.java
new file mode 100644
index 0000000..4d71095
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/ForTest.java
@@ -0,0 +1,154 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Pilho Kim
+ * @version $Revision$
+ */
+public class ForTest extends TestSupport {
+
+    public void testNonLoop() throws Exception {
+        ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        classNode.addConstructor(new ConstructorNode(ACC_PUBLIC, null));
+
+        Parameter[] parameters = {new Parameter(ClassHelper.OBJECT_TYPE, "coll")};
+
+        Statement statement = createPrintlnStatement(new VariableExpression("coll"));
+        classNode.addMethod(new MethodNode("oneParamDemo", ACC_PUBLIC, ClassHelper.VOID_TYPE, parameters, ClassNode.EMPTY_ARRAY, statement));
+
+        Class fooClass = loadClass(classNode);
+        assertTrue("Loaded a new class", fooClass != null);
+
+        Object bean = fooClass.newInstance();
+        assertTrue("Managed to create bean", bean != null);
+
+        System.out.println("################ Now about to invoke a method without looping");
+        Object value = new Integer(10000);
+
+        try {
+            InvokerHelper.invokeMethod(bean, "oneParamDemo", new Object[]{value});
+        } catch (InvokerInvocationException e) {
+            System.out.println("Caught: " + e.getCause());
+            e.getCause().printStackTrace();
+            fail("Should not have thrown an exception");
+        }
+        System.out.println("################ Done");
+    }
+
+
+    public void testLoop() throws Exception {
+        ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        classNode.addConstructor(new ConstructorNode(ACC_PUBLIC, null));
+
+        Parameter[] parameters = {new Parameter(ClassHelper.OBJECT_TYPE.makeArray(), "coll")};
+
+        Statement loopStatement = createPrintlnStatement(new VariableExpression("i"));
+
+        ForStatement statement = new ForStatement(new Parameter(ClassHelper.OBJECT_TYPE, "i"), new VariableExpression("coll"), loopStatement);
+        classNode.addMethod(new MethodNode("iterateDemo", ACC_PUBLIC, ClassHelper.VOID_TYPE, parameters, ClassNode.EMPTY_ARRAY, statement));
+
+        Class fooClass = loadClass(classNode);
+        assertTrue("Loaded a new class", fooClass != null);
+
+        Object bean = fooClass.newInstance();
+        assertTrue("Managed to create bean", bean != null);
+
+        System.out.println("################ Now about to invoke a method with looping");
+        Object[] array = {new Integer(1234), "abc", "def"};
+
+        try {
+            InvokerHelper.invokeMethod(bean, "iterateDemo", new Object[]{array});
+        } catch (InvokerInvocationException e) {
+            System.out.println("Caught: " + e.getCause());
+            e.getCause().printStackTrace();
+            fail("Should not have thrown an exception");
+        }
+        System.out.println("################ Done");
+    }
+
+    public void testManyParam() throws Exception {
+        ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        classNode.addConstructor(new ConstructorNode(ACC_PUBLIC, null));
+
+        Parameter[] parameters = {new Parameter(ClassHelper.OBJECT_TYPE, "coll1"), new Parameter(ClassHelper.OBJECT_TYPE, "coll2"), new Parameter(ClassHelper.OBJECT_TYPE, "coll3")};
+
+        BlockStatement statement = new BlockStatement();
+        statement.addStatement(createPrintlnStatement(new VariableExpression("coll1")));
+        statement.addStatement(createPrintlnStatement(new VariableExpression("coll2")));
+        statement.addStatement(createPrintlnStatement(new VariableExpression("coll3")));
+
+        classNode.addMethod(new MethodNode("manyParamDemo", ACC_PUBLIC, ClassHelper.VOID_TYPE, parameters, ClassNode.EMPTY_ARRAY, statement));
+
+        Class fooClass = loadClass(classNode);
+        assertTrue("Loaded a new class", fooClass != null);
+
+        Object bean = fooClass.newInstance();
+        assertTrue("Managed to create bean", bean != null);
+
+        System.out.println("################ Now about to invoke a method with many parameters");
+        Object[] array = {new Integer(1000 * 1000), "foo-", "bar~"};
+
+        try {
+            InvokerHelper.invokeMethod(bean, "manyParamDemo", array);
+        } catch (InvokerInvocationException e) {
+            System.out.println("Caught: " + e.getCause());
+            e.getCause().printStackTrace();
+            fail("Should not have thrown an exception");
+        }
+        System.out.println("################ Done");
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/GStringTest.java b/groovy/src/test/org/codehaus/groovy/classgen/GStringTest.java
new file mode 100644
index 0000000..5950775
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/GStringTest.java
@@ -0,0 +1,127 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.AssertStatement;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+import org.codehaus.groovy.syntax.Token;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class GStringTest extends TestSupport {
+
+    public void testConstructor() throws Exception {
+        ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+
+        //Statement printStatement = createPrintlnStatement(new VariableExpression("str"));
+
+        // simulate "Hello ${user}!"
+        GStringExpression compositeStringExpr = new GStringExpression("hello ${user}!");
+        compositeStringExpr.addString(new ConstantExpression("Hello "));
+        compositeStringExpr.addValue(new VariableExpression("user"));
+        compositeStringExpr.addString(new ConstantExpression("!"));
+        BlockStatement block = new BlockStatement();
+        block.addStatement(
+                new ExpressionStatement(
+                        new DeclarationExpression(
+                                new VariableExpression("user"),
+                                Token.newSymbol("=", -1, -1),
+                                new ConstantExpression("World"))));
+        block.addStatement(
+                new ExpressionStatement(
+                        new DeclarationExpression(new VariableExpression("str"), Token.newSymbol("=", -1, -1), compositeStringExpr)));
+        block.addStatement(
+                new ExpressionStatement(
+                        new MethodCallExpression(VariableExpression.THIS_EXPRESSION, "println", new VariableExpression("str"))));
+
+        block.addStatement(
+                new ExpressionStatement(
+                        new DeclarationExpression(
+                                new VariableExpression("text"),
+                                Token.newSymbol("=", -1, -1),
+                                new MethodCallExpression(new VariableExpression("str"), "toString", MethodCallExpression.NO_ARGUMENTS))));
+
+        block.addStatement(
+                new AssertStatement(
+                        new BooleanExpression(
+                                new BinaryExpression(
+                                        new VariableExpression("text"),
+                                        Token.newSymbol("==", -1, -1),
+                                        new ConstantExpression("Hello World!")))));
+        classNode.addMethod(new MethodNode("stringDemo", ACC_PUBLIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, block));
+
+        Class fooClass = loadClass(classNode);
+        assertTrue("Loaded a new class", fooClass != null);
+
+        Object bean = fooClass.newInstance();
+        assertTrue("Managed to create bean", bean != null);
+
+        System.out.println("################ Now about to invoke method");
+
+        //Object[] array = { new Integer(1234), "abc", "def" };
+
+        try {
+            InvokerHelper.invokeMethod(bean, "stringDemo", null);
+        }
+        catch (InvokerInvocationException e) {
+            System.out.println("Caught: " + e.getCause());
+            e.getCause().printStackTrace();
+            fail("Should not have thrown an exception");
+        }
+        System.out.println("################ Done");
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/GenericsGenTest.groovy b/groovy/src/test/org/codehaus/groovy/classgen/GenericsGenTest.groovy
new file mode 100644
index 0000000..7ac7bea
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/GenericsGenTest.groovy
@@ -0,0 +1,73 @@
+package org.codehaus.groovy.classgen

+

+import org.codehaus.groovy.tools.*

+import org.codehaus.groovy.control.*

+

+class GenericsGenTest extends GroovyTestCase{

+

+    void testCompile () {

+        try {

+            Class.forName("java.lang.annotation.Annotation");

+        }

+        catch(Exception ex) {

+            return

+        }

+

+

+        File dir = createTempDir("src-", "-src")

+        assertNotNull dir

+

+        def fileList =  [

+          "JavaClass.java" : """

+         import java.util.*;

+

+         public abstract class JavaClass<T> implements GroovyInterface<T> {

+         }

+    """,

+

+         "JavaInterface.java" : """

+         public interface JavaInterface<X,Y> {

+            public X getKey ();

+            public Y getValue ();

+         }

+    """,

+

+          "GroovyInterface.groovy" : """

+         interface GroovyInterface<X> {

+            X method ();

+         }

+    """,

+

+            "GroovyClass.groovy" : """

+           class GroovyClass extends JavaClass<String> implements JavaInterface<Long,Boolean> {

+              String method() {}

+			  Long getKey(){}

+              Boolean getValue(){}

+           }

+      """

+                ].collect {

+            name, text ->

+              File file = new File(dir, name)

+              file.write text

+              file

+        }

+

+        CompilerConfiguration config = new CompilerConfiguration()

+         config.targetDirectory = createTempDir("target-", "-target")

+         config.jointCompilationOptions = [

+			"stubDir" : createTempDir("stub-", "-stub"),

+			"namedValues" : ["target","1.5","source","1.5"] as String[]

+		 ]

+         config.classpath = "target/classes"

+        FileSystemCompiler compiler = new FileSystemCompiler(config)

+        compiler.compile (fileList.toArray(new File [fileList.size()]) )

+    }

+

+    private File createTempDir (prefix, suffix) {

+        File tempFile = File.createTempFile(prefix, suffix);

+        tempFile.delete();

+        tempFile.mkdirs();

+        tempFile.deleteOnExit()

+        return tempFile;

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/GetPropertyTest.java b/groovy/src/test/org/codehaus/groovy/classgen/GetPropertyTest.java
new file mode 100644
index 0000000..b0a9914
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/GetPropertyTest.java
@@ -0,0 +1,69 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyObject;
+
+/**
+ * Tests using the GroovyObject API from Java on a groovy object
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class GetPropertyTest extends TestSupport {
+
+    public void testProperty() throws Exception {
+        GroovyObject object = compile("src/test/org/codehaus/groovy/classgen/MyBean.groovy");
+        System.out.println("Got object: " + object);
+
+        Object value = object.getProperty("name");
+        assertEquals("name property", "James", value);
+
+        object.setProperty("name", "Bob");
+        assertEquals("name property", "Bob", object.getProperty("name"));
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/GroovyClassLoaderTest.java b/groovy/src/test/org/codehaus/groovy/classgen/GroovyClassLoaderTest.java
new file mode 100644
index 0000000..b57ff5e
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/GroovyClassLoaderTest.java
@@ -0,0 +1,84 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyObject;
+import groovy.lang.MetaClass;
+
+import java.io.File;
+
+
+/**
+ * Tests dynamically compiling a new class
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class GroovyClassLoaderTest extends TestSupport {
+
+    public void testCompile() throws Exception {
+        Class groovyClass = loader.parseClass(new File("src/test/org/codehaus/groovy/classgen/Main.groovy"));
+
+        System.out.println("Invoking main...");
+
+        GroovyObject object = (GroovyObject) groovyClass.newInstance();
+
+        assertTrue(object != null);
+
+        MetaClass metaClass = object.getMetaClass();
+        System.out.println("Metaclass: " + metaClass);
+
+        Class type = object.getClass();
+        System.out.println("Type: " + type);
+
+        // invoke via metaclass
+        metaClass.invokeMethod(object, "main", null);
+
+        // invoke directly
+        object.invokeMethod("main", null);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/IfElseTest.java b/groovy/src/test/org/codehaus/groovy/classgen/IfElseTest.java
new file mode 100644
index 0000000..987b10c
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/IfElseTest.java
@@ -0,0 +1,113 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.BooleanExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.IfStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.syntax.Token;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class IfElseTest extends TestSupport {
+
+    public void testLoop() throws Exception {
+        ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        classNode.addConstructor(new ConstructorNode(ACC_PUBLIC, null));
+        classNode.addProperty(new PropertyNode("bar", ACC_PUBLIC, ClassHelper.STRING_TYPE, classNode, null, null, null));
+
+        classNode.addProperty(new PropertyNode("result", ACC_PUBLIC, ClassHelper.STRING_TYPE, classNode, null, null, null));
+
+        BooleanExpression expression =
+                new BooleanExpression(
+                        new BinaryExpression(
+                                new FieldExpression(
+                                        new FieldNode("bar", ACC_PRIVATE, ClassHelper.STRING_TYPE, classNode, ConstantExpression.NULL)),
+                                Token.newSymbol("==", 0, 0),
+                                new ConstantExpression("abc")));
+
+        Statement trueStatement =
+                new ExpressionStatement(
+                        new BinaryExpression(
+                                new FieldExpression(
+                                        new FieldNode("result", ACC_PRIVATE, ClassHelper.STRING_TYPE, classNode, ConstantExpression.NULL)),
+                                Token.newSymbol("=", 0, 0),
+                                new ConstantExpression("worked")));
+
+        Statement falseStatement = createPrintlnStatement(new ConstantExpression("false"));
+
+        IfStatement statement = new IfStatement(expression, trueStatement, falseStatement);
+        classNode.addMethod(new MethodNode("ifDemo", ACC_PUBLIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statement));
+
+        Class fooClass = loadClass(classNode);
+        assertTrue("Loaded a new class", fooClass != null);
+
+        Object bean = fooClass.newInstance();
+        assertTrue("Managed to create bean", bean != null);
+
+        assertSetProperty(bean, "bar", "abc");
+
+        System.out.println("################ Now about to invoke method");
+
+        Object[] array = {
+        };
+
+        InvokerHelper.invokeMethod(bean, "ifDemo", array);
+
+        System.out.println("################ Done");
+
+        assertGetProperty(bean, "result", "worked");
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/InterfaceTest.groovy b/groovy/src/test/org/codehaus/groovy/classgen/InterfaceTest.groovy
new file mode 100644
index 0000000..9765da8
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/InterfaceTest.groovy
@@ -0,0 +1,67 @@
+package org.codehaus.groovy.classgen

+

+import org.codehaus.groovy.control.CompilerConfiguration

+import org.codehaus.groovy.tools.FileSystemCompiler

+

+class InterfaceTest extends GroovyTestCase{

+

+    void testCompile () {

+        try {

+            Class.forName("java.lang.annotation.Annotation");

+        }

+        catch(Exception ex) {

+            return

+        }

+

+

+        File dir = createTempDir("src-", "-src")

+        assertNotNull dir

+

+        def fileList =  [

+          "GClass.groovy" : """

+         package test;

+

+         class GClass {}

+    """,

+

+         "GInterface.groovy" : """

+         package test;

+

+         interface GInterface {

+            GClass [] get ();

+         }

+    """,

+

+          "JClass.java" : """

+         package test;

+

+         public class JClass implements GInterface {

+            public GClass [] get () { return new GClass [0]; };

+         }

+    """,

+                ].collect {

+            name, text ->

+              File file = new File(dir, name)

+              file.write text

+              file

+        }

+

+        CompilerConfiguration config = new CompilerConfiguration()

+         config.targetDirectory = createTempDir("target-", "-target")

+         config.jointCompilationOptions = [

+			"stubDir" : createTempDir("stub-", "-stub"),

+//			"namedValues" : ["target","1.5","source","1.5"] as String[]

+		 ]

+         config.classpath = "target/classes"

+        FileSystemCompiler compiler = new FileSystemCompiler(config)

+        compiler.compile (fileList.toArray(new File [fileList.size()]) )

+    }

+

+    private File createTempDir (prefix, suffix) {

+        File tempFile = File.createTempFile(prefix, suffix);

+        tempFile.delete();

+        tempFile.mkdirs();

+        tempFile.deleteOnExit()

+        return tempFile;

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/Main.groovy b/groovy/src/test/org/codehaus/groovy/classgen/Main.groovy
new file mode 100644
index 0000000..15becd1
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/Main.groovy
@@ -0,0 +1,8 @@
+package org.codehaus.groovy.classgen
+
+class Main {
+    
+    def main() {
+        println("Hello World!")
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/MainTest.java b/groovy/src/test/org/codehaus/groovy/classgen/MainTest.java
new file mode 100644
index 0000000..eabab73
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/MainTest.java
@@ -0,0 +1,63 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyShell;
+
+import java.io.File;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MainTest extends TestSupport {
+
+    public void testMainMethod() throws Exception {
+        GroovyShell shell = new GroovyShell();
+        shell.run(new File("src/test/groovy/SampleMain.groovy"), new String[]{"A", "B", "C"});
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/MetaClassTest.groovy b/groovy/src/test/org/codehaus/groovy/classgen/MetaClassTest.groovy
new file mode 100644
index 0000000..004835e
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/MetaClassTest.groovy
@@ -0,0 +1,34 @@
+package org.codehaus.groovy.classgen
+
+class MetaClassTest extends GroovyTestCase {
+    
+    void testMetaClass() {
+        test(this)
+        test { print(it) }
+    }
+    
+    protected def test(object) {
+        def metaClass = object.metaClass
+        assert metaClass != null
+        
+        println(metaClass)
+        
+        def classNode = metaClass.getClassNode()
+        assert classNode != null
+
+        println(classNode)
+        
+        def name = object.getClass().getName()
+        assert classNode.name == name
+    }
+    
+	void testMetClassDefinition() {
+		assertScript """
+			class Foo {
+		    	MetaClass metaClass
+			} 
+			def foo = new Foo()
+			assert foo.@metaClass != null
+			"""
+	}
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/MethodTest.java b/groovy/src/test/org/codehaus/groovy/classgen/MethodTest.java
new file mode 100644
index 0000000..dc63029
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/MethodTest.java
@@ -0,0 +1,98 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ReturnStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.runtime.InvokerHelper;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MethodTest extends TestSupport {
+
+    public void testMethods() throws Exception {
+        ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        classNode.addConstructor(new ConstructorNode(ACC_PUBLIC, null));
+
+        Statement statementA = new ReturnStatement(new ConstantExpression("calledA"));
+        Statement statementB = new ReturnStatement(new ConstantExpression("calledB"));
+        Statement emptyStatement = new BlockStatement();
+
+        classNode.addMethod(new MethodNode("a", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statementA));
+        classNode.addMethod(new MethodNode("b", ACC_PUBLIC, null, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statementB));
+
+        classNode.addMethod(new MethodNode("noReturnMethodA", ACC_PUBLIC, null, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, emptyStatement));
+        classNode.addMethod(new MethodNode("noReturnMethodB", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, emptyStatement));
+
+        classNode.addMethod(new MethodNode("c", ACC_PUBLIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, emptyStatement));
+
+        Class fooClass = loadClass(classNode);
+        assertTrue("Loaded a new class", fooClass != null);
+
+        Object bean = fooClass.newInstance();
+        assertTrue("Created instance of class: " + bean, bean != null);
+
+        assertCallMethod(bean, "a", "calledA");
+        assertCallMethod(bean, "b", "calledB");
+        assertCallMethod(bean, "noReturnMethodA", null);
+        assertCallMethod(bean, "noReturnMethodB", null);
+        assertCallMethod(bean, "c", null);
+    }
+
+    protected void assertCallMethod(Object object, String method, Object expected) {
+        Object value = InvokerHelper.invokeMethod(object, method, new Object[0]);
+        assertEquals("Result of calling method: " + method + " on: " + object + " with empty list", expected, value);
+
+        value = InvokerHelper.invokeMethod(object, method, null);
+        assertEquals("Result of calling method: " + method + " on: " + object + " with null", expected, value);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/MyBean.groovy b/groovy/src/test/org/codehaus/groovy/classgen/MyBean.groovy
new file mode 100644
index 0000000..0358757
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/MyBean.groovy
@@ -0,0 +1,7 @@
+package org.codehaus.groovy.classgen
+
+class MyBean {
+
+	def name = "James"
+	def foo = 123
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/PropertyTest.java b/groovy/src/test/org/codehaus/groovy/classgen/PropertyTest.java
new file mode 100644
index 0000000..e99c9c1
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/PropertyTest.java
@@ -0,0 +1,110 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.PropertyNode;
+import org.codehaus.groovy.runtime.DummyBean;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class PropertyTest extends TestSupport {
+
+    public void testFields() throws Exception {
+        ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        classNode.addField("x", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, null);
+        classNode.addField("y", ACC_PUBLIC, ClassHelper.Integer_TYPE, null);
+        classNode.addField("z", ACC_PRIVATE, ClassHelper.STRING_TYPE, null);
+
+        Class fooClass = loadClass(classNode);
+        assertTrue("Loaded a new class", fooClass != null);
+
+        assertField(fooClass, "x", Modifier.PUBLIC, ClassHelper.OBJECT_TYPE);
+        assertField(fooClass, "y", Modifier.PUBLIC, ClassHelper.Integer_TYPE);
+        assertField(fooClass, "z", Modifier.PRIVATE, ClassHelper.STRING_TYPE);
+    }
+
+    public void testProperties() throws Exception {
+        ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC + ACC_SUPER, ClassHelper.OBJECT_TYPE);
+        classNode.addProperty(new PropertyNode("bar", ACC_PUBLIC, ClassHelper.STRING_TYPE, classNode, null, null, null));
+
+        Class fooClass = loadClass(classNode);
+        assertTrue("Loaded a new class", fooClass != null);
+
+        Object bean = fooClass.newInstance();
+        assertTrue("Managed to create bean", bean != null);
+
+        assertField(fooClass, "bar", 0, ClassHelper.STRING_TYPE);
+
+        assertGetProperty(bean, "bar", null);
+        assertSetProperty(bean, "bar", "newValue");
+    }
+
+    public void testInheritedProperties() throws Exception {
+        ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC + ACC_SUPER, ClassHelper.make(DummyBean.class));
+        classNode.addProperty(new PropertyNode("bar", ACC_PUBLIC, ClassHelper.STRING_TYPE, classNode, null, null, null));
+
+        Class fooClass = loadClass(classNode);
+        assertTrue("Loaded a new class", fooClass != null);
+
+        Object bean = fooClass.newInstance();
+        assertTrue("Managed to create bean", bean != null);
+
+        assertField(fooClass, "bar", 0, ClassHelper.STRING_TYPE);
+
+        assertGetProperty(bean, "name", "James");
+        assertSetProperty(bean, "name", "Bob");
+
+        assertGetProperty(bean, "bar", null);
+        assertSetProperty(bean, "bar", "newValue");
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/ReflectorGeneratorTest.java b/groovy/src/test/org/codehaus/groovy/classgen/ReflectorGeneratorTest.java
new file mode 100644
index 0000000..568ad72
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/ReflectorGeneratorTest.java
@@ -0,0 +1,139 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovySystem;
+import groovy.lang.MetaClassRegistry;
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.reflection.CachedMethod;
+import org.codehaus.groovy.reflection.ReflectionCache;
+import org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.util.ASMifierClassVisitor;
+import org.objectweb.asm.util.CheckClassAdapter;
+
+import java.io.FileOutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class ReflectorGeneratorTest extends GroovyTestCase {
+
+    public void testGenerator() throws Exception {
+        List methods = new ArrayList();
+//        methods.add(new MetaMethod("toCharArray", String.class, new CachedClass[0], char[].class, 0));
+        //methods.add(new MetaMethod("toString", String.class, new Class[0], String.class, 0));
+        testMethods(methods);
+    }
+
+    public void testObjectGenerator() throws Exception {
+        List methods = Arrays.asList(ReflectionCache.OBJECT_CLASS.getMethods());
+        testMethods(methods);
+    }
+
+    public void testDummyReflector() throws Exception {
+        DummyReflector dummy = new DummyReflector();
+        assertTrue(dummy != null);
+    }
+
+    protected void testMethods(List methods) throws Exception {
+        ReflectorGenerator generator = new ReflectorGenerator(methods);
+        String name = getClass().getName() + "." + getMethodName();
+        ClassWriter cw = new ClassWriter(true);
+
+        //ASMifierClassVisitor dumper = new ASMifierClassVisitor(new PrintWriter(new OutputStreamWriter(System.out)));
+        //generator.generate(dumper, name);
+
+        generator.generate(new CheckClassAdapter(cw), name);
+
+        byte[] bytecode = cw.toByteArray();
+
+        // lets write it to disk
+        String fileName = "target/" + name + ".class";
+        FileOutputStream out = new FileOutputStream(fileName);
+        out.write(bytecode);
+        out.close();
+
+        // now lets try dump it
+        ASMifierClassVisitor.main(new String[]{fileName});
+
+        // now lets try class load it
+        MetaClassRegistry registry = GroovySystem.getMetaClassRegistry();
+        Object reflector = ((MetaClassRegistryImpl) registry).loadReflector(getClass(), methods);
+
+        System.out.println("Created new reflector: " + reflector);
+    }
+
+    public void testP () {
+        A_GroovyReflector.doIt(); 
+    }
+}
+
+class A {
+    protected void protectedMethod() {}
+}
+
+class A_GroovyReflector {
+    static void doIt () {
+
+        new A ().protectedMethod();
+
+        try {
+            CachedMethod m = CachedMethod.find(A.class.getDeclaredMethod("protectedMethod", new Class [0] ));
+            Object[] arguments = new Object[0];
+            m.setAccessible().invoke(new A(), arguments);
+        } catch (NoSuchMethodException e) {
+        } catch (IllegalAccessException e) {
+        } catch (InvocationTargetException e) {
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/ReflectorLoaderTest.groovy b/groovy/src/test/org/codehaus/groovy/classgen/ReflectorLoaderTest.groovy
new file mode 100644
index 0000000..b622be8
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/ReflectorLoaderTest.groovy
@@ -0,0 +1,14 @@
+package org.codehaus.groovy.classgen

+

+class ReflectorLoaderTest extends GroovyTestCase {

+

+    void testDuplication(){

+      def program =  '''

+		closureA = {}

+		closureB = {closureA ( ) }

+	  '''

+	  def binding  = new Binding()

+	  ( new GroovyShell ( binding ) ).evaluate ( program )

+	  binding.closureB.call( )

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/RunBugsTest.java b/groovy/src/test/org/codehaus/groovy/classgen/RunBugsTest.java
new file mode 100644
index 0000000..8bd8c3b
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/RunBugsTest.java
@@ -0,0 +1,149 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+ * 
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met:
+ *  1. Redistributions of source code must retain copyright statements and
+ * notices. Redistributions must also contain a copy of this document.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *  3. The name "groovy" must not be used to endorse or promote products
+ * derived from this Software without prior written permission of The Codehaus.
+ * For written permission, please contact info@codehaus.org.
+ *  4. Products derived from this Software may not be called "groovy" nor may
+ * "groovy" appear in their names without prior written permission of The
+ * Codehaus. "groovy" is a registered trademark of The Codehaus.
+ *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *  
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyObject;
+
+/**
+ * A helper class for testing bugs in code generation errors. By turning on the
+ * logging in TestSupport we can dump the ASM code generation code for inner
+ * classes etc.
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class RunBugsTest extends TestSupport {
+
+    /*
+    public void testStaticMethodCall() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/StaticMethodCallBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+
+    public void testTryCatchBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/TryCatchBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+
+    public void testRodsBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/RodsBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+
+    public void testCastBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/ClosureMethodCallTest.groovy");
+        object.invokeMethod("testCallingClosureWithMultipleArguments", null);
+    }
+
+    public void testGuillaumesMapBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/GuillaumesMapBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+
+    public void testUseClosureInScript() throws Exception {
+        GroovyObject object = compile("src/test/groovy/script/UseClosureInScript.groovy");
+        object.invokeMethod("run", null);
+    }
+
+    public void testUseStaticInClosure() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/UseStaticInClosureBug.groovy");
+        object.invokeMethod("testBug2", null);
+    }
+
+    public void testPrimitiveTypeFieldTest() throws Exception {
+        GroovyObject object = compile("src/test/groovy/PrimitiveTypeFieldTest.groovy");
+        object.invokeMethod("testPrimitiveField", null);
+    }
+    
+    public void testMethodDispatchBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/MethodDispatchBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+    public void testClosureInClosureTest() throws Exception {
+        GroovyObject object = compile("src/test/groovy/ClosureInClosureTest.groovy");
+        object.invokeMethod("testInvisibleVariable", null);
+    }
+    public void testStaticMarkupBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/StaticMarkupBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+    public void testOverloadInvokeMethodBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/OverloadInvokeMethodBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+    public void testClosureVariableBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/ClosureVariableBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+    
+    public void testMarkupAndMethodBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/MarkupAndMethodBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+    public void testClosureParameterPassingBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/ClosureParameterPassingBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+    public void testNestedClosureBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/NestedClosure2Bug.groovy");
+        object.invokeMethod("testFieldBug", null);
+    }
+    public void testSuperMethod2Bug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/SuperMethod2Bug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+    public void testToStringBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/ToStringBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+    public void testByteIndexBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/ByteIndexBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+    public void testGroovy252_Bug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/Groovy252_Bug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+    
+    */
+
+    public void testGroovy303_Bug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/Groovy303_Bug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/RunClosureTest.java b/groovy/src/test/org/codehaus/groovy/classgen/RunClosureTest.java
new file mode 100644
index 0000000..6cf26be
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/RunClosureTest.java
@@ -0,0 +1,109 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyObject;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class RunClosureTest extends TestSupport {
+
+    public void testClosure() throws Exception {
+        GroovyObject object = compile("src/test/groovy/ClosureUsingOuterVariablesTest.groovy");
+        object.invokeMethod("testExampleUseOfClosureScopesUsingEach", null);
+    }
+
+    public void testStaticClosureBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/StaticClosurePropertyBug.groovy");
+        object.invokeMethod("testCallStaticClosure", null);
+    }
+
+    public void testZoharsBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/ZoharsBug.groovy");
+        object.invokeMethod("testBug", null);
+    }
+
+    public void testBytecodeBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/BytecodeBug.groovy");
+        object.invokeMethod("testTedsBytecodeBug", null);
+    }
+
+    public void testBytecode2Bug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/Bytecode2Bug.groovy");
+        object.invokeMethod("testTedsBytecodeBug", null);
+    }
+
+    public void testBytecode3Bug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/Bytecode3Bug.groovy");
+        //object.invokeMethod("testInject", null);
+        object.invokeMethod("testIncrementPropertyInclosure", null);
+    }
+
+    public void testBytecode4Bug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/Bytecode4Bug.groovy");
+        object.invokeMethod("testInject", null);
+        object.invokeMethod("testUsingProperty", null);
+    }
+
+    public void testBytecode5Bug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/Bytecode5Bug.groovy");
+        object.invokeMethod("testUsingLocalVar", null);
+    }
+
+    public void testBytecode6Bug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/bugs/Bytecode6Bug.groovy");
+        object.invokeMethod("testPreFixReturn", null);
+    }
+
+    public void testPropertyTest() throws Exception {
+        GroovyObject object = compile("src/test/groovy/PropertyTest.groovy");
+        object.invokeMethod("testNormalPropertyGettersAndSetters", null);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/RunGroovyTest.java b/groovy/src/test/org/codehaus/groovy/classgen/RunGroovyTest.java
new file mode 100644
index 0000000..2c25719
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/RunGroovyTest.java
@@ -0,0 +1,95 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import groovy.lang.GroovyObject;
+
+/**
+ * Tests dynamically compiling and running a new class
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class RunGroovyTest extends TestSupport {
+
+    public void testArrayBug() throws Exception {
+        GroovyObject object = compile("src/test/groovy/ToArrayBugTest.groovy");
+        object.invokeMethod("testToArrayBug", null);
+    }
+
+
+    public void testPostfix() throws Exception {
+        GroovyObject object = compile("src/test/groovy/PostfixTest.groovy");
+        object.invokeMethod("testIntegerPostfix", null);
+    }
+
+    public void testMap() throws Exception {
+        GroovyObject object = compile("src/test/groovy/MapTest.groovy");
+        object.invokeMethod("testMap", null);
+    }
+
+    public void testClosure() throws Exception {
+        GroovyObject object = compile("src/test/groovy/ClosureMethodTest.groovy");
+        object.invokeMethod("testListCollect", null);
+    }
+
+    public void testClosureWithDefaultParam() throws Exception {
+        GroovyObject object = compile("src/test/groovy/ClosureWithDefaultParamTest.groovy");
+        object.invokeMethod("methodWithDefaultParam", null);
+    }
+
+    public void testOptionalReturn() throws Exception {
+        GroovyObject object = compile("src/test/groovy/OptionalReturnTest.groovy");
+        object.invokeMethod("testSingleExpression", null);
+        object.invokeMethod("testLastExpressionIsSimple", null);
+    }
+
+    public void testConsole() throws Exception {
+        GroovyObject object = compile("src/main/groovy/ui/Console.groovy");
+    }
+    /*    */
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/SimpleBean.java b/groovy/src/test/org/codehaus/groovy/classgen/SimpleBean.java
new file mode 100644
index 0000000..dafb0b1
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/SimpleBean.java
@@ -0,0 +1,79 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+
+/**
+ * A simple bean
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class SimpleBean {
+
+    private String bar;
+    private Object x;
+
+    //private static final Object INT_CONST = new Integer(123);
+
+    public String getBar() {
+        return bar;
+    }
+
+    public void setBar(String value) {
+        this.bar = value;
+    }
+
+
+    public void setNumber() {
+        x = new Integer(123);
+    }
+
+    public void setFloat() {
+        x = new Double(123);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/TestSupport.java b/groovy/src/test/org/codehaus/groovy/classgen/TestSupport.java
new file mode 100644
index 0000000..2b595e2
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/TestSupport.java
@@ -0,0 +1,210 @@
+package org.codehaus.groovy.classgen;
+
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+import groovy.lang.*;
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CompileUnit;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.FieldExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.control.CompilerConfiguration;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.objectweb.asm.Opcodes;
+
+import java.beans.BeanInfo;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+
+/**
+ * Base class for test cases
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TestSupport extends GroovyTestCase implements Opcodes {
+
+    protected static boolean DUMP_CLASS = false;
+
+    // ClassLoader parentLoader = Thread.currentThread().getContextClassLoader();
+    final ClassLoader parentLoader = getClass().getClassLoader();
+    protected final GroovyClassLoader loader =
+            (GroovyClassLoader) AccessController.doPrivileged(new PrivilegedAction() {
+                public Object run() {
+                    return new GroovyClassLoader(parentLoader);
+                }
+            });
+    final CompileUnit unit = new CompileUnit(loader, new CompilerConfiguration());
+    final ModuleNode module = new ModuleNode(unit);
+
+    protected Class loadClass(ClassNode classNode) {
+        classNode.setModule(module);
+        Class fooClass = loader.defineClass(classNode, classNode.getName() + ".groovy", "groovy.testSupport");
+        return fooClass;
+    }
+
+    protected void assertSetProperty(Object bean, String property, Object newValue) throws Exception {
+        PropertyDescriptor descriptor = getDescriptor(bean, property);
+        Method method = descriptor.getWriteMethod();
+        assertTrue("has setter method", method != null);
+
+        Object[] args = {newValue};
+        Object value = invokeMethod(bean, method, args);
+
+        assertEquals("should return null", null, value);
+
+        assertGetProperty(bean, property, newValue);
+    }
+
+    protected void assertGetProperty(Object bean, String property, Object expected) throws Exception {
+        PropertyDescriptor descriptor = getDescriptor(bean, property);
+        Method method = descriptor.getReadMethod();
+        assertTrue("has getter method", method != null);
+
+        Object[] args = {
+        };
+        Object value = invokeMethod(bean, method, args);
+
+        /*
+        System.out.println("Expected: " + expected);
+        System.out.println("Value: " + value);
+        
+        if (expected == null) { System.out.println("Expected is null"); }
+        if (value == null) { System.out.println("value is null"); }
+        */
+
+        assertEquals("property value", expected, value);
+    }
+
+    protected Object invokeMethod(Object bean, Method method, Object[] args) throws Exception {
+        try {
+            return method.invoke(bean, args);
+        }
+        catch (InvocationTargetException e) {
+            fail("InvocationTargetException: " + e.getTargetException());
+            return null;
+        }
+    }
+
+    protected PropertyDescriptor getDescriptor(Object bean, String property) throws Exception {
+        BeanInfo info = Introspector.getBeanInfo(bean.getClass());
+        PropertyDescriptor[] descriptors = info.getPropertyDescriptors();
+        for (int i = 0; i < descriptors.length; i++) {
+            PropertyDescriptor descriptor = descriptors[i];
+            if (descriptor.getName().equals(property)) {
+                return descriptor;
+            }
+        }
+        fail("Could not find property: " + property + " on bean: " + bean);
+        return null;
+    }
+
+    protected void assertField(Class aClass, String name, int modifiers, ClassNode type) throws Exception {
+        Field field = aClass.getDeclaredField(name);
+
+        assertTrue("Found field called: " + name, field != null);
+        assertEquals("Name", name, field.getName());
+        assertEquals("Type", type.getName(), field.getType().getName());
+        assertEquals("Modifiers", modifiers, field.getModifiers());
+    }
+
+    protected ExpressionStatement createPrintlnStatement(Expression expression) throws NoSuchFieldException {
+        return new ExpressionStatement(
+                new MethodCallExpression(
+                        new FieldExpression(FieldNode.newStatic(System.class, "out")),
+                        "println",
+                        expression));
+    }
+
+    /**
+     * Asserts that the script runs without any exceptions
+     */
+    protected void assertScript(String text) throws Exception {
+        assertScript(text, getTestClassName());
+    }
+
+    protected void assertScript(final String text, final String scriptName) throws Exception {
+        log.info("About to execute script");
+        log.info(text);
+        GroovyCodeSource gcs = (GroovyCodeSource) AccessController.doPrivileged(new PrivilegedAction() {
+            public Object run() {
+                return new GroovyCodeSource(text, scriptName, "/groovy/testSupport");
+            }
+        });
+        Class groovyClass = loader.parseClass(gcs);
+        Script script = InvokerHelper.createScript(groovyClass, new Binding());
+        script.run();
+    }
+
+    protected void assertScriptFile(String fileName) throws Exception {
+        log.info("About to execute script: " + fileName);
+
+        Class groovyClass = loader.parseClass(new GroovyCodeSource(new File(fileName)));
+        Script script = InvokerHelper.createScript(groovyClass, new Binding());
+        script.run();
+    }
+
+    protected GroovyObject compile(String fileName) throws Exception {
+        Class groovyClass = loader.parseClass(new GroovyCodeSource(new File(fileName)));
+
+        GroovyObject object = (GroovyObject) groovyClass.newInstance();
+
+        assertTrue(object != null);
+
+        return object;
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/TupleListTest.java b/groovy/src/test/org/codehaus/groovy/classgen/TupleListTest.java
new file mode 100644
index 0000000..b0d6206
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/TupleListTest.java
@@ -0,0 +1,123 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.classgen;
+
+import org.codehaus.groovy.ast.*;
+import org.codehaus.groovy.ast.expr.*;
+import org.codehaus.groovy.ast.stmt.BlockStatement;
+import org.codehaus.groovy.ast.stmt.ExpressionStatement;
+import org.codehaus.groovy.ast.stmt.ForStatement;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.runtime.InvokerHelper;
+import org.codehaus.groovy.runtime.InvokerInvocationException;
+import org.codehaus.groovy.syntax.Token;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TupleListTest extends TestSupport {
+
+    public void testIterateOverTuple() throws Exception {
+        TupleExpression listExpression = new TupleExpression();
+        listExpression.addExpression(new ConstantExpression("a"));
+        listExpression.addExpression(new ConstantExpression("b"));
+        listExpression.addExpression(new ConstantExpression("c"));
+        assertIterate("iterateOverTuple", listExpression);
+    }
+
+    public void testIterateOverList() throws Exception {
+        ListExpression listExpression = new ListExpression();
+        listExpression.addExpression(new ConstantExpression("a"));
+        listExpression.addExpression(new ConstantExpression("b"));
+        listExpression.addExpression(new ConstantExpression("c"));
+        listExpression.addExpression(new ConstantExpression("a"));
+        listExpression.addExpression(new ConstantExpression("b"));
+        listExpression.addExpression(new ConstantExpression("c"));
+        assertIterate("iterateOverList", listExpression);
+    }
+
+    public void testIterateOverMap() throws Exception {
+        MapExpression mapExpression = new MapExpression();
+        mapExpression.addMapEntryExpression(new ConstantExpression("a"), new ConstantExpression("x"));
+        mapExpression.addMapEntryExpression(new ConstantExpression("b"), new ConstantExpression("y"));
+        mapExpression.addMapEntryExpression(new ConstantExpression("c"), new ConstantExpression("z"));
+        assertIterate("iterateOverMap", mapExpression);
+    }
+
+    protected void assertIterate(String methodName, Expression listExpression) throws Exception {
+        ClassNode classNode = new ClassNode("Foo", ACC_PUBLIC, ClassHelper.OBJECT_TYPE);
+        classNode.addConstructor(new ConstructorNode(ACC_PUBLIC, null));
+        classNode.addProperty(new PropertyNode("bar", ACC_PUBLIC, ClassHelper.STRING_TYPE, classNode, null, null, null));
+
+        Statement loopStatement = createPrintlnStatement(new VariableExpression("i"));
+
+        BlockStatement block = new BlockStatement();
+        block.addStatement(new ExpressionStatement(new DeclarationExpression(new VariableExpression("list"), Token.newSymbol("=", 0, 0), listExpression)));
+        block.addStatement(new ForStatement(new Parameter(ClassHelper.DYNAMIC_TYPE, "i"), new VariableExpression("list"), loopStatement));
+        classNode.addMethod(new MethodNode(methodName, ACC_PUBLIC, ClassHelper.VOID_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, block));
+
+        Class fooClass = loadClass(classNode);
+        assertTrue("Loaded a new class", fooClass != null);
+
+        Object bean = fooClass.newInstance();
+        assertTrue("Managed to create bean", bean != null);
+
+        System.out.println("################ Now about to invoke method");
+
+        try {
+            InvokerHelper.invokeMethod(bean, methodName, null);
+        }
+        catch (InvokerInvocationException e) {
+            System.out.println("Caught: " + e.getCause());
+            e.getCause().printStackTrace();
+            fail("Should not have thrown an exception");
+        }
+        System.out.println("################ Done");
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/classgen/VerifierCodeVisitorTest.java b/groovy/src/test/org/codehaus/groovy/classgen/VerifierCodeVisitorTest.java
new file mode 100644
index 0000000..f124c44
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/classgen/VerifierCodeVisitorTest.java
@@ -0,0 +1,58 @@
+/**
+ *
+ * Copyright 2004 James Strachan
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ **/
+package org.codehaus.groovy.classgen;
+
+import junit.framework.TestCase;
+import org.codehaus.groovy.ast.ASTNode;
+import org.codehaus.groovy.syntax.RuntimeParserException;
+
+/**
+ * @version $Revision$
+ */
+public class VerifierCodeVisitorTest extends TestCase {
+    public void testValidNames() {
+        assertValidName("a");
+        assertValidName("a1234");
+        assertValidName("a_b_c");
+        assertValidName("a____1234");
+    }
+
+    public void testInvalidNames() {
+        assertInvalidName("1");
+        assertInvalidName("100");
+        assertInvalidName("1a");
+        assertInvalidName("a!");
+        assertInvalidName("a.");
+        assertInvalidName("$");
+        assertInvalidName("$foo");
+    }
+
+    protected void assertValidName(String name) {
+        VerifierCodeVisitor.assertValidIdentifier(name, "variable name", new ASTNode());
+    }
+
+    protected void assertInvalidName(String name) {
+        try {
+            VerifierCodeVisitor.assertValidIdentifier(name, "variable name", new ASTNode());
+            fail("Should have thrown exception due to invalid name: " + name);
+        }
+        catch (RuntimeParserException e) {
+            System.out.println("Caught invalid exception: " + e);
+        }
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/control/CompilationUnitTest.java b/groovy/src/test/org/codehaus/groovy/control/CompilationUnitTest.java
new file mode 100644
index 0000000..c359eb7
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/control/CompilationUnitTest.java
@@ -0,0 +1,43 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 2005 The Codehaus - http://groovy.codehaus.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and limitations under the License.
+ *
+ */
+
+
+package org.codehaus.groovy.control;
+
+import groovy.lang.GroovyClassLoader;
+import org.jmock.Mock;
+import org.jmock.cglib.MockObjectTestCase;
+
+import java.util.Iterator;
+
+public class CompilationUnitTest extends MockObjectTestCase {
+
+    public void testAppendsTheClasspathOfTheCompilerConfigurationToCurrentClassLoaderWhenInstantiated() {
+        CompilerConfiguration configuration = new CompilerConfiguration();
+        configuration.setClasspath(System.getProperty("java.class.path"));
+        // disabled until checked with fraz
+        //new CompilationUnit(configuration, null, createGroovyClassLoaderWithExpectations(configuration));
+    }
+
+    private GroovyClassLoader createGroovyClassLoaderWithExpectations(CompilerConfiguration configuration) {
+        Mock mockGroovyClassLoader = mock(GroovyClassLoader.class);
+        for (Iterator iterator = configuration.getClasspath().iterator(); iterator.hasNext();) {
+            mockGroovyClassLoader.expects(once()).method("addClasspath").with(eq(iterator.next()));
+        }
+        return (GroovyClassLoader) mockGroovyClassLoader.proxy();
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/control/CompilerConfigurationTest.java b/groovy/src/test/org/codehaus/groovy/control/CompilerConfigurationTest.java
new file mode 100644
index 0000000..6b32cb1
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/control/CompilerConfigurationTest.java
@@ -0,0 +1,247 @@
+

+/*

+ * $Id:$

+ *

+ * Copyright (c) 2007 James P. White

+ *

+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with

+ * the License. You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on

+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ *

+ * See the License for the specific language governing permissions and limitations under the License.

+ *

+ */

+

+package org.codehaus.groovy.control;

+

+import groovy.util.GroovyTestCase;

+import org.codehaus.groovy.control.messages.WarningMessage;

+

+import java.io.File;

+import java.io.PrintWriter;

+import java.util.HashMap;

+import java.util.List;

+import java.util.Map;

+import java.util.Properties;

+

+/**

+ * Make sure CompilerConfiguration works.

+ */

+public class CompilerConfigurationTest extends GroovyTestCase {

+    Properties savedProperties;

+

+    // Use setUp/tearDown to avoid mucking with system properties for other tests...

+

+    public void setUp() {

+        savedProperties = System.getProperties();

+        System.setProperties(new Properties(savedProperties));

+    }

+

+    public void tearDown() {

+        System.setProperties(savedProperties);

+    }

+

+    public void testDefaultConstructor() {

+        final CompilerConfiguration config = CompilerConfiguration.DEFAULT;

+

+        assertEquals(WarningMessage.LIKELY_ERRORS, config.getWarningLevel());

+        assertEquals(Boolean.getBoolean("groovy.output.debug"), config.getDebug());

+        assertEquals(Boolean.getBoolean("groovy.output.verbose"), config.getVerbose());

+        assertEquals(false, config.getDebug());

+        assertEquals(false, config.getVerbose());

+        assertEquals(10, config.getTolerance());

+        assertEquals(100, config.getMinimumRecompilationInterval());

+        assertNull(config.getScriptBaseClass());

+        assertEquals(getSystemEncoding(), config.getSourceEncoding());

+        assertEquals(getVMVersion(), config.getTargetBytecode());

+        assertEquals(false, config.getRecompileGroovySource());

+        {

+            final List listCP = config.getClasspath();

+            assertNotNull(listCP);

+            assertEquals(0, listCP.size());

+        }

+        assertNotNull(config.getOutput());

+        assertNull(config.getTargetDirectory());

+        assertEquals(".groovy", config.getDefaultScriptExtension());

+        assertNull(config.getJointCompilationOptions());

+        assertNotNull(config.getPluginFactory());

+    }

+

+    private String getSystemEncoding() {

+        return System.getProperty("file.encoding", "US-ASCII");

+    }

+

+    private static String getVMVersion() {

+        try {

+            Class.forName("java.lang.annotation.Annotation");

+            return CompilerConfiguration.POST_JDK5;

+        }

+        catch(Exception ex) {

+            // IGNORE

+        }

+

+        return CompilerConfiguration.PRE_JDK5;

+    }

+

+    public void testSetViaSystemProperties() {

+        System.setProperty("groovy.warnings", "PaRaNoiA");

+        System.setProperty("groovy.output.verbose", "trUE");

+        System.setProperty("groovy.recompile.minimumInterval", "867892345");

+

+        assertEquals("PaRaNoiA", System.getProperty("groovy.warnings"));

+

+        final CompilerConfiguration config = new CompilerConfiguration(System.getProperties());

+

+        assertEquals(WarningMessage.PARANOIA, config.getWarningLevel());

+        assertEquals(false, config.getDebug());

+        assertEquals(true, config.getVerbose());

+        assertEquals(10, config.getTolerance());

+        assertEquals(867892345, config.getMinimumRecompilationInterval());

+        assertNull(config.getScriptBaseClass());

+        assertEquals(getSystemEncoding(), config.getSourceEncoding());

+        assertEquals(getVMVersion(), config.getTargetBytecode());

+        assertEquals(false, config.getRecompileGroovySource());

+        {

+            final List listCP = config.getClasspath();

+            assertNotNull(listCP);

+            assertEquals(0, listCP.size());

+        }

+        assertNotNull(config.getOutput());

+        assertNull(config.getTargetDirectory());

+        assertEquals(".groovy", config.getDefaultScriptExtension());

+        assertNull(config.getJointCompilationOptions());

+        assertNotNull(config.getPluginFactory());

+    }

+

+    public void testCopyConstructor1() {

+        final CompilerConfiguration init = new CompilerConfiguration();

+

+        init.setWarningLevel(WarningMessage.POSSIBLE_ERRORS);

+        init.setDebug(true);

+        init.setVerbose(false);

+        init.setTolerance(720);

+        init.setMinimumRecompilationInterval(234);

+        init.setScriptBaseClass("blarg.foo.WhatSit");

+        init.setSourceEncoding("LEAD-123");

+        init.setTargetBytecode(CompilerConfiguration.POST_JDK5);

+        init.setRecompileGroovySource(true);

+        init.setClasspath("File1" + File.pathSeparator + "Somewhere");

+

+        final PrintWriter initOut = new PrintWriter(System.out);

+        init.setOutput(initOut);

+

+        final File initTDFile = new File("A wandering path");

+        init.setTargetDirectory(initTDFile);

+        init.setDefaultScriptExtension(".jpp");

+

+        final Map initJoint = new HashMap();

+        initJoint.put("somekey", "somevalue");

+        init.setJointCompilationOptions(initJoint);

+

+        final ParserPluginFactory initPPF = ParserPluginFactory.newInstance(true);

+        init.setPluginFactory(initPPF);

+

+        assertEquals(WarningMessage.POSSIBLE_ERRORS, init.getWarningLevel());

+        assertEquals(true, init.getDebug());

+        assertEquals(false, init.getVerbose());

+        assertEquals(720, init.getTolerance());

+        assertEquals(234, init.getMinimumRecompilationInterval());

+        assertEquals("blarg.foo.WhatSit", init.getScriptBaseClass());

+        assertEquals("LEAD-123", init.getSourceEncoding());

+        assertEquals(CompilerConfiguration.POST_JDK5, init.getTargetBytecode());

+        assertEquals(true, init.getRecompileGroovySource());

+        {

+            final List listCP = init.getClasspath();

+            assertEquals("File1", listCP.get(0));

+            assertEquals("Somewhere", listCP.get(1));

+        }

+        assertEquals(initOut, init.getOutput());

+        assertEquals(initTDFile, init.getTargetDirectory());

+        assertEquals(".jpp", init.getDefaultScriptExtension());

+        assertEquals(initJoint, init.getJointCompilationOptions());

+        assertEquals(initPPF, init.getPluginFactory());

+

+        final CompilerConfiguration config = new CompilerConfiguration(init);

+

+        assertEquals(WarningMessage.POSSIBLE_ERRORS, config.getWarningLevel());

+        assertEquals(true, config.getDebug());

+        assertEquals(false, config.getVerbose());

+        assertEquals(720, config.getTolerance());

+        assertEquals(234, config.getMinimumRecompilationInterval());

+        assertEquals("blarg.foo.WhatSit", config.getScriptBaseClass());

+        assertEquals("LEAD-123", config.getSourceEncoding());

+        assertEquals(CompilerConfiguration.POST_JDK5, config.getTargetBytecode());

+        assertEquals(true, config.getRecompileGroovySource());

+        {

+            final List listCP = config.getClasspath();

+            assertEquals("File1", listCP.get(0));

+            assertEquals("Somewhere", listCP.get(1));

+        }

+        assertEquals(initOut, config.getOutput());

+        assertEquals(initTDFile, config.getTargetDirectory());

+        assertEquals(".jpp", config.getDefaultScriptExtension());

+        assertEquals(initJoint, config.getJointCompilationOptions());

+        assertEquals(initPPF, config.getPluginFactory());

+

+    }

+

+    public void testCopyConstructor2() {

+        final CompilerConfiguration init = new CompilerConfiguration();

+

+        init.setWarningLevel(WarningMessage.POSSIBLE_ERRORS);

+        init.setDebug(false);

+        init.setVerbose(true);

+        init.setTolerance(55);

+        init.setMinimumRecompilationInterval(975);

+        init.setScriptBaseClass("");

+        init.setSourceEncoding("Gutenberg");

+        init.setTargetBytecode(CompilerConfiguration.PRE_JDK5);

+        init.setRecompileGroovySource(false);

+        init.setClasspath("");

+

+        final PrintWriter initOut = new PrintWriter(System.out);

+        init.setOutput(initOut);

+

+        final File initTDFile = new File("A wandering path");

+        init.setTargetDirectory(initTDFile);

+

+        assertEquals(WarningMessage.POSSIBLE_ERRORS, init.getWarningLevel());

+        assertEquals(false, init.getDebug());

+        assertEquals(true, init.getVerbose());

+        assertEquals(55, init.getTolerance());

+        assertEquals(975, init.getMinimumRecompilationInterval());

+        assertEquals("", init.getScriptBaseClass());

+        assertEquals("Gutenberg", init.getSourceEncoding());

+        assertEquals(CompilerConfiguration.PRE_JDK5, init.getTargetBytecode());

+        assertEquals(false, init.getRecompileGroovySource());

+        {

+            final List listCP = init.getClasspath();

+            assertNotNull(listCP);

+            assertEquals(0, listCP.size());

+        }

+        assertEquals(initOut, init.getOutput());

+        assertEquals(initTDFile, init.getTargetDirectory());

+

+        final CompilerConfiguration config = new CompilerConfiguration(init);

+

+        assertEquals(WarningMessage.POSSIBLE_ERRORS, config.getWarningLevel());

+        assertEquals(false, config.getDebug());

+        assertEquals(true, config.getVerbose());

+        assertEquals(55, config.getTolerance());

+        assertEquals(975, config.getMinimumRecompilationInterval());

+        assertEquals("", config.getScriptBaseClass());

+        assertEquals("Gutenberg", config.getSourceEncoding());

+        assertEquals(CompilerConfiguration.PRE_JDK5, config.getTargetBytecode());

+        assertEquals(false, config.getRecompileGroovySource());

+        {

+            final List listCP = config.getClasspath();

+            assertEquals(0, listCP.size());

+        }

+        assertEquals(initOut, config.getOutput());

+        assertEquals(initTDFile, config.getTargetDirectory());

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/control/io/NullWriterTest.groovy b/groovy/src/test/org/codehaus/groovy/control/io/NullWriterTest.groovy
new file mode 100644
index 0000000..a11419c
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/control/io/NullWriterTest.groovy
@@ -0,0 +1,15 @@
+package org.codehaus.groovy.control.io
+
+class NullWriterTest extends GroovyTestCase {
+
+    void testProperties() {
+        assert NullWriter.DEFAULT instanceof NullWriter
+    }
+
+    void testWriterMethodsForCoverage() {
+        def writer = NullWriter.DEFAULT
+        writer.close()
+        writer.flush()
+        writer.write((char[])null, 0, 0)
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/control/io/StringReaderSourceTest.groovy b/groovy/src/test/org/codehaus/groovy/control/io/StringReaderSourceTest.groovy
new file mode 100644
index 0000000..47d1442
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/control/io/StringReaderSourceTest.groovy
@@ -0,0 +1,12 @@
+package org.codehaus.groovy.control.io
+
+import org.codehaus.groovy.control.CompilerConfiguration
+
+class StringReaderSourceTest extends GroovyTestCase {
+
+    void testFileReaderCanNotBeReopened() {
+        def dummyString = "return false"
+        def writer = new StringReaderSource( dummyString, CompilerConfiguration.DEFAULT )
+        assert writer.canReopenSource()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/control/messages/SyntaxErrorMessageTest.java b/groovy/src/test/org/codehaus/groovy/control/messages/SyntaxErrorMessageTest.java
new file mode 100644
index 0000000..7474774
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/control/messages/SyntaxErrorMessageTest.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * Copyright (c) 2005 The Codehaus - http://groovy.codehaus.org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *
+ * See the License for the specific language governing permissions and limitations under the License.
+ *
+ */
+
+
+package org.codehaus.groovy.control.messages;
+
+import junit.framework.TestCase;
+import org.codehaus.groovy.control.SourceUnit;
+import org.codehaus.groovy.syntax.SyntaxException;
+
+public class SyntaxErrorMessageTest extends TestCase {
+
+    public void testSetsTheSourceLocatorOfItsSyntaxExceptionAsTheNameOfTheCorrespondingSourceUnitWhenInstantiated() {
+        SyntaxException syntaxException = new SyntaxException(someString(), -1, -1);
+        assertEquals("source locator", null, syntaxException.getSourceLocator());
+
+        String sourceUnitName = someString();
+        SourceUnit sourceUnit = SourceUnit.create(sourceUnitName, someString());
+
+        new SyntaxErrorMessage(syntaxException, sourceUnit);
+        assertEquals("source locator", sourceUnitName, syntaxException.getSourceLocator());
+    }
+
+    private String someString() {
+        return String.valueOf(Math.random() * System.currentTimeMillis());
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/dummy/ClassWithStaticMethod.groovy b/groovy/src/test/org/codehaus/groovy/dummy/ClassWithStaticMethod.groovy
new file mode 100644
index 0000000..3b24e66
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/dummy/ClassWithStaticMethod.groovy
@@ -0,0 +1,9 @@
+package org.codehaus.groovy.dummy;
+
+/**
+ * Class used by groovy.bugs.StaticMethodImportBug.
+ * Bug reference: Explicit import needed to call static method, GROOVY-935
+ */
+class ClassWithStaticMethod {
+    static boolean staticMethod() { return true }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/dummy/FooHandler.java b/groovy/src/test/org/codehaus/groovy/dummy/FooHandler.java
new file mode 100644
index 0000000..df47e38
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/dummy/FooHandler.java
@@ -0,0 +1,11 @@
+package org.codehaus.groovy.dummy;
+
+import java.io.Reader;
+
+/**
+ * @author Robert Fuller
+ * @version $Revision$
+ */
+public interface FooHandler {
+    void handle(Reader reader);
+}
diff --git a/groovy/src/test/org/codehaus/groovy/reflection/WeakMapTest.groovy b/groovy/src/test/org/codehaus/groovy/reflection/WeakMapTest.groovy
new file mode 100644
index 0000000..6704f65
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/reflection/WeakMapTest.groovy
@@ -0,0 +1,34 @@
+package org.codehaus.groovy.reflection
+
+class WeakMapTest extends GroovyTestCase{
+   void testClassUnload () {
+       GroovyShell shell = new GroovyShell()
+       int SIZE = 100
+       for (int i = 0; i != SIZE; ++i) {
+           Object s = shell.parse ("""
+              class A extends B {
+                def B callMe (b) {
+                  b instanceof A ? this : b
+                }
+              }
+
+              class B {
+              }
+
+              new A ().callMe ("lambda")
+           """)
+
+           ReflectionCache.isAssignableFrom s.class, s.class.superclass
+           if (i % 10 == 0) {
+               System.gc ()
+             println "${i} ${ReflectionCache.assignableMap.size()} ${ReflectionCache.CACHED_CLASS_MAP.size()}"
+           }
+
+           shell.classLoader.clearCache()
+           GroovySystem.metaClassRegistry.removeMetaClass s.class.superclass
+           GroovySystem.metaClassRegistry.removeMetaClass s.class
+       }
+
+       println "${SIZE} ${ReflectionCache.assignableMap.size()} ${ReflectionCache.CACHED_CLASS_MAP.size()}"
+   }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/CategoryForIteratorTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/CategoryForIteratorTest.groovy
new file mode 100644
index 0000000..8974cf7
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/CategoryForIteratorTest.groovy
@@ -0,0 +1,58 @@
+package org.codehaus.groovy.runtime;
+
+/*
+ * Test whether the Invoker includes categories when 
+ * trying to find an iterator (via the method iterator())
+ */ 
+class CategoryForIteratorTest extends GroovyTestCase {
+
+	def identity = { val -> val }
+	def c
+	def countCalls = { c = c + 1 }
+
+	void setUp() {
+		c = 0
+	}
+	
+	/*
+	 * Ensure that without the iterator category a
+	 * one-element collection is returned that
+	 * results in one call to the countCalls closure
+	 */
+	void testWithoutIteratorCategory() {
+		identity.each countCalls
+		assert c == 1
+	}
+	/*
+	 * When using the IteratorCategory below we get an
+	 * iterator that does no iteration. So the count
+	 * has to be 0
+	 */
+	void testWithIteratorCategory() {
+		use(IteratorCategory) {
+			c = 0
+			identity.each countCalls
+			assert c == 0
+		}
+	}
+}
+
+/*
+ * The category simply adds an iterator()-method returning
+ * the null iterator defined below
+ */
+class IteratorCategory {
+	static Iterator iterator(Closure c) { 
+		return new TestIterator()
+	}
+}
+
+/*
+ * This iterator returns 0 elements, allowing us to distinguish
+ * from the default collection-iterator
+ */
+class TestIterator implements Iterator {
+    public boolean hasNext() { return false }
+    public Object next() { return null }
+    public void remove() {}
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/DefaultGroovyMethodsTest.java b/groovy/src/test/org/codehaus/groovy/runtime/DefaultGroovyMethodsTest.java
new file mode 100644
index 0000000..8ae701c
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/DefaultGroovyMethodsTest.java
@@ -0,0 +1,173 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+import groovy.util.GroovyTestCase;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URI;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author Marc Guillemot
+ * @version $Revision$
+ */
+public class DefaultGroovyMethodsTest extends GroovyTestCase {
+
+    public void testPrint() throws Exception {
+        Map map = new HashMap();
+        map.put("bob", "drools");
+        map.put("james", "geronimo");
+        List list = new ArrayList();
+        list.add(map);
+
+        /** @todo fix this! */
+        //assertConsoleOutput(list, "[['bob':'drools', 'james':'geronimo']]");
+    }
+
+    public void testIncrementString() throws Exception {
+        String original = "z";
+        String answer = DefaultGroovyMethods.next(original);
+
+        System.out.println(answer);
+        assertTrue(answer.compareTo(original) > 0);
+    }
+
+    public void testDecrementString() throws Exception {
+        String original = "a";
+        String answer = DefaultGroovyMethods.previous(original);
+
+        System.out.println(answer);
+        assertTrue(ScriptBytecodeAdapter.compareLessThan(answer, original));
+    }
+
+    public void testToMethods() throws Exception {
+        Number n = new Long(7);
+
+        assertEquals(DefaultGroovyMethods.toInteger("1"), new Integer(1));
+        assertEquals(DefaultGroovyMethods.toInteger(n), new Integer(7));
+        assertEquals(DefaultGroovyMethods.toLong("1"), new Long(1));
+        assertEquals(DefaultGroovyMethods.toLong(n), new Long(7));
+
+        assertEquals(DefaultGroovyMethods.toFloat("1"), new Float(1));
+        assertEquals(DefaultGroovyMethods.toFloat(n), new Float(7));
+        assertEquals(DefaultGroovyMethods.toDouble("1"), new Double(1));
+        assertEquals(DefaultGroovyMethods.toDouble(n), new Double(7));
+
+        assertEquals(DefaultGroovyMethods.toBigInteger("1"), new BigInteger("1"));
+        assertEquals(DefaultGroovyMethods.toBigInteger(n), new BigInteger("7"));
+        assertEquals(DefaultGroovyMethods.toBigDecimal("1"), new BigDecimal("1"));
+        assertEquals(DefaultGroovyMethods.toBigDecimal(n), new BigDecimal("7"));
+
+        assertEquals(DefaultGroovyMethods.toURL("http://example.org/"), new URL("http://example.org/"));
+        assertEquals(DefaultGroovyMethods.toURI("http://example.org/"), new URI("http://example.org/"));
+
+        assertEquals(DefaultGroovyMethods.toBoolean("True"), Boolean.TRUE);
+        assertEquals(DefaultGroovyMethods.toBoolean("Y"), Boolean.TRUE);
+        assertEquals(DefaultGroovyMethods.toBoolean(" y "), Boolean.TRUE);
+        assertEquals(DefaultGroovyMethods.toBoolean("1"), Boolean.TRUE);
+        assertEquals(DefaultGroovyMethods.toBoolean("false"), Boolean.FALSE);
+        assertEquals(DefaultGroovyMethods.toBoolean("n"), Boolean.FALSE);
+        assertEquals(DefaultGroovyMethods.toBoolean("0"), Boolean.FALSE);
+    }
+
+    public void testIsMethods() throws Exception {
+        String intStr = "123";
+        String floatStr = "1.23E-1";
+        String nonNumberStr = "ONE";
+
+        assertTrue(DefaultGroovyMethods.isInteger(intStr));
+        assertFalse(DefaultGroovyMethods.isInteger(floatStr));
+        assertFalse(DefaultGroovyMethods.isInteger(nonNumberStr));
+        assertTrue(DefaultGroovyMethods.isLong(intStr));
+        assertFalse(DefaultGroovyMethods.isLong(floatStr));
+        assertFalse(DefaultGroovyMethods.isLong(nonNumberStr));
+
+        assertTrue(DefaultGroovyMethods.isFloat(intStr));
+        assertTrue(DefaultGroovyMethods.isFloat(floatStr));
+        assertFalse(DefaultGroovyMethods.isFloat(nonNumberStr));
+        assertTrue(DefaultGroovyMethods.isDouble(intStr));
+        assertTrue(DefaultGroovyMethods.isDouble(floatStr));
+        assertFalse(DefaultGroovyMethods.isDouble(nonNumberStr));
+
+        assertTrue(DefaultGroovyMethods.isBigInteger(intStr));
+        assertFalse(DefaultGroovyMethods.isBigInteger(floatStr));
+        assertFalse(DefaultGroovyMethods.isBigInteger(nonNumberStr));
+        assertTrue(DefaultGroovyMethods.isBigDecimal(intStr));
+        assertTrue(DefaultGroovyMethods.isBigDecimal(floatStr));
+        assertFalse(DefaultGroovyMethods.isBigDecimal(nonNumberStr));
+        assertTrue(DefaultGroovyMethods.isNumber(intStr));
+        assertTrue(DefaultGroovyMethods.isNumber(floatStr));
+        assertFalse(DefaultGroovyMethods.isNumber(nonNumberStr));
+    }
+
+
+    public void testDownto() {
+        final int[] count = new int[]{0};
+        final Closure closure = new Closure(null) {
+            public Object doCall(final Object params) {
+                count[0]++;
+                return null;
+            }
+        };
+
+        DefaultGroovyMethods.downto(new BigInteger("1"), new BigDecimal("0"), closure);
+        assertEquals(count[0], 2);
+
+        count[0] = 0;
+
+        DefaultGroovyMethods.downto(new BigInteger("1"), new BigDecimal("0.123"), closure);
+        assertEquals(count[0], 1);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/DummyBean.java b/groovy/src/test/org/codehaus/groovy/runtime/DummyBean.java
new file mode 100644
index 0000000..f968548
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/DummyBean.java
@@ -0,0 +1,137 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.runtime;
+
+import java.awt.*;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A bean used by the test cases
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @author <a href="mailto:shemnon@yahoo.com">Danno Ferrin</a>
+ * @version $Revision$
+ */
+public class DummyBean {
+    private String name = "James";
+    private Integer i = new Integer(123);
+    private Map dynamicProperties = new HashMap();
+    private Point point;
+    private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
+
+    public DummyBean() {
+    }
+
+    public DummyBean(String name) {
+        this.name = name;
+    }
+
+    public DummyBean(String name, Integer i) {
+        this.name = name;
+        this.i = i;
+    }
+
+    public void addPropertyChangeListener(PropertyChangeListener listener) {
+        changeSupport.addPropertyChangeListener(listener);
+    }
+
+    public Integer getI() {
+        return i;
+    }
+
+    public void setI(Integer i) {
+        changeSupport.firePropertyChange("i", this.i, this.i = i);
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        changeSupport.firePropertyChange("name", this.name, this.name = name);
+    }
+
+    // dynamic properties
+    public Object get(String property) {
+        return dynamicProperties.get(property);
+    }
+
+    public void set(String property, Object newValue) {
+        dynamicProperties.put(property, newValue);
+    }
+
+    public static String dummyStaticMethod(String text) {
+        return text.toUpperCase();
+    }
+
+    public boolean equals(Object that) {
+        if (that instanceof DummyBean) {
+            return equals((DummyBean) that);
+        }
+        return false;
+    }
+
+    public boolean equals(DummyBean that) {
+        return this.name.equals(that.name) && this.i.equals(that.i);
+    }
+
+    public String toString() {
+        return super.toString() + "[name=" + name + ";i=" + i + "]";
+    }
+
+    public Point getPoint() {
+        return point;
+    }
+
+    public void setPoint(Point point) {
+        changeSupport.firePropertyChange("point", this.point, this.point = point);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/EachWithReaderAndInputStreamTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/EachWithReaderAndInputStreamTest.groovy
new file mode 100644
index 0000000..c5bc159
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/EachWithReaderAndInputStreamTest.groovy
@@ -0,0 +1,76 @@
+package org.codehaus.groovy.runtime
+
+import java.io.StringReader
+import java.io.StringBufferInputStream
+
+/** 
+ * Test .each with Reader and InputStream
+ * 
+ * @author <a href="mailto:joachim.baumann@xinaris.de">Joachim Baumann</a>
+ * @version $Revision: $
+ */
+class EachWithReaderAndInputStreamTest extends GroovyTestCase {
+	/**
+	 * The following instances are used in testing the file operations
+	 */
+
+	String multiLineVal = """
+This text
+as can be seen
+has multiple lines
+and not one punctuation mark
+"""
+
+	// Our file instance
+	def File file;
+	
+	void setUpFile() {
+		// Setup guarantees us that we use a non-existent file
+		file = File.createTempFile("unitTest", ".txt") 
+		assert file.exists() == true
+		//println file.canonicalPath
+		assert file.length() == 0L
+		file << multiLineVal
+	}
+	void tearDownFile() {
+		// we remove our temporary file
+		def deleted = false
+		while(deleted == false)
+			deleted = file.delete()
+		assert file.exists() == false
+	}
+
+	void testEachForStringBufferInputStream(){
+		def ist = new StringBufferInputStream(multiLineVal)
+		def readVal = ""
+		ist.each { readVal += (char)it }
+		assert readVal == multiLineVal
+	}
+	 		
+	void testEachForStringReader(){
+		def ir = new StringReader(multiLineVal)
+		def readVal = ""
+		ir.each { readVal += it + "\n" }
+		assert readVal == multiLineVal
+	}
+
+	void testEachForFileWithInputStream() {
+		setUpFile()
+		def readVal = ""
+		file.withInputStream{ is ->
+			is.each { readVal += (char)it }
+		}
+		tearDownFile()
+		assert readVal == multiLineVal
+	}
+
+	void testEachForFileWithReader() {
+		setUpFile()
+		def readVal = ""
+		file.withReader{ reader ->
+			reader.each { readVal += it + "\n" }
+		}
+		tearDownFile()
+		assert readVal == multiLineVal
+	}
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/FileAppendTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/FileAppendTest.groovy
new file mode 100644
index 0000000..b53214d
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/FileAppendTest.groovy
@@ -0,0 +1,218 @@
+package org.codehaus.groovy.runtime;
+
+import java.io.File
+import java.io.Reader
+
+/** 
+ * Test File append and left shift methods in Groovy
+ * 
+ * @author <a href="mailto:joachim.baumann@xinaris.de">Joachim Baumann</a>
+ * @version $Revision$
+ */
+class FileAppendTest extends GroovyTestCase {
+	/**
+	 * The following instances are used in testing the file writes
+	 */
+	static text = """
+			<groovy>
+			  <things>
+			    <thing>Jelly Beans</thing>
+			  </things>
+			  <music>
+			    <tune>The 59th Street Bridge Song</tune>
+			  </music>
+			  <characters>
+			    <character name="Austin Powers">
+			       <enemy>Dr. Evil</enemy>
+			       <enemy>Mini Me</enemy>
+			    </character>
+			  </characters>
+			</groovy>
+			"""
+	static gPathResult = new XmlSlurper().parseText(text)
+	static gPathWriteTo;
+	{
+		StringWriter sw = new StringWriter()
+		gPathResult.writeTo(sw)
+		gPathWriteTo = sw.toString()
+	}
+	
+	// see below for class definition
+	def testInstance = new TestClass()
+
+	// Our file instance
+	def File file;
+	
+	void setUp() {
+		// Setup guarantees us that we use a non-existent file
+		file = File.createTempFile("unitTest", ".txt") 
+		assert file.exists() == true
+		//println file.canonicalPath
+		assert file.length() == 0L
+	}
+	void tearDown() {
+		// we remove our temporary file
+		file.deleteOnExit()
+	}
+
+	void testAppendString(){
+		def expected
+		
+		// test new
+		file.append(text)
+		expected = text
+		assert hasContents(file, expected)
+		
+		// test existing
+		file.append(text)
+		expected += text
+		assert hasContents(file, expected)
+	}
+	 		
+	void testAppendObjectToString(){
+		def expected
+		
+		// test new
+		file.append(testInstance)
+		expected = testInstance.toString()
+		assert hasContents(file, expected)
+		
+		// test existing
+		file.append(testInstance)
+		expected += testInstance.toString()
+		assert hasContents(file, expected)
+	}
+
+	void testappendWritable(){
+		def expected
+		
+		// test new
+		file.append(gPathResult)
+		expected = gPathWriteTo
+		assert hasContents(file, expected)
+		
+		// test existing
+		file.append(gPathResult)
+		expected += gPathWriteTo
+		assert hasContents(file, expected)
+	}
+
+	void testappendMixed(){
+		def expected
+		
+		// test new
+		file.append(text)
+		expected = text
+		assert hasContents(file, expected)
+		
+		file.append(testInstance)
+		expected += testInstance.toString()
+		assert hasContents(file, expected)
+		
+		file.append(gPathResult)
+		expected += gPathWriteTo
+		assert hasContents(file, expected)
+		
+		// test existing
+		file.append(gPathResult)
+		expected += gPathWriteTo
+		assert hasContents(file, expected)
+
+		file.append(testInstance)
+		expected += testInstance.toString()
+		assert hasContents(file, expected)
+
+		file.append(text)
+		expected += text
+		assert hasContents(file, expected)
+	}
+
+	void testLeftShiftString(){
+		def expected
+		
+		// test new
+		file << text
+		expected = text
+		assert hasContents(file, expected)
+		
+		// test existing
+		file << text
+		expected += text
+		assert hasContents(file, expected)
+	}
+			
+	void testLeftShiftObjectToString(){
+		def expected
+		
+		// test new
+		file << testInstance
+		expected = testInstance.toString()
+		assert hasContents(file, expected)
+		
+		// test existing
+		file << testInstance
+		expected += testInstance.toString()
+		assert hasContents(file, expected)
+	}
+
+	void testLeftShiftWritable(){
+		def expected
+		
+		// test new
+		file << gPathResult
+		expected = gPathWriteTo
+		assert hasContents(file, expected)
+		
+		// test existing
+		file << gPathResult
+		expected += gPathWriteTo
+		assert hasContents(file, expected)
+	}		 
+
+	void testLeftShiftMixed(){
+		def expected
+		
+		// test new
+		file << text
+		expected = text
+		assert hasContents(file, expected)
+		
+		file << testInstance
+		expected += testInstance.toString()
+		assert hasContents(file, expected)
+		
+		file << gPathResult
+		expected += gPathWriteTo
+		assert hasContents(file, expected)
+		
+		// test existing
+		file << gPathResult
+		expected += gPathWriteTo
+		assert hasContents(file, expected)
+
+		file << testInstance
+		expected += testInstance.toString()
+		assert hasContents(file, expected)
+
+		file << text
+		expected += text
+		assert hasContents(file, expected)
+	}
+
+	boolean hasContents(File f, String expected)
+	{
+		assert file.length() == expected.length()
+		// read contents the Java way
+		char[] cbuf = new char[expected.length()];
+		def fileReader = new FileReader(file)
+		fileReader.read(cbuf)
+		return expected == String.valueOf(cbuf)
+	}
+}
+
+class TestClass {
+	def testString = "TestThis"
+	public String toString() {
+		super.toString() + ": " + testString
+	}
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/FileLeftShiftTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/FileLeftShiftTest.groovy
new file mode 100644
index 0000000..fa6db01
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/FileLeftShiftTest.groovy
@@ -0,0 +1,10 @@
+package org.codehaus.groovy.runtime
+
+class FileLeftShiftTest extends GroovyTestCase {
+    void testFileLeftShift() {
+        new File("target/test-classes/MyFileLeftShiftTest.txt").delete()
+        new File("target/test-classes/MyFileLeftShiftTest.txt") << "This is " << "groovy"
+        assertEquals(new File("target/test-classes/MyFileLeftShiftTest.txt").text, "This is groovy")
+        new File("target/test-classes/MyFileLeftShiftTest.txt").delete()
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/GroovyCategoryTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/GroovyCategoryTest.groovy
new file mode 100644
index 0000000..75d9436
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/GroovyCategoryTest.groovy
@@ -0,0 +1,44 @@
+package org.codehaus.groovy.runtime
+
+class GroovyCategoryTest extends GroovyTestCase {
+    void testUseWithVarArg() {
+        // Try out the single class case
+        use(Category1) {
+            assert "HeLlO".upper() == "HELLO"
+        }
+
+        // Try out the list case
+        use([Category1, Category2]) {
+            assert "HeLlO".upper() == "HELLO"
+            assert "HeLlO".lower() == "hello"
+        }
+
+        // Try out the vararg version
+        use(Category1, Category2) {
+            assert "HeLlO".upper() == "HELLO"
+            assert "HeLlO".lower() == "hello"
+        }
+
+        // This should fail
+        try {
+            use(Category1)
+            fail()
+        } catch (IllegalArgumentException e) {
+        }
+
+        // And so should this
+        try {
+            use(Category1, Category2)
+            fail()
+        } catch (IllegalArgumentException e) {
+        }
+    }
+}
+
+class Category1 {
+    static String upper(String message) {return message.toUpperCase()}
+}
+
+class Category2 {
+    static String lower(String message) {return message.toLowerCase()}
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/InheritedInterfaceMethodTest.java b/groovy/src/test/org/codehaus/groovy/runtime/InheritedInterfaceMethodTest.java
new file mode 100644
index 0000000..dea460a
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/InheritedInterfaceMethodTest.java
@@ -0,0 +1,66 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package org.codehaus.groovy.runtime;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class InheritedInterfaceMethodTest extends TestCase {
+
+    public void testInvokeNewListMethodOnArrayList() {
+        List list = new ArrayList();
+        Object answer = InvokerHelper.invokeMethod(list, "count", new Object[]{"123"});
+        assertEquals(new Integer(0), answer);
+
+        System.out.println("Found: " + answer);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/InterfaceConversionTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/InterfaceConversionTest.groovy
new file mode 100644
index 0000000..952709a
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/InterfaceConversionTest.groovy
@@ -0,0 +1,28 @@
+package org.codehaus.groovy.runtime;

+

+class InterfaceConversionTest extends GroovyTestCase {

+ 

+  void testClosureConversion(){

+	def c1 = {Object[] args -> args?.length}

+	def c2 = c1 as InterfaceConversionTestFoo

+	assert !(c1 instanceof InterfaceConversionTestFoo)

+	assert c2 instanceof InterfaceConversionTestFoo

+	assert c2.a() == null

+	assert c2.b(null) == 1

+  }

+  

+  void testMapConversion() {  

+	def m1 = [a:{1}, b:{2}]

+	def m2 = m1 as InterfaceConversionTestFoo

+	

+	assert !(m1 instanceof InterfaceConversionTestFoo)

+	assert m2 instanceof InterfaceConversionTestFoo

+	assert m2.a() == 1

+	assert m2.b(null) == 2

+  }

+}

+ 

+interface InterfaceConversionTestFoo {

+    def a();

+    def b(Integer i);

+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/InvokeConstructorTest.java b/groovy/src/test/org/codehaus/groovy/runtime/InvokeConstructorTest.java
new file mode 100644
index 0000000..30ba9a1
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/InvokeConstructorTest.java
@@ -0,0 +1,100 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.GString;
+import groovy.util.GroovyTestCase;
+
+/**
+ * Tests method invocation
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class InvokeConstructorTest extends GroovyTestCase {
+
+    protected Invoker invoker = new Invoker();
+
+    public void testInvokeConstructorNoParams() throws Throwable {
+        assertConstructor(new DummyBean(), new Object[0]);
+    }
+
+    public void testInvokeConstructorOneParam() throws Throwable {
+        assertConstructor(new DummyBean("Bob"), "Bob");
+    }
+
+    public void testInvokeConstructorOneParamWhichIsNull() throws Throwable {
+        assertConstructor(new DummyBean("Bob", new Integer(1707)), new Object[]{"Bob", new Integer(1707)});
+    }
+
+    public void testConstructorWithGStringCoercion() throws Throwable {
+        GString gstring = new GString(new Object[]{new Integer(123)}) {
+            public String[] getStrings() {
+                return new String[]{""};
+            }
+        };
+
+        Object expected = new Long(gstring.toString());
+
+        assertConstructor(expected, new Object[]{gstring});
+    }
+
+    protected void assertConstructor(Object expected, Object arguments) throws Throwable {
+        Object value = invoke(expected.getClass(), arguments);
+
+        assertEquals("Invoking overloaded method for arguments: " + InvokerHelper.toString(arguments), expected, value);
+    }
+
+    protected Object invoke(Class type, Object args) throws Throwable {
+        try {
+            return invoker.invokeConstructorOf(type, args);
+        }
+        catch (InvokerInvocationException e) {
+            throw e.getCause();
+        }
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/InvokeGroovyMethodTest.java b/groovy/src/test/org/codehaus/groovy/runtime/InvokeGroovyMethodTest.java
new file mode 100644
index 0000000..7c4d6be
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/InvokeGroovyMethodTest.java
@@ -0,0 +1,112 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+import groovy.util.GroovyTestCase;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Tests method invocation
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class InvokeGroovyMethodTest extends GroovyTestCase {
+
+    protected Invoker invoker = new Invoker();
+    private StringBuffer buffer;
+
+    // Method invocation tests
+    //-------------------------------------------------------------------------
+
+    public void testInvokeMethodNoParams() throws Throwable {
+        buffer = new StringBuffer();
+
+        List list = new ArrayList();
+        list.add("abc");
+        list.add("def");
+
+        invoker.invokeMethod(list, "each", new Closure(this) {
+            protected Object doCall(Object arguments) {
+                buffer.append(arguments.toString());
+                return null;
+            }
+        });
+
+        assertEquals("buffer", "abcdef", buffer.toString());
+    }
+
+    public void testMatchesWithObject() throws Throwable {
+        assertMatches(new Integer(1), new Integer(1), true);
+        assertMatches(new Integer(1), new Integer(2), false);
+    }
+
+    public void testMatchesWithClass() throws Throwable {
+        assertMatches(new Integer(1), Integer.class, true);
+        assertMatches(new Integer(1), Number.class, true);
+        assertMatches(new Integer(1), Double.class, false);
+    }
+
+    public void testMatchesWithList() throws Throwable {
+        assertMatches(new Integer(1), Arrays.asList(new Object[]{new Integer(2), new Integer(1)}), true);
+        assertMatches(new Integer(1), Arrays.asList(new Object[]{new Integer(2), new Integer(3)}), false);
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+    protected void assertMatches(Object switchValue, Object caseValue, boolean expected) {
+        assertEquals(
+                "Switch on: " + switchValue + " Case: " + caseValue,
+                expected,
+                ((Boolean) (InvokerHelper.invokeMethod(caseValue, "isCase", switchValue))).booleanValue());
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/InvokeMethodTest.java b/groovy/src/test/org/codehaus/groovy/runtime/InvokeMethodTest.java
new file mode 100644
index 0000000..51704cf
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/InvokeMethodTest.java
@@ -0,0 +1,451 @@
+/*
+ * $Id$
+ *
+ * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+ *
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met:
+ *  1. Redistributions of source code must retain copyright statements and
+ * notices. Redistributions must also contain a copy of this document.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *  3. The name "groovy" must not be used to endorse or promote products
+ * derived from this Software without prior written permission of The Codehaus.
+ * For written permission, please contact info@codehaus.org.
+ *  4. Products derived from this Software may not be called "groovy" nor may
+ * "groovy" appear in their names without prior written permission of The
+ * Codehaus. "groovy" is a registered trademark of The Codehaus.
+ *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.GString;
+import groovy.lang.GroovyRuntimeException;
+import groovy.lang.IntRange;
+import groovy.util.GroovyTestCase;
+import junit.framework.AssertionFailedError;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import java.math.BigDecimal;
+import java.text.SimpleDateFormat;
+import java.util.*;
+
+/**
+ * Tests method invocation
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class InvokeMethodTest extends GroovyTestCase {
+
+    protected Invoker invoker = InvokerHelper.getInstance();
+
+    // Method invocation tests
+    //-------------------------------------------------------------------------
+
+    public void testInvokeMethodNoParams() throws Throwable {
+        Object value = invoke(this, "mockCallWithNoParams", null);
+        assertEquals("return value", "NoParams", value);
+
+        value = invoke(this, "mockCallWithNoParams", new Object[0]);
+        assertEquals("return value", "NoParams", value);
+    }
+
+    public void testInvokeMethodOneParam() throws Throwable {
+        Object value = invoke(this, "mockCallWithOneParam", "abc");
+        assertEquals("return value", "OneParam", value);
+    }
+
+    public void testInvokeMethodOneParamWhichIsNull() throws Throwable {
+        Object value = invoke(this, "mockCallWithOneNullParam", new Object[]{null});
+        assertEquals("return value", "OneParamWithNull", value);
+
+        value = invoke(this, "mockCallWithOneNullParam", null);
+        assertEquals("return value", "OneParamWithNull", value);
+    }
+
+    public void testInvokeOverloadedMethodWithOneParamWhichIsNull() throws Throwable {
+        Object value = invoke(this, "mockOverloadedMethod", new Object[]{null});
+        assertEquals("return value", "Object", value);
+    }
+
+    public void testInvokeMethodOneCollectionParameter() throws Throwable {
+        Object[] foo = {"a", "b", "c"};
+
+        Object value = invoke(this, "mockCallWithOneCollectionParam", new Object[]{foo});
+        assertEquals("return value", new Integer(3), value);
+
+        List list = new ArrayList();
+        list.add("a");
+        list.add("b");
+        value = invoke(this, "mockCallWithOneCollectionParam", list);
+        assertEquals("return value", new Integer(2), value);
+    }
+
+    public void testInvokePrintlnMethod() throws Throwable {
+        Object value = invoke(System.out, "println", "testing System.out.println...");
+        assertEquals("return value", null, value);
+    }
+
+    public void testMethodChooserNull() throws Throwable {
+        assertMethodChooser("Object", new Object[]{null});
+    }
+
+    public void testMethodChooserNoParams() throws Throwable {
+        assertMethodChooser("void", null);
+    }
+
+    public void testMethodChooserObject() throws Throwable {
+        assertMethodChooser("Object", new Object());
+        assertMethodChooser("Object", new Date());
+    }
+
+    public void testMethodChooserString_FAILS() throws Throwable {
+        if (notYetImplemented()) return;
+        assertMethodChooser("String", "foo");
+        assertMethodChooser("String", new StringBuffer());
+        assertMethodChooser("String", new Character('a'));
+    }
+
+    public void testMethodChooserNumber() throws Throwable {
+        assertMethodChooser("Number", new Integer(2));
+        assertMethodChooser("Number", new Double(2));
+    }
+
+    public void testMethodChooserTwoParams() throws Throwable {
+        List list = new ArrayList();
+        list.add("foo");
+        list.add("bar");
+        assertMethodChooser("Object,Object", list.toArray());
+
+        Object[] blah = {"a", "b"};
+        assertMethodChooser("Object,Object", blah);
+    }
+
+    public void testInstanceofWorksForArray() {
+        Class type = Object[].class;
+        Object value = new Object[1];
+        assertTrue("instanceof works for array", type.isInstance(value));
+    }
+
+    public void testMethodChooserTwoParamsWithSecondAnObjectArray() throws Throwable {
+        Object[] blah = {"a", new Object[]{"b"}
+        };
+        assertMethodChooser("Object,Object[]", blah);
+    }
+
+    public void testCollectionMethods() throws Throwable {
+        Object list = InvokerHelper.createList(new Object[]{"a", "b"});
+
+        Object value = invoke(list, "size", null);
+        assertEquals("size of collection", new Integer(2), value);
+
+        value = invoke(list, "contains", "a");
+        assertEquals("contains method", Boolean.TRUE, value);
+    }
+
+    public void testNewMethods() throws Throwable {
+        Object value = invoke("hello", "size", null);
+        assertEquals("size of string", new Integer(5), value);
+    }
+
+    public void testStaticMethod() throws Throwable {
+        Object value = invoke(DummyBean.class, "dummyStaticMethod", "abc");
+        assertEquals("size of string", "ABC", value);
+    }
+
+    public void testBaseClassMethod() throws Throwable {
+        Object object = new DummyBean();
+        Object value = invoke(object, "toString", null);
+        assertEquals("toString", object.toString(), value);
+    }
+
+    //SPG modified to reflect DefaultGroovyMethod name change and expected result from
+    //Integer/Integer division.
+    public void testDivideNumbers() throws Throwable {
+        assertMethodCall(new Double(10), "div", new Double(2), new Double(5));
+        assertMethodCall(new Double(10), "div", new Integer(2), new Double(5));
+        assertMethodCall(new Integer(10), "div", new Double(2), new Double(5));
+        assertMethodCall(new Integer(10), "div", new Integer(2), new java.math.BigDecimal("5"));
+    }
+
+    public void testBaseFailMethod() throws Throwable {
+        try {
+            invoke(this, "fail", "hello");
+        } catch (AssertionFailedError e) {
+            // worked
+        }
+    }
+
+    public void testToArrayOnList() throws Throwable {
+        List object = new ArrayList();
+        object.add("Hello");
+
+        Object[] value = (Object[]) invoke(object, "toArray", null);
+        assertArrayEquals(object.toArray(), value);
+        assertEquals(1, value.length);
+        assertEquals("Hello", value[0]);
+
+        value = (Object[]) invoke(object, "toArray", new Object[0]);
+        assertArrayEquals(object.toArray(), value);
+    }
+
+    public void testInvalidOverloading() throws Throwable {
+        try {
+            invoke(this, "badOverload", new Object[]{"a", "b"});
+            fail("Should fail as an unambiguous method is invoked");
+        }
+        catch (GroovyRuntimeException e) {
+            System.out.println("Caught: " + e);
+        }
+    }
+
+    public void testPlusWithNull() throws Throwable {
+        String param = "called with: ";
+        Object value = invoke(param, "plus", new Object[]{null});
+        assertEquals("called with null", param + null, value);
+    }
+
+    public void testCallIntMethodWithInteger() throws Throwable {
+        Object value = invoke(this, "overloadedRemove", new Object[]{new Integer(5)});
+        assertEquals("called with integer", "int5", value);
+    }
+
+    public void testCallListRemove() throws Throwable {
+        List list = new ArrayList();
+        list.add("foo");
+        list.add("bar");
+
+        invoke(list, "remove", new Object[]{new Integer(0)});
+
+        assertEquals("Should have just 1 item left: " + list, 1, list.size());
+    }
+
+    public void testCoerceGStringToString() throws Throwable {
+        GString param = new GString(new Object[]{"James"}) {
+            public String[] getStrings() {
+                return new String[]{"Hello "};
+            }
+        };
+        Object value = invoke(this, "methodTakesString", new Object[]{param});
+        assertEquals("converted GString to string", param.toString(), value);
+    }
+
+    public void testCoerceGStringToStringOnGetBytes() throws Throwable {
+        GString param = new GString(new Object[]{"US-ASCII"}) {
+            public String[] getStrings() {
+                return new String[]{""};
+            }
+        };
+        Object value = invoke("test", "getBytes", new Object[]{param});
+        assertEquals("converted GString to string", "test".getBytes("US-ASCII").getClass(), value.getClass());
+    }
+
+    public void testBadBDToDoubleCoerce() throws Throwable {
+        try {
+            invoke(Math.class, "floor", new BigDecimal("1.7E309"));
+        } catch (IllegalArgumentException e) {
+            assertTrue("Math.floor(1.7E309) should fail because it is out of range for a Double. "
+                    + e, e.getMessage().indexOf("out of range") > 0);
+            return;
+        }
+        fail("Math.floor(1.7E309) should fail because it is out of range for a Double.");
+    }
+
+    public void testClassMethod() throws Throwable {
+        Class c = String.class;
+        Object value = invoke(c, "getName", null);
+        assertEquals("Class.getName()", c.getName(), value);
+        c = getClass();
+        value = invoke(c, "getName", null);
+        assertEquals("Class.getName()", c.getName(), value);
+    }
+
+    public void testProtectedMethod() throws Throwable {
+        String param = "hello";
+        Object value = invoke(this, "aProtectedMethod", param);
+        assertEquals("protected method call", aProtectedMethod(param), value);
+    }
+
+    public void testPrivateMethod() throws Throwable {
+        String param = "hello";
+        Object value = invoke(this, "aPrivateMethod", param);
+        assertEquals("private method call", aPrivateMethod(param), value);
+    }
+
+    public void testStringSubstringMethod() throws Throwable {
+        String object = "hello";
+        Object value = invoke(object, "substring", new Integer(2));
+        assertEquals("substring(2)", object.substring(2), value);
+
+        value = invoke(object, "substring", new Object[]{new Integer(1), new Integer(3)});
+        assertEquals("substring(1,3)", object.substring(1, 3), value);
+    }
+
+    public void testListGetWithRange() throws Throwable {
+        List list = Arrays.asList(new Object[]{"a", "b", "c"});
+        Object range = new IntRange(0, 2);
+        Object value = invoke(list, "getAt", range);
+        assertTrue("Returned List: " + value, value instanceof List);
+        List retList = (List) value;
+        assertEquals("List size", 3, retList.size());
+    }
+
+    public void testSetLenientOnDateFormat() throws Throwable {
+        SimpleDateFormat a = new SimpleDateFormat("MM/dd/yyyy");
+
+        Object value = invoke(a, "setLenient", new Object[]{Boolean.FALSE});
+        assertEquals("void method", null, value);
+    }
+
+    public void testInvokeUnknownMethod() throws Throwable {
+        try {
+            Object value = invoke(this, "unknownMethod", "abc");
+            fail("Should have thrown an exception");
+        }
+        catch (GroovyRuntimeException e) {
+            // worked
+        }
+    }
+
+    public void testInvokeMethodWithWrongNumberOfParameters() throws Throwable {
+        try {
+            Object[] args = {"a", "b"};
+            invoke(this, "unknownMethod", args);
+            fail("Should have thrown an exception");
+        }
+        catch (GroovyRuntimeException e) {
+            // worked
+        }
+    }
+
+    public void testInvokeMethodOnNullObject() throws Throwable {
+        try {
+            invoke(null, "mockCallWithNoParams", null);
+            fail("Should have thrown an exception");
+        }
+        catch (NullPointerException e) {
+            // worked
+        }
+    }
+
+    // Mock methods used for testing
+    //-------------------------------------------------------------------------
+
+    public Object mockCallWithNoParams() {
+        return "NoParams";
+    }
+
+    public Object mockCallWithOneParam(Object value) {
+        assertEquals("Method not passed in the correct value", "abc", value);
+        return "OneParam";
+    }
+
+    public Object mockCallWithOneNullParam(Object value) {
+        assertEquals("Method not passed in the correct value", null, value);
+        return "OneParamWithNull";
+    }
+
+    public Integer mockCallWithOneCollectionParam(Object collection) {
+        Collection coll = DefaultTypeTransformation.asCollection(collection);
+        return new Integer(coll.size());
+    }
+
+    public Object mockOverloadedMethod() {
+        return "void";
+    }
+
+    public Object mockOverloadedMethod(Object object) {
+        return "Object";
+    }
+
+    public Object mockOverloadedMethod(Number object) {
+        return "Number";
+    }
+
+    public Object mockOverloadedMethod(String object) {
+        return "String";
+    }
+
+    public Object mockOverloadedMethod(Object object, Object bar) {
+        return "Object,Object";
+    }
+
+    public Object mockOverloadedMethod(Object object, Object[] array) {
+        return "Object,Object[]";
+    }
+
+    public Object badOverload(String a, Object b) {
+        return "String, Object";
+    }
+
+    public Object badOverload(Object a, String b) {
+        return "Object, String";
+    }
+
+    public Object methodTakesString(String x) {
+        return x;
+    }
+
+    public Object overloadedRemove(int idx) {
+        return "int" + idx;
+    }
+
+    public Object overloadedRemove(Object value) {
+        return "Object" + value;
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+
+    protected Object aProtectedMethod(String param) {
+        return param + " there!";
+    }
+
+    private Object aPrivateMethod(String param) {
+        return param + " James!";
+    }
+
+    protected void assertMethodCall(Object object, String method, Object param, Object expected) {
+        Object value = InvokerHelper.invokeMethod(object, method, new Object[]{param});
+        assertEquals("result of method: " + method, expected, value);
+    }
+
+    /**
+     * Asserts that invoking the method chooser finds the right overloaded
+     * method implementation
+     *
+     * @param expected  is the expected value of the method
+     * @param arguments the argument(s) to the method invocation
+     */
+    protected void assertMethodChooser(Object expected, Object arguments) throws Throwable {
+        Object value = invoke(this, "mockOverloadedMethod", arguments);
+
+        assertEquals("Invoking overloaded method for arguments: " + InvokerHelper.toString(arguments), expected, value);
+    }
+
+    protected Object invoke(Object object, String method, Object args) throws Throwable {
+        try {
+            return invoker.invokeMethod(object, method, args);
+        }
+        catch (InvokerInvocationException e) {
+            throw e.getCause();
+        }
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/InvokerHelperTest.java b/groovy/src/test/org/codehaus/groovy/runtime/InvokerHelperTest.java
new file mode 100644
index 0000000..f3bbe2c
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/InvokerHelperTest.java
@@ -0,0 +1,36 @@
+package org.codehaus.groovy.runtime;
+
+import java.util.HashMap;
+
+import groovy.lang.Binding;
+import groovy.lang.GroovyClassLoader;
+import groovy.lang.GroovyCodeSource;
+import groovy.lang.Script;
+import junit.framework.TestCase;
+
+public class InvokerHelperTest extends TestCase {
+    private HashMap bindingVariables;
+
+    protected void setUp() throws Exception {
+        bindingVariables = new HashMap();
+        bindingVariables.put("name", "hans");
+    }
+
+    public void testCreateScriptWithNullClass() {
+        Script script = InvokerHelper.createScript(null, new Binding(bindingVariables));
+        assertEquals(bindingVariables, script.getBinding().getVariables());
+    }
+
+    public void testCreateScriptWithScriptClass() {
+        GroovyClassLoader classLoader = new GroovyClassLoader();
+        String controlProperty = "text";
+        String controlValue = "I am a script";
+        String code = controlProperty + " = '" + controlValue + "'";
+        GroovyCodeSource codeSource = new GroovyCodeSource(code, "testscript", "/groovy/shell");
+        Class scriptClass = classLoader.parseClass(codeSource, false);
+        Script script = InvokerHelper.createScript(scriptClass, new Binding(bindingVariables));
+        assertEquals(bindingVariables, script.getBinding().getVariables());
+        script.run();
+        assertEquals(controlValue, script.getProperty(controlProperty));
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/InvokerTest.java b/groovy/src/test/org/codehaus/groovy/runtime/InvokerTest.java
new file mode 100644
index 0000000..10708db
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/InvokerTest.java
@@ -0,0 +1,185 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.GString;
+import groovy.lang.GroovyRuntimeException;
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.runtime.typehandling.DefaultTypeTransformation;
+
+import java.util.*;
+
+
+/**
+ * Test the Invoker class
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class InvokerTest extends GroovyTestCase {
+
+    protected Invoker invoker = new Invoker();
+
+    public void testAsCollectionWithArray() {
+        Object[] array = {"A", "B", "C"};
+        assertAsCollection(array, 3);
+    }
+
+    public void testAsCollectionWithMap() {
+        Map map = new HashMap();
+        map.put("A", "abc");
+        map.put("B", "def");
+        map.put("C", "xyz");
+        assertAsCollection(map, 3);
+    }
+
+    public void testAsCollectionWithList() {
+        List list = new ArrayList();
+        list.add("A");
+        list.add("B");
+        list.add("C");
+        assertAsCollection(list, 3);
+    }
+
+    public void testInvokerException() throws Throwable {
+        try {
+            throw new GroovyRuntimeException("message", new NullPointerException());
+        }
+        catch (GroovyRuntimeException e) {
+            // worked
+            assertEquals("message", e.getMessage());
+            assertTrue(e.getCause() instanceof NullPointerException);
+        }
+    }
+
+    public void testAsBoolean() {
+        assertAsBoolean(true, Boolean.TRUE);
+        assertAsBoolean(true, "true");
+        assertAsBoolean(true, "TRUE");
+        assertAsBoolean(true, "false");
+        assertAsBoolean(false, Boolean.FALSE);
+        assertAsBoolean(false, (String) null);
+        assertAsBoolean(false, "");
+        GString emptyGString = new GString(new Object[]{""}) {
+            public String[] getStrings() {
+                return new String[]{""};
+            }
+        };
+        assertAsBoolean(false, emptyGString);
+        GString nonEmptyGString = new GString(new Object[]{"x"}) {
+            public String[] getStrings() {
+                return new String[]{"x"};
+            }
+        };
+        assertAsBoolean(true, nonEmptyGString);
+        assertAsBoolean(true, new Integer(1234));
+        assertAsBoolean(false, new Integer(0));
+        assertAsBoolean(true, new Float(0.3f));
+        assertAsBoolean(true, new Double(3.0f));
+        assertAsBoolean(false, new Float(0.0f));
+        assertAsBoolean(true, new Character((char) 1));
+        assertAsBoolean(false, new Character((char) 0));
+        assertAsBoolean(false, Collections.EMPTY_LIST);
+        assertAsBoolean(true, Arrays.asList(new Integer[]{new Integer(1)}));
+    }
+
+    public void testLessThan() {
+        assertTrue(ScriptBytecodeAdapter.compareLessThan(new Integer(1), new Integer(2)));
+        assertTrue(ScriptBytecodeAdapter.compareLessThanEqual(new Integer(2), new Integer(2)));
+    }
+
+    public void testGreaterThan() {
+        assertTrue(ScriptBytecodeAdapter.compareGreaterThan(new Integer(3), new Integer(2)));
+        assertTrue(ScriptBytecodeAdapter.compareGreaterThanEqual(new Integer(2), new Integer(2)));
+    }
+
+    public void testCompareTo() {
+        assertTrue(DefaultTypeTransformation.compareEqual("x", new Integer('x')));
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+
+    /**
+     * Asserts the asBoolean method returns the given flag
+     */
+    protected void assertAsBoolean(boolean expected, Object value) {
+        boolean answer = DefaultTypeTransformation.castToBoolean(value);
+        assertEquals("value: " + value + " asBoolean()", expected, answer);
+    }
+
+    /**
+     * Asserts that the given object can be converted into a collection and iterator
+     * of the given size
+     */
+    protected void assertAsCollection(Object collectionObject, int count) {
+        Collection collection = DefaultTypeTransformation.asCollection(collectionObject);
+        assertTrue("Collection is not null", collection != null);
+        assertEquals("Collection size", count, collection.size());
+
+        assertIterator("collections iterator", collection.iterator(), count);
+        assertIterator("InvokerHelper.asIterator", InvokerHelper.asIterator(collectionObject), count);
+        assertIterator("InvokerHelper.asIterator(InvokerHelper.asCollection)", InvokerHelper.asIterator(collection), count);
+        assertIterator("InvokerHelper.asIterator(InvokerHelper.asIterator)", InvokerHelper.asIterator(InvokerHelper.asIterator(collectionObject)), count);
+    }
+
+    /**
+     * Asserts that the iterator is valid and of the right size
+     */
+    protected void assertIterator(String message, Iterator iterator, int count) {
+        for (int i = 0; i < count; i++) {
+            assertTrue(message + ": should have item: " + i, iterator.hasNext());
+            assertTrue(message + ": item: " + i + " should not be null", iterator.next() != null);
+        }
+
+        assertFalse(
+                message + ": should not have item after iterating through: " + count + " items",
+                iterator.hasNext());
+    }
+
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyInvocationHandler.java b/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyInvocationHandler.java
new file mode 100644
index 0000000..2d787cc
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyInvocationHandler.java
@@ -0,0 +1,26 @@
+package org.codehaus.groovy.runtime;
+
+import java.lang.reflect.*;
+
+public class JdkDynamicProxyInvocationHandler implements InvocationHandler {
+
+	/* InvocationHandler implementation. */
+	Object proxiedObject;
+
+    private JdkDynamicProxyInvocationHandler (Object obj) {
+        this.proxiedObject = obj;
+    }
+
+    public static Object getProxiedObject (Object obj) {
+        Class cl = obj.getClass();
+        return Proxy.newProxyInstance(
+            cl.getClassLoader(),
+            cl.getInterfaces(),
+            new JdkDynamicProxyInvocationHandler (obj)
+        );
+    }
+
+    public Object invoke(Object proxy, Method m, Object[] args) throws IllegalAccessException, InvocationTargetException {
+        return m.invoke (proxiedObject, args);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyServiceBean.java b/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyServiceBean.java
new file mode 100644
index 0000000..96fb502
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyServiceBean.java
@@ -0,0 +1,7 @@
+package org.codehaus.groovy.runtime;
+
+public interface JdkDynamicProxyServiceBean {
+	public String doService ();
+	public void setJdkDynamicProxyServiceBean (JdkDynamicProxyServiceBean in);
+}
+
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyServiceBeanImpl1.groovy b/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyServiceBeanImpl1.groovy
new file mode 100644
index 0000000..ecec51d
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyServiceBeanImpl1.groovy
@@ -0,0 +1,14 @@
+package org.codehaus.groovy.runtime;
+
+class JdkDynamicProxyServiceBeanImpl1 implements JdkDynamicProxyServiceBean {
+
+	JdkDynamicProxyServiceBean jdkDynamicProxyServiceBean;
+
+	String doService () {
+		if (jdkDynamicProxyServiceBean != null) {
+			return jdkDynamicProxyServiceBean.doService ();
+		} else {
+			return "SERVICE";
+		}
+	}
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyServiceBeanImpl2.groovy b/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyServiceBeanImpl2.groovy
new file mode 100644
index 0000000..67d71be
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyServiceBeanImpl2.groovy
@@ -0,0 +1,14 @@
+package org.codehaus.groovy.runtime;
+
+class JdkDynamicProxyServiceBeanImpl2 implements JdkDynamicProxyServiceBean {
+	
+	JdkDynamicProxyServiceBean jdkDynamicProxyServiceBean;
+
+	String doService () {
+		if (jdkDynamicProxyServiceBean != null) {
+			return jdkDynamicProxyServiceBean.doService ();
+		} else {
+			return "SERVICE";
+		}
+	}
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyTest.java b/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyTest.java
new file mode 100644
index 0000000..8b7c22f
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/JdkDynamicProxyTest.java
@@ -0,0 +1,31 @@
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.GroovyClassLoader;
+import groovy.util.GroovyTestCase;
+
+public class JdkDynamicProxyTest extends GroovyTestCase {
+
+	public void testJdkDynamicProxySameLoader() throws Exception {
+
+		// Instantiate all beans.
+        final GroovyClassLoader loader = new GroovyClassLoader();
+        JdkDynamicProxyServiceBean sb1 = (JdkDynamicProxyServiceBean) JdkDynamicProxyInvocationHandler.getProxiedObject (loader.loadClass ("org.codehaus.groovy.runtime.JdkDynamicProxyServiceBeanImpl1").newInstance () );
+		JdkDynamicProxyServiceBean sb2 = (JdkDynamicProxyServiceBean) JdkDynamicProxyInvocationHandler.getProxiedObject (loader.loadClass ("org.codehaus.groovy.runtime.JdkDynamicProxyServiceBeanImpl2").newInstance () );
+
+		// Manually wire beans together.
+		sb1.setJdkDynamicProxyServiceBean (sb2);
+		assertEquals ("SERVICE", sb1.doService () );
+	}
+
+    public void testJdkDynamicProxyDifferentLoaders() throws Exception {
+
+        // Instantiate all beans.
+        JdkDynamicProxyServiceBean sb1 = (JdkDynamicProxyServiceBean) JdkDynamicProxyInvocationHandler.getProxiedObject (new GroovyClassLoader().loadClass ("org.codehaus.groovy.runtime.JdkDynamicProxyServiceBeanImpl1").newInstance () );
+        JdkDynamicProxyServiceBean sb2 = (JdkDynamicProxyServiceBean) JdkDynamicProxyInvocationHandler.getProxiedObject (new GroovyClassLoader().loadClass ("org.codehaus.groovy.runtime.JdkDynamicProxyServiceBeanImpl2").newInstance () );
+
+        // Manually wire beans together.
+        sb1.setJdkDynamicProxyServiceBean (sb2);
+        assertEquals ("SERVICE", sb1.doService () );
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/MetaClassHelperTest.java b/groovy/src/test/org/codehaus/groovy/runtime/MetaClassHelperTest.java
new file mode 100644
index 0000000..3b8a600
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/MetaClassHelperTest.java
@@ -0,0 +1,10 @@
+package org.codehaus.groovy.runtime;

+

+import junit.framework.TestCase;

+

+public class MetaClassHelperTest extends TestCase {

+    public void testGetClassName() {

+        // GROOVY-1262

+        MetaClassHelper.getClassName(null);

+    }

+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/MethodFailureTest.java b/groovy/src/test/org/codehaus/groovy/runtime/MethodFailureTest.java
new file mode 100644
index 0000000..fd6740d
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/MethodFailureTest.java
@@ -0,0 +1,104 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObject;
+import groovy.lang.GroovyRuntimeException;
+import groovy.util.GroovyTestCase;
+
+/**
+ * Tests failing method invocations to ensure correct exceptions
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MethodFailureTest extends GroovyTestCase {
+
+    public void testFailingMethod() {
+        MockGroovyObject object = new MockGroovyObject();
+        try {
+            object.invokeMethod("nonExistentMethod", "hello");
+
+            fail("Should have thrown an exception");
+        }
+        catch (GroovyRuntimeException e) {
+            System.out.println(e);
+        }
+    }
+
+    public void testMethodWhichCallsTheFailingMethod() {
+        MockGroovyObject object = new MockGroovyObject();
+        try {
+            object.invokeMethod("methodThatFails", null);
+
+            fail("Should have thrown an exception");
+        }
+        catch (GroovyRuntimeException e) {
+            System.out.println(e);
+            //e.printStackTrace();
+        }
+    }
+
+    public void testMethodWhichCallsTheFailingMethodInsideAClosure() {
+        MockGroovyObject object = new MockGroovyObject();
+        try {
+            object.invokeMethod("callClosure", new Closure(this) {
+                protected Object doCall(GroovyObject object) {
+                    return object.invokeMethod("nonExistentMethod", "hello");
+                }
+            });
+
+            fail("Should have thrown an exception");
+        }
+        catch (GroovyRuntimeException e) {
+            System.out.println(e);
+            //e.printStackTrace();
+        }
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/MethodKeyTest.java b/groovy/src/test/org/codehaus/groovy/runtime/MethodKeyTest.java
new file mode 100644
index 0000000..5cd6c9c
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/MethodKeyTest.java
@@ -0,0 +1,93 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package org.codehaus.groovy.runtime;
+
+import junit.framework.TestCase;
+import org.codehaus.groovy.runtime.metaclass.TemporaryMethodKey;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MethodKeyTest extends TestCase {
+
+    public void testDefaultImplementation() throws Exception {
+        MethodKey a = new DefaultMethodKey(Object.class, "foo", new Class[]{Object.class, Integer.class}, false);
+        MethodKey a2 = new DefaultMethodKey(Object.class, "foo", new Class[]{Object.class, Integer.class}, false);
+        MethodKey b = new DefaultMethodKey(Object.class, "foo", new Class[]{Object.class}, false);
+        MethodKey c = new DefaultMethodKey(Object.class, "bar", new Class[]{Object.class, Integer.class}, false);
+
+        assertCompare(a, a, true);
+        assertCompare(a, a2, true);
+        assertCompare(b, b, true);
+
+        assertCompare(a, b, false);
+        assertCompare(a, c, false);
+        assertCompare(b, c, false);
+    }
+
+    public void testTemporaryImplementation() throws Exception {
+        MethodKey a = new DefaultMethodKey(Object.class, "foo", new Class[]{Object.class, Integer.class}, false);
+        MethodKey a2 = new TemporaryMethodKey(Object.class, "foo", new Object[]{new Object(), new Integer(1)}, false);
+        MethodKey b = new TemporaryMethodKey(Object.class, "foo", new Object[]{new Object()}, false);
+        MethodKey c = new TemporaryMethodKey(Object.class, "bar", new Object[]{new Object(), new Integer(1)}, false);
+
+        assertCompare(a, a, true);
+        assertCompare(a, a2, true);
+        assertCompare(b, b, true);
+
+        assertCompare(a, b, false);
+        assertCompare(a, c, false);
+        assertCompare(b, c, false);
+    }
+
+    protected void assertCompare(Object a, Object b, boolean expected) {
+        assertEquals("Compare " + a + " to " + b, expected, a.equals(b));
+        if (expected) {
+            assertEquals("hashCode " + a + " to " + b, a.hashCode(), b.hashCode());
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/MinusTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/MinusTest.groovy
new file mode 100644
index 0000000..0478ab9
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/MinusTest.groovy
@@ -0,0 +1,84 @@
+
+class MinusTest extends GroovyTestCase {
+
+	void doTestMinus(String type, def a, b, c, d) {
+		assertEquals(type, [ a, b ], [ a, b, c ] - [ c ])
+		assertEquals(type, [ a, b ], [ a, b, c ] - [ c, d ])
+		assertEquals(type, [], [ a, b, c ] - [ a, b, c ])
+		assertEquals(type, [], [ a, b, c ] - [ c, b, a ])
+		assertEquals(type, [ a, b, c ], [ a, b, c ] - [])
+		assertEquals(type, [], [] - [ a, b, c ])
+	}
+
+	void doTestMinusDupplicates(String type, def a, b, c, d) {
+		assertEquals(type, [a, a], [a, a] - [])
+		assertEquals(type, [a, b, b, c], [a, b, b, c] - [])
+		assertEquals(type, [b, b, c], [a, b, b, c] - [a])
+		assertEquals(type, [a], [a, b, b, c] - [b, c])
+		assertEquals(type, [], [a] - [ a, a ])
+	}
+
+	void doTestMinusWithNull(String type, def a, b, c, d) {
+		assertEquals(type, [ a, b, c ], [ a, b, c ] - [ null ])
+		assertEquals(type, [ a, b, c ], [ a, b, c , null] - [ null ])
+		assertEquals(type, [ a, b ], [ a, b, c, null ] - [ null, c ])
+		assertEquals(type, [], [] - [ a, b, c, null ])
+		assertEquals(type, [ a, b, c, null ], [ a, b, c, null ] - [ ])
+		assertEquals(type, [ a, b, null ], [ a, b, c, null ] - [ c ])
+	}
+
+	void testMinusComparable() {
+	    def a = 'a'
+	    def b = 'b'
+	    def c = 'c'
+	    def d = 'd'
+	    
+	    doTestMinus('Comparable', a, b, c, d)
+	    doTestMinusDupplicates('Comparable', a, b, c, d)
+	    doTestMinusWithNull('Comparable', a, b, c, d)
+	}
+	
+	void testMinusNumber() {
+	    def a = 1
+	    def b = 2
+	    def c = 3
+	    def d = 4
+	    
+	    doTestMinus('Number', a, b, c, d)
+	    doTestMinusDupplicates('Number', a, b, c, d)
+	    doTestMinusWithNull('Number', a, b, c, d)
+	}
+
+	void testMinusNumbersMixed() {
+	    def a = 1
+	    def b = new BigInteger('2')
+	    def c = 3.0d
+	    def d = new BigDecimal('4.0')
+	    
+	    doTestMinus('NumbersMixed', a, b, c, d)
+	    doTestMinusDupplicates('NumbersMixed', a, b, c, d)
+	    doTestMinusWithNull('NumbersMixed', a, b, c, d)
+	}
+
+	void testMinusNonComparable() {
+	    def a = new Object()
+	    def b = new Object()
+	    def c = new Object()
+	    def d = new Object()
+	    
+	    doTestMinus('NonComparable', a, b, c, d)
+	    doTestMinusDupplicates('NonComparable', a, b, c, d)
+	    doTestMinusWithNull('NonComparable', a, b, c, d)
+	}
+	    
+	void testMinusMixed() {
+	    def a = new Object()
+	    def b = 2
+	    def c = '3'
+	    def d = new BigDecimal('4.0')
+	    
+	    doTestMinus('Mixed', a, b, c, d)
+	    doTestMinusDupplicates('Mixed', a, b, c, d)
+	    doTestMinusWithNull('Mixed', a, b, c, d)
+	}
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/MockGroovyObject.java b/groovy/src/test/org/codehaus/groovy/runtime/MockGroovyObject.java
new file mode 100644
index 0000000..616c2ed
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/MockGroovyObject.java
@@ -0,0 +1,68 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.Closure;
+import groovy.lang.GroovyObjectSupport;
+
+/**
+ * A POGO used by the test cases
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class MockGroovyObject extends GroovyObjectSupport {
+
+    public Object methodThatFails() {
+        return invokeMethod("nonExistentMethod", "hello");
+    }
+
+    public Object callClosure(Closure closure) {
+        return closure.call(this);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/NestedCategoryTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/NestedCategoryTest.groovy
new file mode 100644
index 0000000..f7406d3
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/NestedCategoryTest.groovy
@@ -0,0 +1,65 @@
+package org.codehaus.groovy.runtime

+

+import groovy.util.GroovyTestCase

+

+

+class NestedCategoryTest extends GroovyTestCase{

+   void testGreeter_plain(){

+	   def greeter = new Greeter();

+	   assertEquals "Hello Groovy!", greeter.greet();

+	}

+

+	void testGreeter_withOne(){

+	   def greeter = new Greeter();

+	   assertEquals "Hello Groovy!", greeter.greet();

+	   use( CategoryOne.class ){

+	      assertEquals "Hello from One", greeter.greet();

+	   }

+	   assertEquals "Hello Groovy!", greeter.greet();

+	}

+

+	void testGreeter_withTwo(){

+	   def greeter = new Greeter();

+	   assertEquals "Hello Groovy!", greeter.greet();

+	   use( CategoryTwo.class ){

+	      assertEquals "Hello from Two", greeter.greet();

+	   }

+	   assertEquals "Hello Groovy!", greeter.greet();

+	}

+

+	void testGreeter_withOneAndTwo_nested(){

+	   // fails!

+	   def greeter = new Greeter();

+	   assertEquals "Hello Groovy!", greeter.greet();

+	   use( CategoryOne.class ){

+	      assertEquals "Hello from One", greeter.greet();

+	      use( CategoryTwo.class ){

+	         assertEquals "Hello from Two", greeter.greet();

+	      }

+	      assertEquals "Hello from One", greeter.greet();

+	   }

+	   assertEquals "Hello Groovy!", greeter.greet();

+	}

+}

+

+class Greeter{

+    String greet(){

+       return "Hello Groovy!";

+    }

+

+    String say( String s ){

+       return "I say: "+ s;

+    }

+ }

+

+ class CategoryOne{

+    static String greet( Greeter self ){

+       return "Hello from One";

+    }

+ }

+

+ class CategoryTwo{

+    static String greet( Greeter self ){

+       return "Hello from Two";

+    }

+ }
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/NewStaticMetaMethodTest.java b/groovy/src/test/org/codehaus/groovy/runtime/NewStaticMetaMethodTest.java
new file mode 100644
index 0000000..d1f0501
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/NewStaticMetaMethodTest.java
@@ -0,0 +1,100 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+package org.codehaus.groovy.runtime;
+
+import junit.framework.TestCase;
+import org.codehaus.groovy.reflection.CachedMethod;
+import org.codehaus.groovy.runtime.metaclass.NewInstanceMetaMethod;
+
+import java.lang.reflect.Method;
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class NewStaticMetaMethodTest extends TestCase {
+
+    public void testInvokeMetaMethod() throws Exception {
+        Method method = getClass().getMethod("dummyMethod", new Class[]{String.class, String.class});
+        assertTrue("Should have found a method", method != null);
+
+        NewInstanceMetaMethod metaMethod = createNewMetaMethod(method);
+
+        Object answer = metaMethod.invoke("abc", new Object[]{"xyz"});
+        assertEquals("def", answer);
+
+        assertTrue("Should not appear as static method", !metaMethod.isStatic());
+    }
+
+    public void testInvokeDefaultGroovyMethod() throws Exception {
+        Method method = DefaultGroovyMethods.class.getMethod("plus", new Class[]{String.class, Object.class});
+        assertTrue("Should have found a method", method != null);
+
+        NewInstanceMetaMethod metaMethod = createNewMetaMethod(method);
+
+        Object answer = metaMethod.invoke("abc", new Object[]{"123"});
+        assertEquals("abc123", answer);
+
+        System.out.println("Found: " + answer);
+    }
+
+    public void testInvokeDefaultGroovyMethodUsingMetaClass() {
+        Object answer = InvokerHelper.invokeMethod("abc", "plus", new Object[]{"123"});
+        assertEquals("abc123", answer);
+
+        System.out.println("Found: " + answer);
+    }
+
+    public static String dummyMethod(String foo, String bar) throws Exception {
+        assertEquals("abc", foo);
+        assertEquals("xyz", bar);
+        return "def";
+    }
+
+    protected NewInstanceMetaMethod createNewMetaMethod(Method method) {
+        return new NewInstanceMetaMethod(CachedMethod.find(method));
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/NullObjectTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/NullObjectTest.groovy
new file mode 100644
index 0000000..c634323
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/NullObjectTest.groovy
@@ -0,0 +1,55 @@
+package org.codehaus.groovy.runtime
+
+class NullObjectTest extends GroovyTestCase {
+    void testCallingMethod() {
+        def foo = null
+        try {
+          println foo.bar
+          assert false  // should throw exception
+        } catch (NullPointerException ex) {
+          // is successfull
+        }
+    }
+    
+    void testtoStringMethod() {
+        def foo = null
+        assert foo.toString() == "null"
+    }
+
+    void testEquals() {
+        def a = [1]
+        assert a[3] == a[4]
+        assert a[2].equals(a[4])
+    }
+    
+    void testAsExpression() {
+      assert null as String == null
+    }
+    
+    void testIs(){
+      assert null.is(null)
+    }
+    
+    void testCategory() {
+        def n = null
+
+        assert "a $n b" == "a null b"
+            assert n.toString() == "null"
+            assert n + " is a null value" == "null is a null value"
+            assert "this is a null value " + null == "this is a null value null"
+
+            use (MyCategory) {
+                assert "a $n b" == "a  b"
+                assert n.toString() == ""
+                assert n + " is a null value" == " is a null value"
+                assert "this is a null value " + null == "this is a null value "
+            }
+        } 
+}
+
+class MyCategory {
+    public static String toString(NullObject obj) {
+        return ""
+    }
+}
+
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/PropertyTest.java b/groovy/src/test/org/codehaus/groovy/runtime/PropertyTest.java
new file mode 100644
index 0000000..f41aa93
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/PropertyTest.java
@@ -0,0 +1,259 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.MissingMethodException;
+import groovy.util.GroovyTestCase;
+import groovy.util.Node;
+
+import javax.swing.*;
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Test the property access of the Invoker class
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class PropertyTest extends GroovyTestCase {
+
+    protected Invoker invoker = new Invoker();
+
+    public void testMapProperties() throws Exception {
+        Map map = new HashMap();
+        map.put("foo", "abc");
+        map.put("bar", new Integer(123));
+
+        assertGetSetProperty(map, "foo", "abc", "def");
+        assertGetSetProperty(map, "bar", new Integer(123), new Double(12.34));
+    }
+
+    public void testBeanProperties() throws Exception {
+        DummyBean bean = new DummyBean();
+
+        assertGetSetProperty(bean, "name", "James", "Bob");
+        assertGetSetProperty(bean, "i", new Integer(123), new Integer(455));
+
+        // dynamic properties
+        assertGetSetProperty(bean, "dynamicFoo", null, "aValue");
+        assertGetSetProperty(bean, "dynamicFoo", "aValue", "NewValue");
+    }
+
+    /**
+     * todo this is no longer possible in new groovy
+     * public void testUsingMethodProperty() throws Exception {
+     * DummyBean bean = new DummyBean();
+     * <p/>
+     * assertGetSetProperty(bean, "name", "James", "Bob");
+     * <p/>
+     * Object value = InvokerHelper.getProperty(bean, "getName");
+     * assertTrue("Should have returned a closure: " + value, value instanceof Closure);
+     * Closure closure = (Closure) value;
+     * Object result = closure.call(null);
+     * assertEquals("Result of call to closure", "Bob", result);
+     * }
+     */
+
+
+    public void testStaticProperty() throws Exception {
+        Object value = InvokerHelper.getProperty(System.class, "out");
+        assertEquals("static property out", System.out, value);
+    }
+
+    public void testClassProperty() throws Exception {
+        Class c = String.class;
+        Object value = InvokerHelper.getProperty(c, "name");
+        assertEquals("class name property", c.getName(), value);
+    }
+
+    public void testMapEntryProperty() throws Exception {
+        HashMap map = new HashMap();
+        map.put("a", "x");
+        Object[] array = map.entrySet().toArray();
+        Object entry = array[0];
+
+        Object key = InvokerHelper.getProperty(entry, "key");
+        assertEquals("key property", "a", key);
+
+        Object value = InvokerHelper.getProperty(entry, "value");
+        assertEquals("value property", "x", value);
+    }
+
+    /**
+     * todo this is no longer possible in new groovy
+     * public void testMethodProperty() throws Exception {
+     * Object value = InvokerHelper.getProperty(this, "getCheese");
+     * assertTrue("Should have returned a closure: " + value, value instanceof Closure);
+     * <p/>
+     * Object result = ((Closure) value).call();
+     * assertEquals("result of closure call", getCheese(), result);
+     * <p/>
+     * System.out.println("Closure: " + value + " and cheese: " + result);
+     * }
+     */
+
+    public void testListCoercionProperty() throws Exception {
+        DummyBean bean = new DummyBean();
+        List list = new ArrayList();
+        list.add(new Integer(10));
+        list.add(new Integer(20));
+
+        InvokerHelper.setProperty(bean, "point", list);
+        assertEquals("Should have set a point", new Point(10, 20), bean.getPoint());
+    }
+
+    public void testListCoercionPropertyOnJFrame() throws Exception {
+        try {
+            JFrame bean = new JFrame();
+            List list = new ArrayList();
+            list.add(new Integer(10));
+            list.add(new Integer(20));
+
+            InvokerHelper.setProperty(bean, "location", list);
+            assertEquals("Should have set a point", new Point(10, 20), bean.getLocation());
+        }
+        catch (HeadlessException e) {
+            // its fine to not run this test on headless environments
+        }
+        catch (MissingMethodException e) {
+            System.out.println("Failed with cause: " + e);
+            e.printStackTrace();
+            fail("Should not have throw: " + e);
+        }
+    }
+
+    public void testListNavigationProperty() throws Exception {
+        List list = new ArrayList();
+        list.add(new DummyBean("James"));
+        list.add(new DummyBean("Bob"));
+
+        List value = (List) InvokerHelper.getProperty(list, "name");
+        assertArrayEquals(new Object[]{"James", "Bob"}, value.toArray());
+    }
+
+    public void testListOfListNavigationProperty() throws Exception {
+        List list = new ArrayList();
+        list.add(new DummyBean("James"));
+        list.add(new DummyBean("Bob"));
+
+        List listOfList = new ArrayList();
+        listOfList.add(list);
+
+        List value = (List) InvokerHelper.getProperty(listOfList, "name");
+        Object[] objects = value.toArray();
+        List objectList = (List) objects[0];
+        assertArrayEquals(new Object[]{"James", "Bob"}, objectList.toArray());
+    }
+
+    public void testNodeNavigationProperty() throws Exception {
+        Node z = new Node(null, "z");
+        Node y = new Node(null, "y");
+
+        List children = new ArrayList();
+        children.add(y);
+        children.add(z);
+
+        Node x = new Node(null, "x", children);
+
+        children = new ArrayList();
+        children.add(x);
+        Node b = new Node(null, "b", children);
+
+        // @todo should try with just a node as the child
+
+        List value = (List) InvokerHelper.getProperty(b, "x");
+        assertArrayEquals(new Object[]{x}, value.toArray());
+
+        value = (List) InvokerHelper.getProperty(value, "z");
+        assertArrayEquals(new Object[]{z}, value.toArray());
+    }
+
+    public void testUsingInPropertyOnProcessViaGroovyMethod() throws Exception {
+        Process process = DefaultGroovyMethods.execute("java -version");
+        Object value = InvokerHelper.getProperty(process, "in");
+        assertNotNull(value);
+
+        System.out.println("Found in: " + value);
+
+        process.destroy();
+    }
+
+    public Object getCheese() {
+        return "cheddar";
+    }
+
+    public void testComponentParent() {
+        JPanel panel = new JPanel();
+        JButton bean = new JButton();
+
+        panel.add(bean);
+
+        Object value = InvokerHelper.getProperty(bean, "parent");
+        assertTrue(value != null);
+    }
+
+    // Implementation methods
+    //-------------------------------------------------------------------------
+
+    protected void assertGetSetProperty(Object object, String property, Object currentValue, Object newValue) {
+        assertGetProperty(object, property, currentValue);
+
+        InvokerHelper.setProperty(object, property, newValue);
+
+        assertGetProperty(object, property, newValue);
+    }
+
+    protected void assertGetProperty(Object object, String property, Object expected) {
+        Object value = InvokerHelper.getProperty(object, property);
+
+        assertEquals("property: " + property + " of: " + object, expected, value);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/StaticInitTest.java b/groovy/src/test/org/codehaus/groovy/runtime/StaticInitTest.java
new file mode 100644
index 0000000..71b1f2e
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/StaticInitTest.java
@@ -0,0 +1,31 @@
+package org.codehaus.groovy.runtime;
+
+import groovy.lang.GroovyClassLoader;
+import junit.framework.TestCase;
+
+import java.lang.reflect.Field;
+
+class X {
+
+    public static int field = 0;
+
+    static {
+        StaticInitTest.failed = true;
+        System.out.println("INIT");
+    }
+}
+
+public class StaticInitTest extends TestCase {
+
+    static boolean failed;
+
+    public void testInitOrder () throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
+        System.out.println("GET FIELD");
+        final Field f = new GroovyClassLoader().loadClass("org.codehaus.groovy.runtime.X", false, false, false).getField("field");
+        System.out.println(failed);
+        assertTrue(!failed);
+        f.getInt(null);
+        System.out.println(failed);
+        assertTrue(failed);
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/StaticPrintlnTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/StaticPrintlnTest.groovy
new file mode 100644
index 0000000..cf74fe1
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/StaticPrintlnTest.groovy
@@ -0,0 +1,14 @@
+package org.codehaus.groovy.runtime;
+
+import groovy.bugs.TestSupport
+
+class StaticPrintlnTest extends TestSupport {
+
+    void testStaticPrint() {
+        main(getMockArguments())
+	}
+	
+    static void main(args) {
+        println("called with: " + args)
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/StringAsClassTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/StringAsClassTest.groovy
new file mode 100644
index 0000000..bb8e4d7
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/StringAsClassTest.groovy
@@ -0,0 +1,17 @@
+package org.codehaus.groovy.runtime
+
+class StringAsClassTest extends GroovyTestCase{
+    void testStringAsClass  () {
+        assertEquals "java.util.ArrayList" as Class, ArrayList
+    }
+
+    void testStringBuffer () {
+        assertEquals "${ArrayList.'package'.name}.ArrayList" as Class, ArrayList
+    }
+
+    void testFails () {
+        shouldFail {
+            assertEquals "NOSUCHCLASS" as Class, ArrayList
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/TimeCategoryTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/TimeCategoryTest.groovy
new file mode 100644
index 0000000..8a84fdc
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/TimeCategoryTest.groovy
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.codehaus.groovy.runtime
+
+import org.codehaus.groovy.runtime.TimeCategory
+
+/** 
+ * Tests the org.codehaus.groovy.runtime.TimeCategory class. 
+ * Most of these tests use January 1 as a start time to avoid 
+ * leap years and daylight savings time issues. 
+ * 
+ * @author Hamlet D'Arcy
+ */
+class TimeCategoryTest extends GroovyTestCase {
+
+    void testDurationArithmeticOnMilliseconds() {
+        use(TimeCategory) {
+            def midnight = new Date(100, 0, 1, 0, 0, 0)
+            def oneSecondPastMidnight = new Date(100, 0, 1, 0, 0, 1)
+            def twoSecondsPastMidnight = new Date(100, 0, 1, 0, 0, 2)
+
+            assert (midnight + 1000.millisecond) == oneSecondPastMidnight
+            assert (midnight + 2000.milliseconds) == twoSecondsPastMidnight
+            assert (twoSecondsPastMidnight - 1000.millisecond) == oneSecondPastMidnight
+            assert (twoSecondsPastMidnight - 2000.milliseconds) == midnight
+        }
+    }
+
+    void testDurationArithmeticOnSeconds() {
+        use(TimeCategory) {
+            def midnight = new Date(100, 0, 1, 0, 0, 0)
+            def oneSecondPastMidnight = new Date(100, 0, 1, 0, 0, 1)
+            def twoSecondsPastMidnight = new Date(100, 0, 1, 0, 0, 2)
+
+            assert (midnight + 1.second) == oneSecondPastMidnight
+            assert (midnight + 2.seconds) == twoSecondsPastMidnight
+            assert (twoSecondsPastMidnight - 1.second) == oneSecondPastMidnight
+            assert (twoSecondsPastMidnight - 2.seconds) == midnight
+        }
+    }
+
+    void testDurationArithmeticOnMinutes() {
+        use(TimeCategory) {
+            def midnight = new Date(100, 0, 1, 0, 0, 0)
+            def oneMinutePastMidnight = new Date(100, 0, 1, 0, 1, 0)
+            def twoMinutesPastMidnight = new Date(100, 0, 1, 0, 2, 0)
+
+            assert (midnight + 1.minute) == oneMinutePastMidnight
+            assert (midnight + 2.minutes) == twoMinutesPastMidnight
+            assert (twoMinutesPastMidnight - 60.seconds) == oneMinutePastMidnight
+            assert (twoMinutesPastMidnight - 1.minute) == oneMinutePastMidnight
+            assert (twoMinutesPastMidnight - 120.seconds) == midnight
+            assert (twoMinutesPastMidnight - 2.minutes) == midnight
+        }
+    }
+
+    void testDurationArithmeticOnHours() {
+        use(TimeCategory) {
+            def midnight = new Date(100, 0, 1, 0, 0, 0)
+            def oneAM = new Date(100, 0, 1, 1, 0, 0)
+            def twoAM = new Date(100, 0, 1, 2, 0, 0)
+
+            assert (midnight + 1.hour) == oneAM
+            assert (midnight + 2.hours) == twoAM
+            assert (twoAM - 3600.seconds) == oneAM
+            assert (twoAM - 1.hour) == oneAM
+            assert (twoAM - 7200.seconds) == midnight
+            assert (twoAM - 2.hours) == midnight
+        }
+    }
+
+    void testDurationArithmeticOnDays() {
+        use(TimeCategory) {
+            def januaryFirst = new Date(100, 0, 1, 0, 0, 0)
+            def januarySecond = new Date(100, 0, 2, 0, 0, 0)
+            def januaryThird = new Date(100, 0, 3, 0, 0, 0)
+
+            assert (januaryFirst + 1.day) == januarySecond
+            assert (januaryFirst + 2.days) == januaryThird
+            assert (januaryThird - 1.day) == januarySecond
+            assert (januaryThird - 2.days) == januaryFirst
+        }
+    }
+
+    void testDurationArithmeticOnWeeks() {
+        use(TimeCategory) {
+            def firstWeek = new Date(100, 0, 1, 0, 0, 0)
+            def secondWeek = new Date(100, 0, 8, 0, 0, 0)
+            def thirdWeek = new Date(100, 0, 15, 0, 0, 0)
+
+            assert (firstWeek + 1.week) == secondWeek
+            assert (firstWeek + 2.weeks) == thirdWeek
+            assert (thirdWeek - 1.week) == secondWeek
+            assert (thirdWeek - 2.weeks) == firstWeek
+        }
+    }
+
+    void testDurationArithmeticOnMonths() {
+        use(TimeCategory) {
+            def january = new Date(100, 0, 1, 0, 0, 0)
+            def february = new Date(100, 1, 1, 0, 0, 0)
+            def march = new Date(100, 2, 1, 0, 0, 0)
+
+            assert (january + 1.month) == february
+            assert (january + 2.months) == march
+            assert (march - 1.month) == february
+            assert (march - 2.months) == january
+        }
+    }
+
+    void testDurationArithmeticOnYears() {
+        use(TimeCategory) {
+            def firstYear = new Date(100, 0, 1, 0, 0, 0)
+            def secondYear = new Date(101, 0, 1, 0, 0, 0)
+            def thirdYear = new Date(102, 0, 1, 0, 0, 0)
+
+            assert (firstYear + 1.year) == secondYear
+            assert (firstYear + 2.years) == thirdYear
+            assert (thirdYear - 1.year) == secondYear
+            assert (thirdYear - 2.years) == firstYear
+        }
+    }
+
+
+    void testDateSubtractionOnSeconds() {
+        use(TimeCategory) {
+            def current = new Date(100, 0, 1, 0, 0, 0)
+            def oneSecondLater = new Date(100, 0, 1, 0, 0, 1)
+            def twoSecondsLater = new Date(100, 0, 1, 0, 0, 2)
+
+            def result = oneSecondLater - current
+            assert result.seconds == 1
+            result = twoSecondsLater - current
+            assert result.seconds == 2
+        }
+    }
+
+    void testDateSubtractionOnMinutes() {
+        use(TimeCategory) {
+            def current = new Date(100, 0, 1, 0, 0, 0)
+            def oneMinuteLater = new Date(100, 0, 1, 0, 1, 0)
+            def twoMinutesLater = new Date(100, 0, 1, 0, 2, 0)
+
+            def result = oneMinuteLater - current
+            assert result.minutes == 1
+            result = twoMinutesLater - current
+            assert result.minutes == 2
+        }
+    }
+
+    void testDateSubtractionOnHours() {
+        use(TimeCategory) {
+            def current = new Date(100, 0, 1, 0, 0, 0)
+            def oneHourLater = new Date(100, 0, 1, 1, 0, 0)
+            def twoHoursLater = new Date(100, 0, 1, 2, 0, 0)
+
+            def result = oneHourLater - current
+            assert result.hours == 1
+            result = twoHoursLater - current
+            assert result.hours == 2
+        }
+    }
+
+    void testDateSubtractionOnDays() {
+        use(TimeCategory) {
+            def current = new Date(100, 0, 1, 0, 0, 0)
+            def oneDayLater = new Date(100, 0, 2, 0, 0, 0)
+            def twoDaysLater = new Date(100, 0, 3, 0, 0, 0)
+
+            def result = oneDayLater - current
+            assert result.days == 1
+            result = twoDaysLater - current
+            assert result.days == 2
+        }
+    }
+
+    void testDateSubtraction_NoYearsOrMonths() {
+        use(TimeCategory) {
+            def yearOne = new Date(100, 0, 1, 0, 0, 0)
+            def yearThree = new Date(102, 0, 1, 0, 0, 0)
+
+            def result = yearThree - yearOne
+
+            //do NOT expect months and years to be
+            //set on the result of date subtraction
+            assert result.years == 0
+            assert result.months == 0
+
+        }
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/TupleListTest.java b/groovy/src/test/org/codehaus/groovy/runtime/TupleListTest.java
new file mode 100644
index 0000000..00edadc
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/TupleListTest.java
@@ -0,0 +1,93 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.runtime;
+
+import groovy.util.GroovyTestCase;
+
+import java.util.Iterator;
+import java.util.Map;
+
+
+/**
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class TupleListTest extends GroovyTestCase {
+
+    public void testIterateOverTuple() throws Exception {
+        StringBuffer buffer = new StringBuffer();
+        for (Iterator iter = InvokerHelper.asIterator(InvokerHelper.createTuple(new Object[]{"a", "b", "c"}));
+             iter.hasNext();
+                ) {
+            Object i = iter.next();
+            buffer.append(i);
+        }
+
+        assertEquals("buffer", "abc", buffer.toString());
+    }
+
+    public void testIterateOverList() throws Exception {
+        StringBuffer buffer = new StringBuffer();
+        for (Iterator iter = InvokerHelper.asIterator(InvokerHelper.createList(new Object[]{"a", "b", "c"}));
+             iter.hasNext();
+                ) {
+            Object i = iter.next();
+            buffer.append(i);
+        }
+
+        assertEquals("buffer", "abc", buffer.toString());
+    }
+
+    public void testCreateMap() throws Exception {
+        Map map = InvokerHelper.createMap(new Object[]{"a", "x", "b", "y"});
+
+        assertNotNull("map", map);
+        assertEquals("size", 2, map.size());
+        assertEquals("value of a", "x", map.get("a"));
+        assertEquals("value of b", "y", map.get("b"));
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/WithResourceStreamClosedTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/WithResourceStreamClosedTest.groovy
new file mode 100644
index 0000000..d48ce47
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/WithResourceStreamClosedTest.groovy
@@ -0,0 +1,34 @@
+package org.codehaus.groovy.runtime;
+
+/** 
+ * Test withWriter with inner loop closing the stream
+ * in advance.
+ * Some methods e.g. transformChar() close the streams.
+ * If used inside a withWriter(), it must not lead to 
+ * problems
+ * 
+ * @author Joachim Baumann</a>
+ * @version $Revision$
+ */
+
+class WithResourceStreamClosedTest extends GroovyTestCase {
+
+	void testWithWriterStreamClosed() {
+
+	    def outer = new StringWriter()
+	    def reader = new StringReader("Hallo Welt")
+
+	    outer.withWriter { writer ->
+	        reader.transformChar(writer) { it }
+	      
+	    }
+		assert outer.toString() == "Hallo Welt"
+	}
+	void testWithOutputStreamClosed() {
+	    def os = new ByteArrayOutputStream()
+	    os.withStream { out ->
+	        os.close()     
+	    }
+	}
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/runtime/WriterAppendTest.groovy b/groovy/src/test/org/codehaus/groovy/runtime/WriterAppendTest.groovy
new file mode 100644
index 0000000..9c3423c
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/runtime/WriterAppendTest.groovy
@@ -0,0 +1,223 @@
+package org.codehaus.groovy.runtime;

+

+import java.io.File

+import java.io.Reader

+

+/** 

+ * Test Writer append and left shift methods in Groovy

+ * 

+ * @author Joachim Baumann</a>

+ * @version $Revision$

+ */

+class WriterAppendTest extends GroovyTestCase {

+	/**

+	 * The following instances are used in testing the file writes

+	 */

+	static text = """

+			<groovy>

+			  <things>

+			    <thing>Jelly Beans</thing>

+			  </things>

+			  <music>

+			    <tune>The 59th Street Bridge Song</tune>

+			  </music>

+			  <characters>

+			    <character name="Austin Powers">

+			       <enemy>Dr. Evil</enemy>

+			       <enemy>Mini Me</enemy>

+			    </character>

+			  </characters>

+			</groovy>

+			"""

+	static gPathResult = new XmlSlurper().parseText(text)

+	static gPathWriteTo

+	static defaultEncoding

+	static UTF8_ENCODING

+

+	static {

+		StringWriter sw = new StringWriter()

+		gPathResult.writeTo(sw)

+		gPathWriteTo = sw.toString()

+		UTF8_ENCODING = "UTF-8"

+	    defaultEncoding = System.getProperty("file.encoding")

+	}

+

+	// Our file instance

+	def File file;

+	

+	void setUp() {

+		// Setup guarantees us that we use a non-existent file

+		file = File.createTempFile("unitTest", ".txt") 

+		assert file.exists() == true

+		//println file.canonicalPath

+		assert file.length() == 0L

+	}

+	void tearDown() {

+		// we remove our temporary file

+		def deleted = false

+		while(deleted == false)

+			deleted = file.delete()

+		assert file.exists() == false

+	}

+

+	void testAppendStringWithEncoding(){

+		def expected

+		// test new

+		file.withWriterAppend(UTF8_ENCODING) { writer ->

+		    writer.write(text)

+		}

+		expected = text

+		assert hasContents(file, expected, UTF8_ENCODING)

+		

+		// test existing

+		file.withWriterAppend(UTF8_ENCODING) { writer ->

+		    writer.write(text)

+		}

+		expected += text

+		assert hasContents(file, expected, UTF8_ENCODING)

+	}

+

+	void testAppendWritableWithEncoding(){

+		def expected

+		

+		// test new

+		file.withWriterAppend(UTF8_ENCODING) { writer ->

+		    writer.write(gPathResult)

+		}

+		expected = gPathWriteTo

+		assert hasContents(file, expected, UTF8_ENCODING)

+		

+		// test existing

+		file.withWriterAppend(UTF8_ENCODING) { writer ->

+		    writer.write(gPathResult)

+		}

+		expected += gPathWriteTo

+		assert hasContents(file, expected, UTF8_ENCODING)

+	}

+

+

+	void testLeftShiftStringWithEncoding(){

+		def expected

+		

+		// test new

+		file.withWriterAppend(UTF8_ENCODING) { writer ->

+		    writer << text

+		}

+		expected = text

+		assert hasContents(file, expected, UTF8_ENCODING)

+		

+		// test existing

+		file.withWriterAppend(UTF8_ENCODING) { writer ->

+		    writer << text

+		}

+		expected += text

+		assert hasContents(file, expected, UTF8_ENCODING)

+	}

+			

+

+	void testLeftShiftWritableWithEncoding(){

+		def expected

+		

+		// test new

+		file.withWriterAppend(UTF8_ENCODING) { writer ->

+		    writer << gPathResult

+		}

+		expected = gPathWriteTo

+		assert hasContents(file, expected, UTF8_ENCODING)

+		

+		// test existing

+		file.withWriterAppend(UTF8_ENCODING) { writer ->

+		    writer << gPathResult

+		}

+		expected += gPathWriteTo

+		assert hasContents(file, expected, UTF8_ENCODING)

+	}		 

+

+	///////////////////////////////////////

+	void testAppendStringDefaultEncoding(){

+		def expected

+		// test new

+		file.withWriterAppend { writer ->

+		    writer.write(text)

+		}

+		expected = text

+		assert hasContents(file, expected, defaultEncoding)

+		

+		// test existing

+		file.withWriterAppend { writer ->

+		    writer.write(text)

+		}

+		expected += text

+		assert hasContents(file, expected, defaultEncoding)

+	}

+

+	void testAppendWritableDefaultEncoding(){

+		def expected

+		

+		// test new

+		file.withWriterAppend { writer ->

+		    writer.write(gPathResult)

+		}

+		expected = gPathWriteTo

+		assert hasContents(file, expected, defaultEncoding)

+		

+		// test existing

+		file.withWriterAppend { writer ->

+		    writer.write(gPathResult)

+		}

+		expected += gPathWriteTo

+		assert hasContents(file, expected, defaultEncoding)

+	}

+

+

+	void testLeftShiftStringDefaultEncoding(){

+		def expected

+		

+		// test new

+		file.withWriterAppend { writer ->

+		    writer << text

+		}

+		expected = text

+		assert hasContents(file, expected, defaultEncoding)

+		

+		// test existing

+		file.withWriterAppend { writer ->

+		    writer << text

+		}

+		expected += text

+		assert hasContents(file, expected, defaultEncoding)

+	}

+			

+

+	void testLeftShiftWritableDefaultEncoding(){

+		def expected

+		

+		// test new

+		file.withWriterAppend { writer ->

+		    writer << gPathResult

+		}

+		expected = gPathWriteTo

+		assert hasContents(file, expected, defaultEncoding)

+		

+		// test existing

+		file.withWriterAppend { writer ->

+		    writer << gPathResult

+		}

+		expected += gPathWriteTo

+		assert hasContents(file, expected, defaultEncoding)

+	}		 

+

+	

+	boolean hasContents(File f, String expected, String charSet)

+	{

+		// read contents the Java way

+		byte[] buf = new byte[expected.length()];

+		

+		def fileIS = new FileInputStream(file)

+		fileIS.read(buf)

+		fileIS.close()

+		if (expected != new String(buf, charSet))

+		    println "EX: " + expected + "------" + new String(buf, charSet) + "\n----"

+		return expected == new String(buf, charSet)

+	}

+}

diff --git a/groovy/src/test/org/codehaus/groovy/syntax/TokenTest.java b/groovy/src/test/org/codehaus/groovy/syntax/TokenTest.java
new file mode 100644
index 0000000..2f10a75
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/syntax/TokenTest.java
@@ -0,0 +1,752 @@
+package org.codehaus.groovy.syntax;
+
+
+import groovy.util.GroovyTestCase;
+
+public class TokenTest
+        extends GroovyTestCase {
+
+    public void testNothing() {
+    }
+
+/*
+    private static final int LINE = 11;
+    private static final int COLUMN = 33;
+
+    public void testConstruct()
+    {
+        Token token = new Token( 42,
+                                 "forty-two",
+                                 11,
+                                 22 );
+
+        assertEquals( 42,
+                      token.getType() );
+
+        assertEquals( "forty-two",
+                      token.getText() );
+
+        assertEquals( 11,
+                      token.getStartLine() );
+
+        assertEquals( 22,
+                      token.getStartColumn() );
+    }
+
+    public void testLeftCurlyBrace()
+    {
+        Token token = Token.leftCurlyBrace( LINE,
+                                            COLUMN );
+
+        assertToken( token,
+                     Token.LEFT_CURLY_BRACE,
+                     "{" );
+    }
+
+    public void testRightCurlyBrace()
+    {
+        Token token = Token.rightCurlyBrace( LINE,
+                                             COLUMN );
+
+        assertToken( token,
+                     Token.RIGHT_CURLY_BRACE,
+                     "}" );
+    }
+
+    public void testLeftSquareBracket()
+    {
+        Token token = Token.leftSquareBracket( LINE,
+                                               COLUMN );
+
+        assertToken( token,
+                     Token.LEFT_SQUARE_BRACKET,
+                     "[" );
+    }
+
+    public void testRightSquareBracket()
+    {
+        Token token = Token.rightSquareBracket( LINE,
+                                                COLUMN );
+
+        assertToken( token,
+                     Token.RIGHT_SQUARE_BRACKET,
+                     "]" );
+    }
+
+    public void testLeftParenthesis()
+    {
+        Token token = Token.leftParenthesis( LINE,
+                                             COLUMN );
+
+        assertToken( token,
+                     Token.LEFT_PARENTHESIS,
+                     "(" );
+    }
+
+    public void testRightParenthesis()
+    {
+        Token token = Token.rightParenthesis( LINE,
+                                              COLUMN );
+
+        assertToken( token,
+                     Token.RIGHT_PARENTHESIS,
+                     ")" );
+    }
+
+    public void testDot()
+    {
+        Token token = Token.dot( LINE,
+                                 COLUMN );
+
+        assertToken( token,
+                     Token.DOT,
+                     "." );
+    }
+
+    public void testDotDot()
+    {
+        Token token = Token.dotDot( LINE,
+                                    COLUMN );
+
+        assertToken( token,
+                     Token.DOT_DOT,
+                     ".." );
+    }
+
+    public void testNot()
+    {
+        Token token = Token.not( LINE,
+                                 COLUMN );
+
+        assertToken( token,
+                     Token.NOT,
+                     "!" );
+    }
+
+    public void testCompareNotEqual()
+    {
+        Token token = Token.compareNotEqual( LINE,
+                                             COLUMN );
+
+        assertToken( token,
+                     Token.COMPARE_NOT_EQUAL,
+                     "!=" );
+    }
+
+    public void testEqual()
+    {
+        Token token = Token.equal( LINE,
+                                   COLUMN );
+
+        assertToken( token,
+                     Token.EQUAL,
+                     "=" );
+    }
+
+    public void testCompareIdentical()
+    {
+        Token token = Token.compareIdentical( LINE,
+                                              COLUMN );
+
+        assertToken( token,
+                     Token.COMPARE_IDENTICAL,
+                     "===" );
+    }
+
+    public void testCompareEqual()
+    {
+        Token token = Token.compareEqual( LINE,
+                                          COLUMN );
+
+        assertToken( token,
+                     Token.COMPARE_EQUAL,
+                     "==" );
+    }
+
+    public void testCompareLessThan()
+    {
+        Token token = Token.compareLessThan( LINE,
+                                             COLUMN );
+
+        assertToken( token,
+                     Token.COMPARE_LESS_THAN,
+                     "<" );
+    }
+
+    public void testCompareLessThanEqual()
+    {
+        Token token = Token.compareLessThanEqual( LINE,
+                                                  COLUMN );
+
+        assertToken( token,
+                     Token.COMPARE_LESS_THAN_EQUAL,
+                     "<=" );
+    }
+
+    public void testCompareGreaterThan()
+    {
+        Token token = Token.compareGreaterThan( LINE,
+                                                COLUMN );
+
+        assertToken( token,
+                     Token.COMPARE_GREATER_THAN,
+                     ">" );
+    }
+
+    public void testCompareGreaterThanEqual()
+    {
+        Token token = Token.compareGreaterThanEqual( LINE,
+                                                     COLUMN );
+
+        assertToken( token,
+                     Token.COMPARE_GREATER_THAN_EQUAL,
+                     ">=" );
+    }
+
+    public void testLogicalOr()
+    {
+        Token token = Token.logicalOr( LINE,
+                                       COLUMN );
+
+        assertToken( token,
+                     Token.LOGICAL_OR,
+                     "||" );
+    }
+
+    public void testLogicalAnd()
+    {
+        Token token = Token.logicalAnd( LINE,
+                                        COLUMN );
+
+        assertToken( token,
+                     Token.LOGICAL_AND,
+                     "&&" );
+    }
+
+    public void testPlus()
+    {
+        Token token = Token.plus( LINE,
+                                  COLUMN );
+
+        assertToken( token,
+                     Token.PLUS,
+                     "+" );
+    }
+
+    public void testPlusPlus()
+    {
+        Token token = Token.plusPlus( LINE,
+                                      COLUMN );
+
+        assertToken( token,
+                     Token.PLUS_PLUS,
+                     "++" );
+    }
+
+    public void testPlusEqual()
+    {
+        Token token = Token.plusEqual( LINE,
+                                       COLUMN );
+
+        assertToken( token,
+                     Token.PLUS_EQUAL,
+                     "+=" );
+    }
+
+    public void testMinus()
+    {
+        Token token = Token.minus( LINE,
+                                   COLUMN );
+
+        assertToken( token,
+                     Token.MINUS,
+                     "-" );
+    }
+
+    public void testMinusMinus()
+    {
+        Token token = Token.minusMinus( LINE,
+                                        COLUMN );
+
+        assertToken( token,
+                     Token.MINUS_MINUS,
+                     "--" );
+    }
+
+    public void testMinusEqual()
+    {
+        Token token = Token.minusEqual( LINE,
+                                        COLUMN );
+
+        assertToken( token,
+                     Token.MINUS_EQUAL,
+                     "-=" );
+    }
+
+    public void testDivide()
+    {
+        Token token = Token.divide( LINE,
+                                    COLUMN );
+
+        assertToken( token,
+                     Token.DIVIDE,
+                     "/" );
+    }
+
+    public void testDivideEqual()
+    {
+        Token token = Token.divideEqual( LINE,
+                                         COLUMN );
+
+        assertToken( token,
+                     Token.DIVIDE_EQUAL,
+                     "/=" );
+    }
+
+    public void testMod()
+    {
+        Token token = Token.mod( LINE,
+                                 COLUMN );
+
+        assertToken( token,
+                     Token.MOD,
+                     "%" );
+    }
+
+    public void testModEqual()
+    {
+        Token token = Token.modEqual( LINE,
+                                      COLUMN );
+
+        assertToken( token,
+                     Token.MOD_EQUAL,
+                     "%=" );
+    }
+
+    public void testMultiply()
+    {
+        Token token = Token.multiply( LINE,
+                                      COLUMN );
+
+        assertToken( token,
+                     Token.MULTIPLY,
+                     "*" );
+    }
+
+    public void testMultiplyEqual()
+    {
+        Token token = Token.multiplyEqual( LINE,
+                                           COLUMN );
+
+        assertToken( token,
+                     Token.MULTIPLY_EQUAL,
+                     "*=" );
+    }
+
+    public void testComma()
+    {
+        Token token = Token.comma( LINE,
+                                   COLUMN );
+
+        assertToken( token,
+                     Token.COMMA,
+                     "," );
+    }
+
+    public void testColon()
+    {
+        Token token = Token.colon( LINE,
+                                   COLUMN );
+
+        assertToken( token,
+                     Token.COLON,
+                     ":" );
+    }
+
+    public void testSemicolon()
+    {
+        Token token = Token.semicolon( LINE,
+                                       COLUMN );
+
+        assertToken( token,
+                     Token.SEMICOLON,
+                     ";" );
+    }
+
+    public void testQuestion()
+    {
+        Token token = Token.question( LINE,
+                                      COLUMN );
+
+        assertToken( token,
+                     Token.QUESTION,
+                     "?" );
+    }
+
+    public void testPipe()
+    {
+        Token token = Token.pipe( LINE,
+                                  COLUMN );
+
+        assertToken( token,
+                     Token.PIPE,
+                     "|" );
+    }
+
+    public void testDoubleQuoteString()
+    {
+        Token token = Token.doubleQuoteString( LINE,
+                                               COLUMN,
+                                               "cheese" );
+
+        assertToken( token,
+                     Token.DOUBLE_QUOTE_STRING,
+                     "cheese",
+                     "<string literal>");
+    }
+
+    public void testSingleQuoteString()
+    {
+        Token token = Token.singleQuoteString( LINE,
+                                               COLUMN,
+                                               "cheese" );
+
+        assertToken( token,
+                     Token.SINGLE_QUOTE_STRING,
+                     "cheese",
+                     "<string literal>" );
+    }
+
+    public void testIdentifier()
+    {
+        Token token = Token.identifier( LINE,
+                                        COLUMN,
+                                        "cheese" );
+
+        assertToken( token,
+                     Token.IDENTIFIER,
+                     "cheese",
+                     "<identifier>" );
+    }
+
+    public void testIntegerNumber()
+    {
+        Token token = Token.integerNumber( LINE,
+                                           COLUMN,
+                                           "42" );
+
+        assertToken( token,
+                     Token.INTEGER_NUMBER,
+                     "42",
+                     "<number>" );
+    }
+
+    public void testFloatNumber()
+    {
+        Token token = Token.floatNumber( LINE,
+                                         COLUMN,
+                                         "42.84" );
+
+        assertToken( token,
+                     Token.FLOAT_NUMBER,
+                     "42.84",
+                     "<number>" );
+    }
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+
+    public void testKeyword_As()
+    {
+        assertKeywordToken( "as",
+                            Token.KEYWORD_AS );
+    }
+
+    public void testKeyword_Abstract()
+    {
+        assertKeywordToken( "abstract",
+                            Token.KEYWORD_ABSTRACT );
+    }
+
+    public void testKeyword_Break()
+    {
+        assertKeywordToken( "break",
+                            Token.KEYWORD_BREAK );
+    }
+
+    public void testKeyword_Case()
+    {
+        assertKeywordToken( "case",
+                            Token.KEYWORD_CASE );
+    }
+
+    public void testKeyword_Catch()
+    {
+        assertKeywordToken( "catch",
+                            Token.KEYWORD_CATCH );
+    }
+
+    public void testKeyword_Class()
+    {
+        assertKeywordToken( "class",
+                            Token.KEYWORD_CLASS );
+    }
+
+    public void testKeyword_Const()
+    {
+        assertKeywordToken( "const",
+                            Token.KEYWORD_CONST );
+    }
+
+    public void testKeyword_Continue()
+    {
+        assertKeywordToken( "continue",
+                            Token.KEYWORD_CONTINUE );
+    }
+
+    public void testKeyword_Default()
+    {
+        assertKeywordToken( "default",
+                            Token.KEYWORD_DEFAULT );
+    }
+
+    public void testKeyword_Do()
+    {
+        assertKeywordToken( "do",
+                            Token.KEYWORD_DO );
+    }
+
+    public void testKeyword_Else()
+    {
+        assertKeywordToken( "else",
+                            Token.KEYWORD_ELSE );
+    }
+
+    public void testKeyword_Extends()
+    {
+        assertKeywordToken( "extends",
+                            Token.KEYWORD_EXTENDS );
+    }
+
+    public void testKeyword_Final()
+    {
+        assertKeywordToken( "final",
+                            Token.KEYWORD_FINAL );
+    }
+
+    public void testKeyword_Finally()
+    {
+        assertKeywordToken( "finally",
+                            Token.KEYWORD_FINALLY );
+    }
+
+    public void testKeyword_For()
+    {
+        assertKeywordToken( "for",
+                            Token.KEYWORD_FOR );
+    }
+
+    public void testKeyword_Goto()
+    {
+        assertKeywordToken( "goto",
+                            Token.KEYWORD_GOTO );
+    }
+
+    public void testKeyword_If()
+    {
+        assertKeywordToken( "if",
+                            Token.KEYWORD_IF );
+    }
+
+    public void testKeyword_Implements()
+    {
+        assertKeywordToken( "implements",
+                            Token.KEYWORD_IMPLEMENTS );
+    }
+
+    public void testKeyword_Import()
+    {
+        assertKeywordToken( "import",
+                            Token.KEYWORD_IMPORT );
+    }
+
+    public void testKeyword_Instanceof()
+    {
+        assertKeywordToken( "instanceof",
+                            Token.KEYWORD_INSTANCEOF );
+    }
+
+    public void testKeyword_Interface()
+    {
+        assertKeywordToken( "interface",
+                            Token.KEYWORD_INTERFACE );
+    }
+
+    public void testKeyword_Native()
+    {
+        assertKeywordToken( "native",
+                            Token.KEYWORD_NATIVE );
+    }
+
+    public void testKeyword_New()
+    {
+        assertKeywordToken( "new",
+                            Token.KEYWORD_NEW );
+    }
+
+    public void testKeyword_Package()
+    {
+        assertKeywordToken( "package",
+                            Token.KEYWORD_PACKAGE );
+    }
+
+    public void testKeyword_Private()
+    {
+        assertKeywordToken( "private",
+                            Token.KEYWORD_PRIVATE );
+    }
+
+    public void testKeyword_Property()
+    {
+        assertKeywordToken( "property",
+                            Token.KEYWORD_PROPERTY );
+    }
+
+    public void testKeyword_Protected()
+    {
+        assertKeywordToken( "protected",
+                            Token.KEYWORD_PROTECTED );
+    }
+
+    public void testKeyword_Public()
+    {
+        assertKeywordToken( "public",
+                            Token.KEYWORD_PUBLIC );
+    }
+
+    public void testKeyword_Return()
+    {
+        assertKeywordToken( "return",
+                            Token.KEYWORD_RETURN );
+    }
+
+    public void testKeyword_Static()
+    {
+        assertKeywordToken( "static",
+                            Token.KEYWORD_STATIC );
+    }
+
+    public void testKeyword_Super()
+    {
+        assertKeywordToken( "super",
+                            Token.KEYWORD_SUPER );
+    }
+
+    public void testKeyword_Switch()
+    {
+        assertKeywordToken( "switch",
+                            Token.KEYWORD_SWITCH );
+    }
+
+    public void testKeyword_Synchronized()
+    {
+        assertKeywordToken( "synchronized",
+                            Token.KEYWORD_SYNCHRONIZED );
+    }
+
+    public void testKeyword_This()
+    {
+        assertKeywordToken( "this",
+                            Token.KEYWORD_THIS );
+    }
+
+    public void testKeyword_Throw()
+    {
+        assertKeywordToken( "throw",
+                            Token.KEYWORD_THROW );
+    }
+
+    public void testKeyword_Throws()
+    {
+        assertKeywordToken( "throws",
+                            Token.KEYWORD_THROWS );
+    }
+
+    public void testKeyword_Try()
+    {
+        assertKeywordToken( "try",
+                            Token.KEYWORD_TRY );
+    }
+
+    public void testKeyword_While()
+    {
+        assertKeywordToken( "while",
+                            Token.KEYWORD_WHILE );
+    }
+
+    public void testUniqueKeywordTypes()
+    {
+        Map keywords = Token.getKeywordMap();
+
+        Set types = new HashSet();
+
+        types.addAll( keywords.values() );
+
+        assertEquals( types.size(),
+                      keywords.size() );
+    }
+
+    public void testUnknownTokenType()
+    {
+        assertEquals( "<unknown>",
+                      Token.getTokenDescription( 6666 ) );
+    }
+
+    // ----------------------------------------------------------------------
+    // ----------------------------------------------------------------------
+
+    protected void assertKeywordToken(String text,
+                                      int expectedType)
+    {
+        Token token = Token.keyword( LINE,
+                                     COLUMN,
+                                     text );
+
+        assertToken( token,
+                     expectedType,
+                     text );
+    }
+
+    protected void assertToken(Token token,
+                               int type,
+                               String text)
+    {
+        assertToken( token,
+                     type,
+                     text,
+                     '"' + text + '"' );
+    }
+
+    protected void assertToken(Token token,
+                               int type,
+                               String text,
+                               String description)
+    {
+        assertEquals( type,
+                      token.getType() );
+
+        assertEquals( text,
+                      token.getText() );
+
+        assertEquals( description,
+                      token.getDescription() );
+
+        assertEquals( LINE,
+                      token.getStartLine() );
+
+        assertEquals( COLUMN,
+                      token.getStartColumn() );
+    }
+    
+*/
+}
diff --git a/groovy/src/test/org/codehaus/groovy/syntax/parser/TestParserSupport.java b/groovy/src/test/org/codehaus/groovy/syntax/parser/TestParserSupport.java
new file mode 100644
index 0000000..11f49ec
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/syntax/parser/TestParserSupport.java
@@ -0,0 +1,56 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+ * 
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met: 1. Redistributions of source code must retain
+ * copyright statements and notices. Redistributions must also contain a copy
+ * of this document. 2. Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the distribution. 3.
+ * The name "groovy" must not be used to endorse or promote products derived
+ * from this Software without prior written permission of The Codehaus. For
+ * written permission, please contact info@codehaus.org. 4. Products derived
+ * from this Software may not be called "groovy" nor may "groovy" appear in
+ * their names without prior written permission of The Codehaus. "groovy" is a
+ * registered trademark of The Codehaus. 5. Due credit should be given to The
+ * Codehaus - http://groovy.codehaus.org/
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *  
+ */
+package org.codehaus.groovy.syntax.parser;
+
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.control.SourceUnit;
+
+
+/**
+ * An abstract base class useful for AST parser related test cases
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public abstract class TestParserSupport extends GroovyTestCase {
+
+    public ModuleNode parse(String text, String description) throws Exception {
+        SourceUnit unit = SourceUnit.create(description, text);
+        unit.parse();
+        unit.convert();
+
+        return unit.getAST();
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/CompilerTest.java b/groovy/src/test/org/codehaus/groovy/tools/CompilerTest.java
new file mode 100644
index 0000000..05dbe70
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/CompilerTest.java
@@ -0,0 +1,92 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.tools;
+
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.control.CompilerConfiguration;
+
+import java.io.File;
+
+/**
+ * A handy unit test case for dumping the output of the compiler
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class CompilerTest extends GroovyTestCase {
+
+    Compiler compiler = null;
+    boolean dumpClass = true;
+
+    public void testMethodCall() throws Exception {
+        //runTest("ClosureMethodTest.groovy");
+        //runTest("tree/VerboseTreeTest.groovy");
+        //runTest("tree/NestedClosureBugTest.groovy");
+        runTest("tree/SmallTreeTest.groovy");
+        //runTest("LittleClosureTest.groovy");
+    }
+
+    protected void runTest(String name) throws Exception {
+        File file = new File("src/test/groovy/" + name);
+
+        assertTrue("Could not find source file: " + file, file.exists());
+
+        compiler.compile(file);
+    }
+
+    protected void setUp() throws Exception {
+        File dir = new File("target/test-generated-classes");
+        dir.mkdirs();
+
+        CompilerConfiguration config = new CompilerConfiguration();
+        config.setTargetDirectory(dir);
+        config.setDebug(dumpClass);
+
+        compiler = new Compiler(config);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/DocGeneratorMain.java b/groovy/src/test/org/codehaus/groovy/tools/DocGeneratorMain.java
new file mode 100644
index 0000000..b28baa0
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/DocGeneratorMain.java
@@ -0,0 +1,20 @@
+package org.codehaus.groovy.tools;
+
+import groovy.lang.GroovyShell;
+
+import java.io.File;
+
+public class DocGeneratorMain {
+
+    public static void main(String[] args) {
+        try {
+            GroovyShell shell = new GroovyShell();
+            //shell.run("src/main/org/codehaus/groovy/tools/DocGenerator.groovy", "org.codehaus.groovy.tools.DocGenerator.groovy", args);
+            shell.run(new File("src/main/org/codehaus/groovy/tools/DocGenerator.groovy"), args);
+        }
+        catch (Exception e) {
+            System.out.println("Failed: " + e);
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/FileSystemCompilerTest.java b/groovy/src/test/org/codehaus/groovy/tools/FileSystemCompilerTest.java
new file mode 100644
index 0000000..53386b4
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/FileSystemCompilerTest.java
@@ -0,0 +1,97 @@
+/*
+ $Id$
+
+ Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+
+ Redistribution and use of this software and associated documentation
+ ("Software"), with or without modification, are permitted provided
+ that the following conditions are met:
+
+ 1. Redistributions of source code must retain copyright
+    statements and notices.  Redistributions must also contain a
+    copy of this document.
+
+ 2. Redistributions in binary form must reproduce the
+    above copyright notice, this list of conditions and the
+    following disclaimer in the documentation and/or other
+    materials provided with the distribution.
+
+ 3. The name "groovy" must not be used to endorse or promote
+    products derived from this Software without prior written
+    permission of The Codehaus.  For written permission,
+    please contact info@codehaus.org.
+
+ 4. Products derived from this Software may not be called "groovy"
+    nor may "groovy" appear in their names without prior written
+    permission of The Codehaus. "groovy" is a registered
+    trademark of The Codehaus.
+
+ 5. Due credit should be given to The Codehaus -
+    http://groovy.codehaus.org/
+
+ THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
+ NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
+ THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ */
+
+package org.codehaus.groovy.tools;
+
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.control.CompilerConfiguration;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Tests the compiling & running of GroovyTestCases
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class FileSystemCompilerTest extends GroovyTestCase {
+
+    FileSystemCompiler compiler = null;
+    final boolean dumpClass = true;
+
+    public void testMethodCall() throws Exception {
+        //runTest("ClosureMethodTest.groovy");
+        //runTest("tree/VerboseTreeTest.groovy");
+        //runTest("tree/NestedClosureBugTest.groovy");
+        runTest("tree/SmallTreeTest.groovy");
+        //runTest("LittleClosureTest.groovy");
+    }
+
+    protected void runTest(String name) throws Exception {
+        File file = new File("src/test/groovy/" + name);
+
+        assertTrue("Could not find source file: " + file, file.exists());
+
+        compiler.compile(new File[]{file});
+    }
+
+    protected void setUp() throws Exception {
+        File dir = new File("target/test-generated-classes");
+        dir.mkdirs();
+        Map options = new HashMap();
+        options.put("stubDir", dir);
+
+        CompilerConfiguration configuration = new CompilerConfiguration();
+        configuration.setTargetDirectory(dir);
+        configuration.setVerbose(dumpClass);
+        configuration.setJointCompilationOptions(options);
+
+        compiler = new FileSystemCompiler(configuration);
+    }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/FindAllTestsSuite.java b/groovy/src/test/org/codehaus/groovy/tools/FindAllTestsSuite.java
new file mode 100644
index 0000000..55047ed
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/FindAllTestsSuite.java
@@ -0,0 +1,131 @@
+/*
+ * $Id$
+ * 
+ * Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
+ * 
+ * Redistribution and use of this software and associated documentation
+ * ("Software"), with or without modification, are permitted provided that the
+ * following conditions are met:
+ *  1. Redistributions of source code must retain copyright statements and
+ * notices. Redistributions must also contain a copy of this document.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *  3. The name "groovy" must not be used to endorse or promote products
+ * derived from this Software without prior written permission of The Codehaus.
+ * For written permission, please contact info@codehaus.org.
+ *  4. Products derived from this Software may not be called "groovy" nor may
+ * "groovy" appear in their names without prior written permission of The
+ * Codehaus. "groovy" is a registered trademark of The Codehaus.
+ *  5. Due credit should be given to The Codehaus - http://groovy.codehaus.org/
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *  
+ */
+package org.codehaus.groovy.tools;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import junit.textui.TestRunner;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * A TestSuite which will run a Groovy unit test case inside any Java IDE
+ * either as a unit test case or as an application.
+ * <p/>
+ * You can specify the GroovyUnitTest to run by running this class as an appplication
+ * and specifying the script to run on the command line.
+ * <p/>
+ * <code>
+ * java groovy.util.GroovyTestSuite src/test/Foo.groovy
+ * </code>
+ * <p/>
+ * Or to run the test suite as a unit test suite in an IDE you can use
+ * the 'test' system property to define the test script to run.
+ * e.g. pass this into the JVM when the unit test plugin runs...
+ * <p/>
+ * <code>
+ * -Dtest=src/test/Foo.groovy
+ * </code>
+ *
+ * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
+ * @version $Revision$
+ */
+public class FindAllTestsSuite extends TestSuite {
+
+    protected static final String testDirectory = "target/test-classes";
+
+    public static void main(String[] args) {
+        TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        FindAllTestsSuite suite = new FindAllTestsSuite();
+        try {
+            suite.loadTestSuite();
+        } catch (Exception e) {
+            throw new RuntimeException("Could not create the test suite: " + e, e);
+        }
+        return suite;
+    }
+
+    public void loadTestSuite() throws Exception {
+        recurseDirectory(new File(testDirectory));
+    }
+
+    protected void recurseDirectory(File dir) throws Exception {
+        File[] files = dir.listFiles();
+        List traverseList = new ArrayList();
+        for (int i = 0; i < files.length; i++) {
+            File file = files[i];
+            if (file.isDirectory()) {
+                traverseList.add(file);
+            } else {
+                String name = file.getName();
+                if (name.endsWith("Test.class") || name.endsWith("Bug.class")) {
+                    addTest(file);
+                }
+            }
+        }
+        for (Iterator iter = traverseList.iterator(); iter.hasNext();) {
+            recurseDirectory((File) iter.next());
+        }
+    }
+
+    protected void addTest(File file) throws Exception {
+        String name = file.getPath();
+
+        name = name.substring(testDirectory.length() + 1, name.length() - ".class".length());
+        name = name.replace(File.separatorChar, '.');
+
+        //System.out.println("Found: " + name);
+        Class type = loadClass(name);
+        addTestSuite(type);
+    }
+
+    protected Class loadClass(String name) throws ClassNotFoundException {
+        try {
+            return Thread.currentThread().getContextClassLoader().loadClass(name);
+        } catch (ClassNotFoundException e) {
+            try {
+                return getClass().getClassLoader().loadClass(name);
+            } catch (ClassNotFoundException e1) {
+                return Class.forName(name);
+            }
+        }
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/LoaderConfigurationTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/LoaderConfigurationTest.groovy
new file mode 100644
index 0000000..7b8cb22
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/LoaderConfigurationTest.groovy
@@ -0,0 +1,90 @@
+package org.codehaus.groovy.tools

+

+class LoaderConfigurationTest extends GroovyTestCase {

+

+  void testComment() {

+    def txt = "# I am a comment"

+    

+    def config = new LoaderConfiguration()

+    config.requireMain = false

+    config.configure(new StringBufferInputStream(txt))

+    

+    assert config.classPathUrls.length == 0  

+  }

+  

+  void testNormalPath() {

+    // generate a load instruction with a valid path

+    def file = new File(".")

+    def txt = "load $file"

+    

+    def config = new LoaderConfiguration()

+    config.requireMain = false

+    config.configure(new StringBufferInputStream(txt))

+    

+    assert config.classPathUrls.length == 1

+    assert config.classPathUrls[0].sameFile(file.toURI().toURL())

+  }

+  

+  void testNonexistingPath() {

+    // generate a load instruction with a valid path

+    def file = getNonexistantFile(new File("."))

+

+    def txt = "load $file"

+    

+    def config = new LoaderConfiguration()

+    config.requireMain = false

+    config.configure(new StringBufferInputStream(txt))

+    

+    assert config.classPathUrls.length == 0

+  }  

+  

+  private File getNonexistantFile(File base) {

+    def number = "0"

+    while (base.exists()) {

+      base = new File(base,number)

+      number++

+    }

+    return base

+  }

+  

+  void testExistingProperty() {

+    def txt = 'load ${java.home}'

+    

+    def config = new LoaderConfiguration()

+    config.requireMain = false

+    config.configure(new StringBufferInputStream(txt))

+    

+    assert config.classPathUrls.length == 1

+    def url1 = config.classPathUrls[0]

+    def url2 = new File(System.getProperty("java.home")).toURI().toURL()

+    assert url1.sameFile(url2)

+  }  

+  

+  void testNonexistingProperty() {

+     String name = getNonexistingPropertyName("foo")

+     

+    def txt = 'load !{'+name+'}'

+    

+    def config = new LoaderConfiguration()

+    config.requireMain = false

+    shouldFail {

+        config.configure(new StringBufferInputStream(txt))

+    }

+    

+    txt = 'load ${'+name+'}'

+    

+    config = new LoaderConfiguration()

+    config.requireMain = false

+    config.configure(new StringBufferInputStream(txt))

+    

+    assert config.classPathUrls.length == 0

+  }

+  

+  

+  private getNonexistingPropertyName(String base) {

+    while (System.getProperty(base)!=null) {

+      base += "x"

+    }

+    return base

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/StringHelperTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/StringHelperTest.groovy
new file mode 100644
index 0000000..8573a4c
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/StringHelperTest.groovy
@@ -0,0 +1,18 @@
+package org.codehaus.groovy.tools

+

+import static org.codehaus.groovy.tools.StringHelper.*

+

+class StringHelperTest extends GroovyTestCase {

+

+  void testTokenize() {

+    assert tokenizeUnquoted("a b") == ["a","b"]

+    assert tokenizeUnquoted("a 'b a'") == ["a","'b a'"]

+    assert tokenizeUnquoted(" a 'b a'") == ["a","'b a'"]

+    assert tokenizeUnquoted(" a 'b a'" ) == ["a","'b a'"]

+    assert tokenizeUnquoted('a "b a"') == ["a",'"b a"']

+    assert tokenizeUnquoted("a 'b \"a c\"'") == ["a","'b \"a c\"'"]

+    assert tokenizeUnquoted("a \"b 'a c'\"") == ["a","\"b 'a c'\""]

+    assert tokenizeUnquoted("'a ") == ["'a "]

+    assert tokenizeUnquoted("\"a 'b'") == ["\"a 'b'"]

+  }

+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java b/groovy/src/test/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
new file mode 100644
index 0000000..7ccf0e8
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTest.java
@@ -0,0 +1,165 @@
+/*
+ *
+ * Copyright 2007 Jeremy Rayner
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.codehaus.groovy.tools.groovydoc;
+
+import groovy.util.GroovyTestCase;
+import org.codehaus.groovy.groovydoc.GroovyClassDoc;
+import org.codehaus.groovy.groovydoc.GroovyMethodDoc;
+import org.codehaus.groovy.groovydoc.GroovyRootDoc;
+
+public class GroovyDocToolTest extends GroovyTestCase {
+    GroovyDocTool xmlTool;
+    GroovyDocTool xmlToolForTests;
+    GroovyDocTool plainTool;
+    private static final String FS = "/";
+    private static final String MOCK_DIR = "mock" + FS + "doc";
+    private static final String TEMPLATES_DIR = "main" + FS + "org" + FS + "codehaus" + FS + "groovy" + FS + "tools" + FS + "groovydoc" + FS + "gstring-templates";
+
+    public void setUp() {
+        plainTool = new GroovyDocTool("src" + FS + "test");
+
+        xmlTool = new GroovyDocTool(
+                new FileSystemResourceManager("src"), // template storage
+                "src" + FS + "main", // source file dirs
+                new String[]{TEMPLATES_DIR + FS + "top-level" + FS + "rootDocStructuredData.xml"},
+                new String[]{TEMPLATES_DIR + FS + "package-level" + FS + "packageDocStructuredData.xml"},
+                new String[]{TEMPLATES_DIR + FS + "class-level" + FS + "classDocStructuredData.xml"}
+        );
+
+        xmlToolForTests = new GroovyDocTool(
+                new FileSystemResourceManager("src"), // template storage
+                "src" + FS + "test", // source file dirs
+                new String[]{TEMPLATES_DIR + FS + "top-level" + FS + "rootDocStructuredData.xml"},
+                new String[]{TEMPLATES_DIR + FS + "package-level" + FS + "packageDocStructuredData.xml"},
+                new String[]{TEMPLATES_DIR + FS + "class-level" + FS + "classDocStructuredData.xml"}
+        );
+    }
+
+    public void testPlainGroovyDocTool() throws Exception {
+        plainTool.add("org" + FS + "codehaus" + FS + "groovy" + FS + "tools" + FS + "groovydoc" + FS + "GroovyDocToolTest.java");
+        GroovyRootDoc root = plainTool.getRootDoc();
+
+        // loop through classes in tree
+        GroovyClassDoc[] classDocs = root.classes();
+        for (int i=0; i< classDocs.length; i++) {
+            GroovyClassDoc clazz = root.classes()[i];
+
+            assertEquals("GroovyDocToolTest", clazz.name());
+
+            // loop through methods in class
+            boolean seenThisMethod = false;
+            GroovyMethodDoc[] methodDocs = clazz.methods();
+            for (int j=0; j< methodDocs.length; j++) {
+                GroovyMethodDoc method = clazz.methods()[j];
+
+                if ("testPlainGroovyDocTool".equals(method.name())) {
+                    seenThisMethod = true;
+                }
+
+            }
+            assertTrue(seenThisMethod);
+        }
+    }
+
+    public void testGroovyDocTheCategoryMethodClass() throws Exception {
+        xmlTool.add("groovy" + FS + "util" + FS + "CliBuilder.groovy");
+        xmlTool.add("groovy" + FS + "lang" + FS + "GroovyLogTestCase.groovy");
+        xmlTool.add("groovy" + FS + "mock" + FS + "interceptor" + FS + "StrictExpectation.groovy");
+        xmlTool.add("groovy" + FS + "ui" + FS + "Console.groovy");
+        xmlTool.add("org" + FS + "codehaus" + FS + "groovy" + FS + "runtime" + FS + "GroovyCategorySupport.java");
+        xmlTool.add("org" + FS + "codehaus" + FS + "groovy" + FS + "runtime" + FS + "ConvertedMap.java");
+        MockOutputTool output = new MockOutputTool();
+        xmlTool.renderToOutput(output, MOCK_DIR);
+
+        String categoryMethodDocument = output.getText(MOCK_DIR + FS + "org" + FS + "codehaus" + FS + "groovy" + FS + "runtime" + FS + "CategoryMethod.html"); // todo - figure out how to get xml extension for templates
+
+        assertTrue(categoryMethodDocument.indexOf("<method returns=\"boolean\" name=\"hasCategoryInAnyThread\">") > 0);
+
+        String packageDocument = output.getText(MOCK_DIR + FS + "org" + FS + "codehaus" + FS + "groovy" + FS + "runtime" + FS + "packageDocStructuredData.xml");
+        assertTrue(packageDocument.indexOf("<class name=\"CategoryMethod\" />") > 0);
+
+        String rootDocument = output.getText(MOCK_DIR + FS + "rootDocStructuredData.xml");
+        assertTrue(rootDocument.indexOf("<package name=\"org" + FS + "codehaus" + FS + "groovy" + FS + "runtime\" />") > 0);
+        assertTrue(rootDocument.indexOf("<class path=\"org" + FS + "codehaus" + FS + "groovy" + FS + "runtime" + FS + "CategoryMethod\" name=\"CategoryMethod\" />") > 0);
+    }
+
+    public void testConstructors() throws Exception {
+        xmlTool.add("groovy" + FS + "ui" + FS + "Console.groovy");
+        MockOutputTool output = new MockOutputTool();
+        xmlTool.renderToOutput(output, MOCK_DIR);
+
+        String consoleDoc = output.getText(MOCK_DIR + FS + "groovy" + FS + "ui" + FS + "Console.html");
+        assertTrue(consoleDoc.indexOf("<constructor name=\"Console\">") > 0);
+        assertTrue(consoleDoc.indexOf("<parameter type=\"ClassLoader\" name=\"parent\" />") > 0);
+    }
+
+    public void testClassComment() throws Exception {
+        xmlTool.add("groovy" + FS + "xml" + FS + "DOMBuilder.java");
+        MockOutputTool output = new MockOutputTool();
+        xmlTool.renderToOutput(output, MOCK_DIR);
+
+        String domBuilderDoc = output.getText(MOCK_DIR + FS + "groovy" + FS + "xml" + FS + "DOMBuilder.html");
+        assertTrue(domBuilderDoc.indexOf("A helper class for creating a W3C DOM tree") > 0);
+    }
+
+    public void testMethodComment() throws Exception {
+        xmlTool.add("groovy" + FS + "model" + FS + "DefaultTableColumn.java");
+        MockOutputTool output = new MockOutputTool();
+        xmlTool.renderToOutput(output, MOCK_DIR);
+
+        String defTabColDoc = output.getText(MOCK_DIR + FS + "groovy" + FS + "model" + FS + "DefaultTableColumn.html");
+        System.out.println(defTabColDoc);
+
+        assertTrue(defTabColDoc.indexOf("Evaluates the value of a cell") > 0);
+    }
+    public void testPackageName() throws Exception {
+        xmlTool.add("groovy" + FS + "xml" + FS + "DOMBuilder.java");
+        MockOutputTool output = new MockOutputTool();
+        xmlTool.renderToOutput(output, MOCK_DIR);
+
+        String domBuilderDoc = output.getText(MOCK_DIR + FS + "groovy" + FS + "xml" + FS + "DOMBuilder.html");
+        assertTrue(domBuilderDoc.indexOf("<containingPackage name=\"groovy" + FS + "xml\">groovy.xml</containingPackage>") > 0);
+    }
+
+    public void testExtendsClauseWithoutSuperClassInTree() throws Exception {
+        xmlTool.add("groovy" + FS + "xml" + FS + "DOMBuilder.java");
+        MockOutputTool output = new MockOutputTool();
+        xmlTool.renderToOutput(output, MOCK_DIR);
+
+        String domBuilderDoc = output.getText(MOCK_DIR + FS + "groovy" + FS + "xml" + FS + "DOMBuilder.html");
+        assertTrue(domBuilderDoc.indexOf("<extends>BuilderSupport</extends>") > 0);
+    }
+
+    public void testExtendsClauseWithSuperClassInTree() throws Exception {
+        xmlTool.add("groovy" + FS + "xml" + FS + "DOMBuilder.java");
+        xmlTool.add("groovy" + FS + "util" + FS + "BuilderSupport.java");
+        MockOutputTool output = new MockOutputTool();
+        xmlTool.renderToOutput(output, MOCK_DIR);
+
+        String domBuilderDoc = output.getText(MOCK_DIR + FS + "groovy" + FS + "xml" + FS + "DOMBuilder.html");
+        assertTrue(domBuilderDoc.indexOf("<extends>BuilderSupport</extends>") > 0);
+    }
+    
+    public void testDefaultPackage() throws Exception {
+    	xmlToolForTests.add("UberTestCaseBugs.java");
+        MockOutputTool output = new MockOutputTool();
+        xmlToolForTests.renderToOutput(output, MOCK_DIR);
+        String domBuilderDoc = output.getText(MOCK_DIR + FS + "DefaultPackage" + FS + "UberTestCaseBugs.html");
+        assertTrue(domBuilderDoc.indexOf("<extends>TestCase</extends>") > 0);    	
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTestSampleGroovy.groovy b/groovy/src/test/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTestSampleGroovy.groovy
new file mode 100644
index 0000000..9dbe01b
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/groovydoc/GroovyDocToolTestSampleGroovy.groovy
@@ -0,0 +1,25 @@
+package org.codehaus.groovy.tools.groovydoc;
+
+public class GroovyDocToolTestSampleGroovy {
+  /**
+    * First method. Simple case
+    */
+  def firstMethod() {
+  }
+
+  /** Second method. Has return type */
+  String secondMethod() {}
+
+  /** Third method. Has primitive return type */
+  char thirdMethod() {}
+
+  /** Fourth method. Has fully qualified return type */
+  java.util.Date fourthMethod() {}
+
+  /*
+    * Fifth method. Doesn't have any groovydoc (note: single * at beginning)
+    */
+  def fifthMethod() {
+  }
+
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/AliasCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/AliasCommandTest.groovy
new file mode 100644
index 0000000..428633e
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/AliasCommandTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link AliasCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class AliasCommandTest
+    extends CommandTestSupport
+{
+    void testAlias() {
+        try {
+            shell << 'alias'
+            fail()
+        }
+        catch (CommandException expected) {}
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ClearCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ClearCommandTest.groovy
new file mode 100644
index 0000000..fd8b666
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ClearCommandTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link ClearCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ClearCommandTest
+    extends CommandTestSupport
+{
+    void testClear() {
+        shell << 'clear'
+    }
+
+    void testClearWithArgs() {
+        try {
+            shell << 'clear foo'
+            fail()
+        }
+        catch (CommandException expected) {}
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/CommandTestSupport.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/CommandTestSupport.groovy
new file mode 100644
index 0000000..edbf4fc
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/CommandTestSupport.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.Groovysh
+
+/**
+ * Support for testing {@link Command} instances.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+abstract class CommandTestSupport
+    extends GroovyTestCase
+{
+    Groovysh shell
+
+    Object lastResult
+
+    void setUp() {
+        super.setUp()
+
+        shell = new Groovysh()
+
+        shell.errorHook = { Throwable cause ->
+            throw cause
+        }
+
+        shell.resultHook = { result ->
+            lastResult = result
+        }
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/DisplayCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/DisplayCommandTest.groovy
new file mode 100644
index 0000000..39257f0
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/DisplayCommandTest.groovy
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link DisplayCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class DisplayCommandTest
+    extends CommandTestSupport
+{
+    void testDisplay() {
+        shell << 'display'
+    }
+
+    void testDisplayWithArgs() {
+        try {
+            shell << 'display foo'
+            fail()
+        }
+        catch (CommandException expected) {}
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/EditCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/EditCommandTest.groovy
new file mode 100644
index 0000000..3548588
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/EditCommandTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link EditCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class EditCommandTest
+    extends CommandTestSupport
+{
+    void testTODO() {}
+
+    /*
+    void testEdit() {
+        shell << 'edit'
+    }
+    */
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ExitCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ExitCommandTest.groovy
new file mode 100644
index 0000000..50350e1
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ExitCommandTest.groovy
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.ExitNotification
+
+/**
+ * Tests for the {@link ExitCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ExitCommandTest
+    extends CommandTestSupport
+{
+    void testWithNoArgs() {
+        try {
+            shell << 'exit'
+            fail()
+        }
+        catch (ExitNotification e) {
+            // expected
+        }
+    }
+    
+    /*
+    FIXME: Errors don't currently throw anything, just io.error.println()'s...
+    
+    void testWithArgs() {
+        try {
+            shell << 'exit foo'
+            fail()
+        }
+        catch (Exception e) {
+            // expected
+        }
+    }
+    */
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/HelpCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/HelpCommandTest.groovy
new file mode 100644
index 0000000..fd9c6bc
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/HelpCommandTest.groovy
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+/**
+ * Tests for the {@link HelpCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class HelpCommandTest
+    extends CommandTestSupport
+{
+    void testList() {
+        shell << 'help'
+    }
+    
+    void testCommandHelp() {
+        shell << 'help exit'
+    }
+    
+    void testCommandHelpInvalidCommand() {
+        try {
+            shell << 'help no-such-command'
+        }
+        catch (Exception e) {
+            // expected
+        }
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/HistoryCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/HistoryCommandTest.groovy
new file mode 100644
index 0000000..e14a2c0
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/HistoryCommandTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link HistoryCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class HistoryCommandTest
+    extends CommandTestSupport
+{
+    void testHistory() {
+        try {
+            shell << 'history'
+            fail()
+        }
+        catch (CommandException expected) {}
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ImportCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ImportCommandTest.groovy
new file mode 100644
index 0000000..f65a91a
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ImportCommandTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link ImportCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ImportCommandTest
+    extends CommandTestSupport
+{
+    void testImport() {
+        try {
+            shell << 'import'
+            fail()
+        }
+        catch (CommandException expected) {}
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/InspectCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/InspectCommandTest.groovy
new file mode 100644
index 0000000..8376969
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/InspectCommandTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link InspectCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class InspectCommandTest
+    extends CommandTestSupport
+{
+    void testInspect() {
+        shell << 'inspect'
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/LoadCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/LoadCommandTest.groovy
new file mode 100644
index 0000000..1afd90f
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/LoadCommandTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link LoadCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class LoadCommandTest
+    extends CommandTestSupport
+{
+    void testLoad() {
+        try {
+            shell << 'load'
+            fail()
+        }
+        catch (CommandException expected) {}
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/PurgeCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/PurgeCommandTest.groovy
new file mode 100644
index 0000000..f6e45a4
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/PurgeCommandTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+/**
+ * Tests for the {@link PurgeCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class PurgeCommandTest
+    extends CommandTestSupport
+{
+    void testPurgeVariables() {
+        shell << 'purge variables'
+    }
+
+    void testPurgeClasses() {
+        shell << 'purge classes'
+    }
+
+    void testPurgeImports() {
+        shell << 'purge imports'
+    }
+
+    void testPurgePreferences() {
+        shell << 'purge preferences'
+    }
+
+    void testPurgeAll() {
+        shell << 'purge all'
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/RecordCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/RecordCommandTest.groovy
new file mode 100644
index 0000000..b8a444a
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/RecordCommandTest.groovy
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+/**
+ * Tests for the {@link RecordCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class RecordCommandTest
+    extends CommandTestSupport
+{
+    void testStopNotStarted() {
+        try {
+            shell << 'record stop'
+            fail()
+        }
+        catch (Exception expected) {}
+    }
+
+    void testStartAlreadyStarted() {
+        shell << 'record start'
+
+        try {
+            shell << 'record start'
+            fail()
+        }
+        catch (Exception expected) {}
+
+        def file = shell << 'record stop'
+
+        file.delete()
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/SaveCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/SaveCommandTest.groovy
new file mode 100644
index 0000000..bd8af23
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/SaveCommandTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link SaveCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class SaveCommandTest
+    extends CommandTestSupport
+{
+    void testSave() {
+        try {
+            shell << 'save'
+            fail()
+        }
+        catch (CommandException expected) {}
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/SetCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/SetCommandTest.groovy
new file mode 100644
index 0000000..b8cf1c5
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/SetCommandTest.groovy
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link SetCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class SetCommandTest
+    extends CommandTestSupport
+{
+    void testSet() {
+        shell << 'set'
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ShowCommandTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ShowCommandTest.groovy
new file mode 100644
index 0000000..47b2b0d
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/commands/ShowCommandTest.groovy
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.commands
+
+import org.codehaus.groovy.tools.shell.CommandException
+
+/**
+ * Tests for the {@link ShowCommand} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ShowCommandTest
+    extends CommandTestSupport
+{
+    void testShow() {
+        try {
+            shell << 'show'
+            fail()
+        }
+        catch (CommandException expected) {}
+    }
+}
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/expr/ClassWithPrivateConstructor.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/expr/ClassWithPrivateConstructor.groovy
new file mode 100644
index 0000000..c2f0225
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/expr/ClassWithPrivateConstructor.groovy
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.expr
+
+/**
+ * Tests for classes defined which have private constructors.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ClassWithPrivateConstructor
+    extends ExprTestSupport
+{
+    void testClass() {
+        shell.execute('class K { private K(){} }')
+    }
+    void testEnum() {
+        shell.execute('enum E {A,B,C}')
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/expr/ExitTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/expr/ExitTest.groovy
new file mode 100644
index 0000000..f1b4e89
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/expr/ExitTest.groovy
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.expr
+
+/**
+ * Tests that we can't call <tt>System.exit()</tt>.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class ExitTest
+    extends ExprTestSupport
+{
+    void testSystemExit() {
+        /*
+        FIXME: Need to get this one working... its not happy right now
+        
+        try {
+            shell.execute('System.exit(0)')
+            fail()
+        }
+        catch (SecurityException expected) {}
+        */
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/expr/ExprTestSupport.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/expr/ExprTestSupport.groovy
new file mode 100644
index 0000000..2dcf07c
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/expr/ExprTestSupport.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.expr
+
+import org.codehaus.groovy.tools.shell.Groovysh
+
+/**
+ * Support for expression tests.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+abstract class ExprTestSupport
+    extends GroovyTestCase
+{
+    Groovysh shell
+    
+    Object lastResult
+
+    void setUp() {
+        super.setUp()
+
+        shell = new Groovysh()
+
+        shell.errorHook = { Throwable cause ->
+            throw cause
+        }
+
+        shell.resultHook = { result ->
+            lastResult = result
+        }
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/expr/TimeItTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/expr/TimeItTest.groovy
new file mode 100644
index 0000000..3c3ee9d
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/expr/TimeItTest.groovy
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.expr
+
+/**
+ * Tests for <tt>time = { it() }</tt> expressions.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class TimeItTest
+    extends ExprTestSupport
+{
+    void testSingleLine() {
+        def result = shell.execute('time = { it() }')
+        assert result != null
+    }
+
+    void testMultiLine() {
+        def result
+
+        result = shell.execute('time = {')
+        assert result == null
+
+        result = shell.execute('it()')
+        assert result == null
+
+        result = shell.execute('}')
+        assert result != null
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/util/MessageSourceTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/shell/util/MessageSourceTest.groovy
new file mode 100644
index 0000000..a07f0ef
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/util/MessageSourceTest.groovy
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2003-2007 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.codehaus.groovy.tools.shell.util
+
+/**
+ * Unit tests for the {@link MessageSource} class.
+ *
+ * @version $Id$
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ */
+class MessageSourceTest
+    extends GroovyTestCase
+{
+    MessageSource messages
+
+    void setUp() {
+        messages = new MessageSource(this.class)
+    }
+    
+    void testLoadAndGetMessage() {
+        def a = messages['a']
+        assert '1' == a
+
+        def b = messages['b']
+        assert '2' == b
+
+        def c = messages['c']
+        assert '3' == c
+
+        def f = messages.format('f', a, b, c)
+        assert '1 2 3' == f
+    }
+}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/shell/util/MessageSourceTest.properties b/groovy/src/test/org/codehaus/groovy/tools/shell/util/MessageSourceTest.properties
new file mode 100644
index 0000000..42b2060
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/shell/util/MessageSourceTest.properties
@@ -0,0 +1,27 @@
+#
+# Copyright 2003-2007 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# $Id$
+#
+
+a=1
+
+b=2
+
+c=3
+
+f={0} {1} {2}
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/xml/DomToGroovyTest.groovy b/groovy/src/test/org/codehaus/groovy/tools/xml/DomToGroovyTest.groovy
new file mode 100644
index 0000000..d942972
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/xml/DomToGroovyTest.groovy
@@ -0,0 +1,116 @@
+package org.codehaus.groovy.tools.xml

+

+import java.io.*

+import javax.xml.parsers.DocumentBuilder

+import javax.xml.parsers.DocumentBuilderFactory

+import org.w3c.dom.Document

+import org.xml.sax.InputSource

+import org.xml.sax.SAXException

+

+/**

+ * @author James Strachan

+ * @author paulk

+ * @version $Revision: 4111 $

+ */

+public class DomToGroovyTest extends GroovyTestCase {

+    private static final String LS = System.getProperty("line.separator")

+    private static final String TEST_XML_1 =

+        "<a href='http://groovy.codehaus.org'>Groovy</a>"

+    private static final String TEST_XML_2 =

+        "<project name='testProject'><target name='testTarget'><echo>message</echo><echo/></target></project>"

+    private static final String TEST_XML_3 = '''<?xml version="1.0"?>

+        <!-- this example demonstrates using markup to specify a rich user interface -->

+        <frame size="[300,300]" text="My Window">

+          <label bounds="[10,10,290,30]" text="Save changes"/>

+          <panel bounds="[10,40,290,290]">

+            <button action="save()" text="OK"/>

+            <button action="close()" text="Cancel"/>

+          </panel>

+        </frame>'''

+    private static final String TEST_XML_4 = '''

+        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

+          <xsd:simpleType name="SKU">

+            <xsd:restriction base="xsd:string">

+              <xsd:pattern value="\\d{3}-[A-Z]{2}"/>

+            </xsd:restriction>

+          </xsd:simpleType>

+        </xsd:schema>'''

+    private static final String EXPECTED_BUILDER_SCRIPT_1 =

+        "a(href:'http://groovy.codehaus.org', 'Groovy')"

+    private static final String EXPECTED_BUILDER_SCRIPT_2 = '''

+        project(name:'testProject') {

+          target(name:'testTarget') {

+            echo('message')

+            echo()

+          }

+        }'''

+    private static final String EXPECTED_BUILDER_SCRIPT_3 = '''

+        /* this example demonstrates using markup to specify a rich user interface */

+        frame(size:'[300,300]', text:'My Window') {

+          label(bounds:'[10,10,290,30]', text:'Save changes')

+          panel(bounds:'[10,40,290,290]') {

+            button(action:'save()', text:'OK')

+            button(action:'close()', text:'Cancel')

+          }

+        }'''

+    private static final String EXPECTED_BUILDER_SCRIPT_4 = '''

+        mkp.declareNamespace(xsd:'http://www.w3.org/2001/XMLSchema')

+        'xsd.schema'() {

+          'xsd.simpleType'(name:'SKU') {

+            'xsd.restriction'(base:'xsd:string') {

+              'xsd.pattern'(value:'\\\\d{3}-[A-Z]{2}')

+            }

+          }

+        }'''

+

+    protected DocumentBuilder builder

+    protected DomToGroovy converter

+    protected File dir = new File("target/generated-groovyxml")

+

+    public void testConversion() throws Exception {

+        convert("test1.xml", "test1.groovy")

+        convert("po.xsd", "poSchema.groovy")

+        convert("swing.xml", "swing.groovy")

+    }

+

+    public void testConversionFormat() throws Exception {

+        checkConversion(TEST_XML_1, EXPECTED_BUILDER_SCRIPT_1)

+        checkConversion(TEST_XML_2, EXPECTED_BUILDER_SCRIPT_2)

+        checkConversion(TEST_XML_3, EXPECTED_BUILDER_SCRIPT_3)

+        checkConversion(TEST_XML_4, EXPECTED_BUILDER_SCRIPT_4)

+    }

+

+    private void checkConversion(String testXml, String expectedScript) throws SAXException, IOException {

+        ByteArrayInputStream inputStream = new ByteArrayInputStream(testXml.getBytes())

+        Document document = builder.parse(inputStream)

+        StringWriter writer = new StringWriter()

+        converter = new DomToGroovy(new PrintWriter(writer))

+        converter.print(document)

+        StringTestUtil.assertMultilineStringsEqual(expectedScript, writer.toString())

+    }

+

+    private void convert(String name, String output) throws Exception {

+        Document document = parse(name)

+        PrintWriter writer = new PrintWriter(new FileWriter(new File(dir, output)))

+        converter = new DomToGroovy(writer)

+        writer.println("#!/bin/groovy")

+        writer.println()

+        writer.println("// generated from " + name)

+        writer.println()

+        converter.print(document)

+        writer.close()

+    }

+

+    private Document parse(String name) throws SAXException, IOException {

+        URL resource = getClass().getResource(name)

+        assertTrue("Could not find resource: " + name, resource != null)

+        return builder.parse(new InputSource(resource.toString()))

+    }

+

+    protected void setUp() throws Exception {

+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance()

+        factory.setNamespaceAware(true)

+        builder = factory.newDocumentBuilder()

+        dir.mkdirs()

+    }

+}

diff --git a/groovy/src/test/org/codehaus/groovy/tools/xml/po.xsd b/groovy/src/test/org/codehaus/groovy/tools/xml/po.xsd
new file mode 100644
index 0000000..fea7d12
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/xml/po.xsd
@@ -0,0 +1,66 @@
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+    <xsd:annotation>
+        <xsd:documentation xml:lang="en">
+            Purchase order schema for Example.com.
+            Copyright 2000 Example.com. All rights reserved.
+        </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:element name="purchaseOrder" type="PurchaseOrderType"/>
+
+    <xsd:element name="comment" type="xsd:string"/>
+
+    <xsd:complexType name="PurchaseOrderType">
+        <xsd:sequence>
+            <xsd:element name="shipTo" type="USAddress"/>
+            <xsd:element name="billTo" type="USAddress"/>
+            <xsd:element ref="comment" minOccurs="0"/>
+            <xsd:element name="items" type="Items"/>
+        </xsd:sequence>
+        <xsd:attribute name="orderDate" type="xsd:date"/>
+    </xsd:complexType>
+
+    <xsd:complexType name="USAddress">
+        <xsd:sequence>
+            <xsd:element name="name" type="xsd:string"/>
+            <xsd:element name="street" type="xsd:string"/>
+            <xsd:element name="city" type="xsd:string"/>
+            <xsd:element name="state" type="xsd:string"/>
+            <xsd:element name="zip" type="xsd:decimal"/>
+        </xsd:sequence>
+        <xsd:attribute name="country" type="xsd:NMTOKEN"
+                       fixed="US"/>
+    </xsd:complexType>
+
+    <xsd:complexType name="Items">
+        <xsd:sequence>
+            <xsd:element name="item" minOccurs="0" maxOccurs="unbounded">
+                <xsd:complexType>
+                    <xsd:sequence>
+                        <xsd:element name="productName" type="xsd:string"/>
+                        <xsd:element name="quantity">
+                            <xsd:simpleType>
+                                <xsd:restriction base="xsd:positiveInteger">
+                                    <xsd:maxExclusive value="100"/>
+                                </xsd:restriction>
+                            </xsd:simpleType>
+                        </xsd:element>
+                        <xsd:element name="USPrice" type="xsd:decimal"/>
+                        <xsd:element ref="comment" minOccurs="0"/>
+                        <xsd:element name="shipDate" type="xsd:date" minOccurs="0"/>
+                    </xsd:sequence>
+                    <xsd:attribute name="partNum" type="SKU" use="required"/>
+                </xsd:complexType>
+            </xsd:element>
+        </xsd:sequence>
+    </xsd:complexType>
+
+    <!-- Stock Keeping Unit, a code for identifying products -->
+    <xsd:simpleType name="SKU">
+        <xsd:restriction base="xsd:string">
+            <xsd:pattern value="\d{3}-[A-Z]{2}"/>
+        </xsd:restriction>
+    </xsd:simpleType>
+
+</xsd:schema> 
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/xml/swing.xml b/groovy/src/test/org/codehaus/groovy/tools/xml/swing.xml
new file mode 100644
index 0000000..b325b33
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/xml/swing.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<!-- this example demonstrates using markup to specify a rich user interface -->
+<frame text="My Window" size="[300,300]">
+    <label text="Save changes" bounds="[10,10,290,30]"/>
+    <panel bounds="[10,40,290,290]">
+        <button text="OK" action="save()"/>
+        <button text="Cancel" action="close()"/>
+    </panel>
+</frame>
+ 
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/xml/swing2.xml b/groovy/src/test/org/codehaus/groovy/tools/xml/swing2.xml
new file mode 100644
index 0000000..b325b33
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/xml/swing2.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+<!-- this example demonstrates using markup to specify a rich user interface -->
+<frame text="My Window" size="[300,300]">
+    <label text="Save changes" bounds="[10,10,290,30]"/>
+    <panel bounds="[10,40,290,290]">
+        <button text="OK" action="save()"/>
+        <button text="Cancel" action="close()"/>
+    </panel>
+</frame>
+ 
\ No newline at end of file
diff --git a/groovy/src/test/org/codehaus/groovy/tools/xml/test1.xml b/groovy/src/test/org/codehaus/groovy/tools/xml/test1.xml
new file mode 100644
index 0000000..e04bb44
--- /dev/null
+++ b/groovy/src/test/org/codehaus/groovy/tools/xml/test1.xml
@@ -0,0 +1,19 @@
+<html>
+    <head>
+        <title>XML encoding with Groovy</title>
+    </head>
+    <body>
+        <h1>XML encoding with Groovy</h1>
+        <p>this format can be used as an alternative markup to XML</p>
+        <!-- an element with attributes and text content -->
+        <a href="http://groovy.codehaus.org">Groovy</a>
+        <!-- mixed content -->
+        <p>This is some
+            <b>mixed</b>
+            text. For more see the
+            <a href="http://groovy.codehaus.org">Groovy</a>
+            project
+        </p>
+        <p>some text</p>
+    </body>
+</html>
\ No newline at end of file
diff --git a/groovy/src/tools/org/codehaus/groovy/tools/DocGenerator.groovy b/groovy/src/tools/org/codehaus/groovy/tools/DocGenerator.groovy
new file mode 100644
index 0000000..558dea7
--- /dev/null
+++ b/groovy/src/tools/org/codehaus/groovy/tools/DocGenerator.groovy
@@ -0,0 +1,303 @@
+package org.codehaus.groovy.tools
+
+import groovy.xml.StreamingMarkupBuilder
+
+import java.io.File
+
+import com.thoughtworks.qdox.JavaDocBuilder
+import com.thoughtworks.qdox.model.JavaSource
+import com.thoughtworks.qdox.model.JavaClass
+import com.thoughtworks.qdox.model.JavaMethod
+import com.thoughtworks.qdox.model.JavaParameter
+import com.thoughtworks.qdox.model.Type
+import java.util.*;
+
+
+/**
+ * Generate documentation about the methods provided by the Groovy Development Kit
+ * enhancing the standard JDK classes.
+ *
+ * @author Guillaume Laforge, John Wilson
+ */
+class DocGenerator
+{
+	def sourceFiles = []
+	File outputFolder
+	JavaDocBuilder builder
+	// categorize all groovy methods per core JDK class to which it applies
+	def jdkEnhancedClasses = [:]
+    def packages = [:]
+	def sortedPackages
+
+	DocGenerator(sourceFiles, File outputFolder)
+	{
+		this.sourceFiles = sourceFiles
+		this.outputFolder = outputFolder
+		parse()
+	}
+
+	/**
+	 * Parse the DefaultGroovyMethods class to build a graph representing the structure of the class,
+	 * with its methods, javadoc comments and tags.
+	 */
+	private void parse()
+	{
+		builder = new JavaDocBuilder()
+		sourceFiles.each {
+			println "adding reader for $it"
+			builder.addSource(it.newReader())
+		}
+
+		def sources = builder.getSources()
+
+		def methods = []
+		sources.each { source ->
+			def classes = source.getClasses()
+			classes.each { aClass ->
+				methods.addAll (aClass.methods as List)
+			}
+		}
+	
+        def start = System.currentTimeMillis();
+		for (method in methods) {
+			if (method.isPublic() && method.isStatic()) {
+				def parameters = method.getParameters()
+				def jdkClass = parameters[0].getType().toString()
+
+				if (jdkClass.startsWith('groovy')) {
+					// nothing, skip it
+				}
+				else if (jdkEnhancedClasses.containsKey(jdkClass)) {
+				    List l = jdkEnhancedClasses[jdkClass];
+					l.add(method)
+			    }
+				else
+					jdkEnhancedClasses[jdkClass] = [method]
+			}
+		}
+
+		jdkEnhancedClasses.keySet().each { className ->
+        	def thePackage = className.contains(".") ? className.replaceFirst(/\.[^\.]*$/, "") : ""
+        	if (!packages.containsKey(thePackage)) {
+        		packages[thePackage] = []
+        	}
+    		packages[thePackage] << className
+        }
+        sortedPackages = new TreeSet(packages.keySet())
+	}
+
+	/**
+	 * Builds an HTML page from the structure of DefaultGroovyMethods.
+	 */
+		
+	def generateNew() {
+		def engine = new groovy.text.SimpleTemplateEngine()
+
+		// the index
+		def templateIndex = createTemplate(engine, 'index.html')
+		def out = new File(outputFolder, 'index.html')
+		def binding = [packages: sortedPackages]
+		out.withWriter {
+			it << templateIndex.make(binding)
+		}
+		// the overview
+		def templateOverview = createTemplate(engine, 'overview-summary.html')
+		out = new File(outputFolder, 'overview-summary.html')
+		binding = [packages: sortedPackages]
+		out.withWriter {
+			it << templateOverview.make(binding)
+		}
+		
+		def templateOverviewFrame = createTemplate(engine, 'template.overview-frame.html')
+		out = new File(outputFolder, 'overview-frame.html')
+		binding = [packages: sortedPackages]
+		out.withWriter {
+			it << templateOverviewFrame.make(binding)
+		}
+		
+		// the allclasses-frame.html
+		def templateAllClasses = createTemplate(engine, 'template.allclasses-frame.html')
+		out = new File(outputFolder, 'allclasses-frame.html')
+		binding = [classes: jdkEnhancedClasses.keySet().sort { it.replaceAll('.*\\.', '')}]
+		out.withWriter {
+			it << templateAllClasses.make(binding)
+		}
+		
+		// the package-frame.html for each package
+		def templatePackageFrame = createTemplate(engine, 'template.package-frame.html')
+		packages.each { curPackage, packageClasses ->
+			def packageName = curPackage ? curPackage : "primitive-types"
+			generatePackageFrame(templatePackageFrame, packageName, packageClasses)
+		}		
+		
+		// the class.html for each class
+		def templateClass = createTemplate(engine, 'template.class.html')
+		packages.each { curPackage, packageClasses ->
+			def packageName = curPackage ? curPackage : "primitive-types"
+			packageClasses.each {
+				generateClassDetails(templateClass, packageName, it)
+			}
+		}		
+		
+	}
+	private generateClassDetails(template, curPackage, aClass)
+	{
+        def packagePath = generatePackagePath(curPackage)
+		def dir = new File(outputFolder, packagePath)
+		dir.mkdirs()
+		def out = new File(dir, aClass.replaceAll('.*\\.', '') + '.html')
+		def listOfMethods = jdkEnhancedClasses[aClass].sort{ it.name }
+		def methods = []
+		listOfMethods.each { method ->
+			def parameters = method.getTagsByName("param").collect { [name: it.value.replaceAll(' .*', ''), comment: it.value.replaceAll('^\\w*', '')]}
+			if (parameters)
+				parameters.remove(0) // method is static, first arg is the "real this"
+
+			def returnType = getReturnType(method)
+			def methodInfo = [name: method.name, 
+			                  comment: getComment(method),
+			                  shortComment: getComment(method).replaceAll('\\..*', ''),
+			                  returnComment: method.getTagByName("return")?.getValue() ?: '',
+			                  returnTypeDocUrl: getDocUrl(returnType),
+			                  parametersSignature: getParametersDecl(method),
+			                  parametersDocUrl: getParametersDocUrl(method),
+			                  parameters: parameters,
+			                  isStatic: method.parentClass.name == 'DefaultGroovyStaticMethods']
+			methods << methodInfo
+		}
+
+		def binding = [className: aClass.replaceAll(/.*\./, ''),
+		           packageName: curPackage,
+		           methods: methods]
+		out.withWriter {
+			it << template.make(binding)
+		}
+	}
+
+	private String getParametersDocUrl(method)
+	{
+		getParameters(method).collect{"${getDocUrl(it.type.toString())} ${it.getName()}" }.join(", ")
+	}
+
+	private String getDocUrl(type)
+	{
+		if (!type.contains('.'))
+			return type
+		
+		def shortClassName = type.replaceAll(".*\\.", "")
+		def packageName = type[0..(-shortClassName.size()-2)] 
+		def apiBaseUrl, title
+		if (type.startsWith("groovy")) {
+			apiBaseUrl = "http://groovy.codehaus.org/api/"
+			title = "Groovy class in $packageName"
+		}
+		else {
+			apiBaseUrl = "http://java.sun.com/j2se/1.4.2/docs/api/"
+			title = "JDK class in $packageName"
+		}
+
+		def url = apiBaseUrl + type.replaceAll("\\.", "/") + '.html'
+		return "<a href='$url' title='$title'>$shortClassName</a>"
+	}
+
+    private generatePackagePath(curPackage)
+    {
+        def fileSep = File.separator
+        // need to escape separator on windows for regex's sake
+        if (fileSep == '\\') fileSep *= 2
+        return curPackage.replaceAll('\\.', fileSep)
+    }
+
+	private generatePackageFrame(templatePackageFrame, curPackage, packageClasses)
+	{
+        def packagePath = generatePackagePath(curPackage)
+        def dir = new File(outputFolder, packagePath)
+		dir.mkdirs()
+		def out = new File(dir, 'package-frame.html')
+		def binding = [classes: packageClasses.sort().collect { it.replaceAll(/.*\./, '')},
+		           packageName: curPackage]
+		out.withWriter {
+			it << templatePackageFrame.make(binding)
+		}
+	}
+	
+	def createTemplate(templateEngine, resourceFile)
+	{
+		def resourceUrl = getClass().getResource(resourceFile)
+		return templateEngine.createTemplate(resourceUrl.text)
+	}
+
+
+	/**
+ 	* Retrieves a String representing the return type
+ 	*/
+	private getReturnType(method)
+	{
+	    def returnType = method.getReturns()
+	    
+	    if (returnType != null) {
+	    	    return returnType.toString()
+	    } else {
+	    	    return ""
+	    }
+	}
+
+	/**
+	 * Retrieve a String representing the declaration of the parameters of the method passed as parameter.
+	 *
+	 * @param method a method
+	 * @return the declaration of the method (long version)
+	 */
+	private getParametersDecl(method)
+	{
+		getParameters(method).collect{ "${it.getType()} ${it.getName()}" }.join(", ")
+	}
+
+	/**
+	 * Retrieve the parameters of the method.
+	 *
+	 * @param method a method
+	 * @return a list of parameters without the first one
+	 */
+	private getParameters(method)
+	{
+	    if (method.getParameters().size() > 1)
+		    return method.getParameters().toList()[1..-1]
+		else
+		    return []
+	}
+
+	/**
+	 * Retrieve the JavaDoc comment associated with the method passed as parameter.
+	 *
+	 * @param method a method
+	 * @return the JavaDoc comment associated with this method
+	 */
+	private getComment(method)
+	{
+		def ans = method.getComment()
+		if (ans == null) return ""
+		return ans
+	}
+
+    /**
+     * Main entry point.
+     */
+    static void main(args)
+    {
+        def defaultGroovyMethodSource = new File("src/main/org/codehaus/groovy/runtime/DefaultGroovyMethods.java")
+        def defaultGroovyStaticMethodSource = new File("src/main/org/codehaus/groovy/runtime/DefaultGroovyStaticMethods.java")
+        def outFolder = new File("target/html/groovy-jdk")
+        outFolder.mkdirs()
+
+        def start = System.currentTimeMillis();
+
+        def docGen = new DocGenerator([defaultGroovyMethodSource, defaultGroovyStaticMethodSource], outFolder)
+        docGen.generateNew()
+
+        def end = System.currentTimeMillis();
+
+        println "Done. in ${end - start} millis"
+
+    }
+}
diff --git a/groovy/src/tools/org/codehaus/groovy/tools/groovy.ico b/groovy/src/tools/org/codehaus/groovy/tools/groovy.ico
new file mode 100644
index 0000000..9e9b8d8
--- /dev/null
+++ b/groovy/src/tools/org/codehaus/groovy/tools/groovy.ico
Binary files differ
diff --git a/groovy/src/tools/org/codehaus/groovy/tools/index.html b/groovy/src/tools/org/codehaus/groovy/tools/index.html
new file mode 100644
index 0000000..b1dd463
--- /dev/null
+++ b/groovy/src/tools/org/codehaus/groovy/tools/index.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<TITLE>
+Groovy JDK
+</TITLE>
+<link href="groovy.ico" type="image/x-icon" rel="shortcut icon">
+<link href="groovy.ico" type="image/x-icon" rel="icon">
+
+<SCRIPT type="text/javascript">
+targetPage = "" + window.location.search;
+if (targetPage != "" && targetPage != "undefined")
+   targetPage = targetPage.substring(1);
+
+function loadFrames() 
+{
+    if (targetPage != "" && targetPage != "undefined")
+         top.classFrame.location = top.targetPage;
+}
+</SCRIPT>
+</HEAD>
+<FRAMESET cols="20%,80%" title="" onLoad="top.loadFrames()">
+<FRAMESET rows="30%,70%" title="" onLoad="top.loadFrames()">
+	<FRAME src="overview-frame.html" name="packageListFrame" title="All Packages">
+	<FRAME src="allclasses-frame.html" name="packageFrame" title="All classes and interfaces (except non-static nested types)">
+</FRAMESET>
+
+<FRAME src="overview-summary.html" name="classFrame" title="Package, class and interface descriptions" scrolling="yes">
+<NOFRAMES>
+<H2>
+Frame Alert</H2>
+<P>
+This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+<BR>
+Link to<A HREF="overview-summary.html">Non-frame version.</A>
+</NOFRAMES>
+</FRAMESET>
+</HTML>
diff --git a/groovy/src/tools/org/codehaus/groovy/tools/overview-summary.html b/groovy/src/tools/org/codehaus/groovy/tools/overview-summary.html
new file mode 100644
index 0000000..fa8b90a
--- /dev/null
+++ b/groovy/src/tools/org/codehaus/groovy/tools/overview-summary.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN""http://www.w3.org/TR/REC-html40/loose.dtd">
+<HTML>
+<HEAD>
+<TITLE>
+Overview (Groovy JDK)
+</TITLE>
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+</HEAD>
+<SCRIPT>
+function asd()
+{
+parent.document.title = document.title;
+}
+</SCRIPT>
+<BODY BGCOLOR="white" onload="asd();">
+
+
+<HR>
+<CENTER>
+<H2>
+Groovy JDK<br>API Specification</H2>
+</CENTER>
+<body>
+This document describes the methods added to the JDK to make it more groovy.
+
+</BODY>
+</HTML>
diff --git a/groovy/src/tools/org/codehaus/groovy/tools/template.allclasses-frame.html b/groovy/src/tools/org/codehaus/groovy/tools/template.allclasses-frame.html
new file mode 100644
index 0000000..398c8ee
--- /dev/null
+++ b/groovy/src/tools/org/codehaus/groovy/tools/template.allclasses-frame.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<TITLE>
+All Classes (Groovy GDK)
+</TITLE>
+
+
+<LINK REL ="stylesheet" TYPE="text/css" HREF="stylesheet.css" TITLE="Style">
+
+
+</HEAD>
+
+<BODY BGCOLOR="white">
+<FONT size="+1" CLASS="FrameHeadingFont">
+<B>All Classes</B></FONT>
+
+<BR>
+
+<TABLE BORDER="0" WIDTH="100%" SUMMARY="">
+<TR>
+<TD NOWRAP>
+
+<FONT CLASS="FrameItemFont">
+<% classes.each { %>
+<A HREF="${it.replaceAll('\\.', '/')}.html" target="classFrame">${it.replaceAll('.*\\.', '')}</A>
+<BR>
+<% } %>
+</FONT></TD>
+</TR>
+</TABLE>
+
+</BODY>
+</HTML>
diff --git a/groovy/src/tools/org/codehaus/groovy/tools/template.class.html b/groovy/src/tools/org/codehaus/groovy/tools/template.class.html
new file mode 100644
index 0000000..3f9d584
--- /dev/null
+++ b/groovy/src/tools/org/codehaus/groovy/tools/template.class.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<HTML>
+<HEAD>
+<TITLE>${className} (Groovy JDK)</TITLE>
+
+<LINK REL ="stylesheet" TYPE="text/css" HREF="../../stylesheet.css" TITLE="Style">
+
+<SCRIPT type="text/javascript">
+function windowTitle()
+{
+    parent.document.title = document.title;
+}
+</SCRIPT>
+
+</HEAD>
+
+<BODY BGCOLOR="white" onload="windowTitle();">
+
+
+<!-- ======== START OF CLASS DATA ======== -->
+<H2>
+<FONT SIZE="-1">
+${packageName}</FONT>
+<BR>
+Class ${className}</H2>
+
+<!-- ========== METHOD SUMMARY =========== -->
+
+<A NAME="method_summary"><!-- --></A>
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TH ALIGN="left" COLSPAN="2"><FONT SIZE="+2">
+<B>Method Summary</B></FONT></TH>
+</TR>
+
+<% methods.each { method -> %>
+<TR BGCOLOR="white" CLASS="TableRowColor">
+<TD ALIGN="right" VALIGN="top" WIDTH="1%">
+
+<FONT SIZE="-1">
+<CODE>${method.isStatic ? 'static ' : ''}${method.returnTypeDocUrl}</CODE></FONT>
+</TD>
+<TD><CODE><B><A HREF="#${method.name}(${method.parametersSignature})">${method.name}</A></B>(${method.parametersDocUrl})</CODE>
+<BR>
+${method.shortComment}
+</TD>
+</TR>
+<% } %>
+</TABLE>
+&nbsp;
+
+
+<!-- ============ METHOD DETAIL ========== -->
+
+<A NAME="method_detail"><!-- --></A>
+<TABLE BORDER="1" WIDTH="100%" CELLPADDING="3" CELLSPACING="0" SUMMARY="">
+<TR BGCOLOR="#CCCCFF" CLASS="TableHeadingColor">
+<TH ALIGN="left" COLSPAN="1"><FONT SIZE="+2">
+<B>Method Detail</B></FONT></TH>
+</TR>
+</TABLE>
+
+<% methods.each { method -> %>
+
+<A name="${method.name}(${method.parametersSignature})"><!-- --></A><H3>${method.name}</H3>
+<PRE>public ${method.isStatic ? 'static ' : ''}${method.returnTypeDocUrl} <B>${method.name}</B>(${method.parametersDocUrl})</PRE>
+<DL>
+<DD>${method.comment}
+<P>
+<DD><DL></dl></DD>
+
+<% if (method.parametersSignature) { %>
+<DT><B>Parameters:</B>
+	<% method.parameters.each { param -> %>
+		<DD><CODE>${param.name}</CODE> - ${param.comment}.
+<% } } %>
+
+<% if (method.returnComment) { %>
+<DT><B>Returns:</B><DD>${method.returnComment}</DL>
+<% } %>
+</DD>
+</DL>
+<HR>
+<% } %>
+<!-- ========= END OF CLASS DATA ========= -->
+
+
+</BODY>
+</HTML>
diff --git a/groovy/src/tools/org/codehaus/groovy/tools/template.overview-frame.html b/groovy/src/tools/org/codehaus/groovy/tools/template.overview-frame.html
new file mode 100644
index 0000000..dd7a8d4
--- /dev/null
+++ b/groovy/src/tools/org/codehaus/groovy/tools/template.overview-frame.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+
+<html>
+<head>
+<title>
+Overview (Groovy JDK)
+</title>
+
+<link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
+</head>
+
+<body>
+
+<table border="0" width="100%">
+<tr>
+<th align="left" nowrap>Groovy JDK</th>
+</tr>
+</table>
+
+<table border="0" width="100%">
+<TR>
+<TD NOWRAP><FONT CLASS="FrameItemFont"><A HREF="allclasses-frame.html" target="packageFrame">All Classes</A></FONT>
+
+<P>
+<FONT size="+1" CLASS="FrameHeadingFont">Packages</FONT><BR>
+
+<FONT CLASS="FrameItemFont"><a href="primitive-types/package-frame.html" target="packageFrame">Primitive types</A></FONT>
+<% packages.each { %>
+<FONT CLASS="FrameItemFont"><a href="${it.replaceAll('\\.', '/')}/package-frame.html" target="packageFrame">${it}</A></FONT>
+<BR>
+<% } %>
+</TD>
+</TR>
+</TABLE>
+
+<P>
+&nbsp;
+</BODY>
+</HTML>
diff --git a/groovy/src/tools/org/codehaus/groovy/tools/template.package-frame.html b/groovy/src/tools/org/codehaus/groovy/tools/template.package-frame.html
new file mode 100644
index 0000000..2fc57f2
--- /dev/null
+++ b/groovy/src/tools/org/codehaus/groovy/tools/template.package-frame.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<!--NewPage-->
+<HTML>
+<HEAD>
+<!-- Generated by javadoc (build 1.5.0-rc) on Wed Aug 11 07:28:10 PDT 2004 -->
+<TITLE>
+${packageName} (Java 2 Platform SE 5.0)
+</TITLE>
+
+<META NAME="keywords" CONTENT="${packageName}">
+
+<LINK REL ="stylesheet" TYPE="text/css" HREF="../../stylesheet.css" TITLE="Style">
+
+
+</HEAD>
+
+<BODY BGCOLOR="white">
+<FONT size="+1" CLASS="FrameTitleFont">${packageName}</FONT>
+
+
+<TABLE BORDER="0" WIDTH="100%" SUMMARY="">
+<TR>
+<TD NOWRAP>
+<FONT size="+1" CLASS="FrameHeadingFont">Classes</FONT>&nbsp;
+<FONT CLASS="FrameItemFont">
+<% classes.each { %>
+<BR>
+<A href="${it}.html" title="class in ${packageName}" target="classFrame">${it}</A>
+<% } %>
+</FONT></TD>
+</TR>
+</TABLE>
+
+
+</BODY>
+</HTML>
diff --git a/groovy/src/wiki-snapshot.pdf b/groovy/src/wiki-snapshot.pdf
new file mode 100644
index 0000000..a94f29b
--- /dev/null
+++ b/groovy/src/wiki-snapshot.pdf
Binary files differ
diff --git a/groovy/xdocs/images/groovy-logo.png b/groovy/xdocs/images/groovy-logo.png
new file mode 100644
index 0000000..54af4c1
--- /dev/null
+++ b/groovy/xdocs/images/groovy-logo.png
Binary files differ