You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by da...@apache.org on 2013/07/12 13:19:36 UTC

git commit: ISIS-463: new isis-core-specsupport module

Updated Branches:
  refs/heads/master 7276dc0b5 -> dd7f283ce


ISIS-463: new isis-core-specsupport module

... to decouple scoping of scenarios from step definitions.


Project: http://git-wip-us.apache.org/repos/asf/isis/repo
Commit: http://git-wip-us.apache.org/repos/asf/isis/commit/dd7f283c
Tree: http://git-wip-us.apache.org/repos/asf/isis/tree/dd7f283c
Diff: http://git-wip-us.apache.org/repos/asf/isis/diff/dd7f283c

Branch: refs/heads/master
Commit: dd7f283ce81cf3b0663b5a99e0ab87ac3aae700b
Parents: 7276dc0
Author: Dan Haywood <da...@apache.org>
Authored: Fri Jul 12 12:19:17 2013 +0100
Committer: Dan Haywood <da...@apache.org>
Committed: Fri Jul 12 12:19:17 2013 +0100

----------------------------------------------------------------------
 core/integtestsupport/pom.xml                   |   5 +-
 .../integtestsupport/IsisSystemForTest.java     |   2 +-
 .../ScenarioExecutionForIntegration.java        |  75 +++++
 ...enarioExecutionIntegrationScopeAbstract.java |  93 ------
 .../CukeStepDefsIntegrationScopeAbstract.java   |  75 -----
 core/pom.xml                                    |  29 +-
 core/specsupport/pom.xml                        |  85 +++++
 .../scenarios/DomainServiceProvider.java        |  37 +++
 .../scenarios/ScenarioExecution.java            | 287 ++++++++++++++++
 .../scenarios/ScenarioExecutionScope.java       |  51 +++
 .../specsupport/specs/CukeSpecsAbstract.java    |  38 +++
 .../specsupport/specs/CukeStepDefsAbstract.java | 148 +++++++++
 .../apache/isis/core/specsupport/specs/V.java   | 332 +++++++++++++++++++
 core/unittestsupport/pom.xml                    |  17 -
 .../scenarios/DomainServiceProvider.java        |  24 --
 .../scenarios/ScenarioExecution.java            | 215 ------------
 .../scenarios/specs/CukeSpecsAbstract.java      |  38 ---
 .../scenarios/specs/CukeStepDefsAbstract.java   |  38 ---
 .../core/unittestsupport/scenarios/specs/V.java | 332 -------------------
 19 files changed, 1074 insertions(+), 847 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/integtestsupport/pom.xml
----------------------------------------------------------------------
diff --git a/core/integtestsupport/pom.xml b/core/integtestsupport/pom.xml
index af7bdae..5e90dcd 100644
--- a/core/integtestsupport/pom.xml
+++ b/core/integtestsupport/pom.xml
@@ -67,6 +67,10 @@
             <groupId>org.apache.isis.core</groupId>
             <artifactId>isis-core-unittestsupport</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-specsupport</artifactId>
+        </dependency>
 
         <dependency>
             <groupId>org.apache.isis.core</groupId>
@@ -119,7 +123,6 @@
 			<artifactId>isis-core-runtime</artifactId>
 		</dependency>
 
-        <!-- isis testing -->
         <dependency>
             <groupId>org.apache.isis.core</groupId>
             <artifactId>isis-core-wrapper</artifactId>

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IsisSystemForTest.java
----------------------------------------------------------------------
diff --git a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IsisSystemForTest.java b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IsisSystemForTest.java
index 63b2c68..9f4f6d8 100644
--- a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IsisSystemForTest.java
+++ b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/IsisSystemForTest.java
@@ -57,7 +57,7 @@ import org.apache.isis.core.runtime.system.transaction.IsisTransaction;
 import org.apache.isis.core.runtime.system.transaction.IsisTransaction.State;
 import org.apache.isis.core.runtime.system.transaction.IsisTransactionManager;
 import org.apache.isis.core.security.authentication.AuthenticationRequestNameOnly;
-import org.apache.isis.core.unittestsupport.scenarios.DomainServiceProvider;
+import org.apache.isis.core.specsupport.scenarios.DomainServiceProvider;
 
 /**
  * Wraps a plain {@link IsisSystemDefault}, and provides a number of features to assist with testing.

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionForIntegration.java
----------------------------------------------------------------------
diff --git a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionForIntegration.java b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionForIntegration.java
new file mode 100644
index 0000000..dfc9653
--- /dev/null
+++ b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionForIntegration.java
@@ -0,0 +1,75 @@
+/**
+ *  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.apache.isis.core.integtestsupport.scenarios;
+
+import org.apache.isis.applib.fixtures.InstallableFixture;
+import org.apache.isis.core.integtestsupport.IsisSystemForTest;
+import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
+
+
+/**
+ * An extension of {@link ScenarioExecution} for use within (coarse grained)
+ * integration tests and Cucumber specs where there is back-end database.
+ *
+ * <p>
+ * To this end it provides implementations of 
+ * {@link #install(InstallableFixture...)} (to tear down/setup data)
+ * and of {@link #beginTran() begin} and {@link #endTran(boolean) end} (
+ * for transaction management.
+ */
+public class ScenarioExecutionForIntegration extends ScenarioExecution  {
+
+    protected final IsisSystemForTest isft;
+    
+    public ScenarioExecutionForIntegration(IsisSystemForTest isft) {
+        super(isft);
+        this.isft = isft;
+    }
+
+
+    // //////////////////////////////////////
+
+    /**
+     * Install arbitrary fixtures, eg before an integration tests or as part of a 
+     * Cucumber step definitions or hook.
+     */
+    public void install(InstallableFixture... fixtures) {
+        isft.installFixtures(fixtures);
+    }
+
+    // //////////////////////////////////////
+
+    /**
+     * For Cucumber hooks to call, performing transaction management around each step.
+     */
+    public void beginTran() {
+        isft.beginTran();
+    }
+
+    /**
+     * For Cucumber hooks to call, performing transaction management around each step.
+     */
+    public void endTran(boolean ok) {
+        if(ok) {
+            isft.commitTran();
+        } else {
+            isft.abortTran();
+        }
+    }
+
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionIntegrationScopeAbstract.java
----------------------------------------------------------------------
diff --git a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionIntegrationScopeAbstract.java b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionIntegrationScopeAbstract.java
deleted file mode 100644
index d511420..0000000
--- a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/ScenarioExecutionIntegrationScopeAbstract.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/**
- *  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.apache.isis.core.integtestsupport.scenarios;
-
-import org.apache.isis.applib.DomainObjectContainer;
-import org.apache.isis.applib.fixtures.InstallableFixture;
-import org.apache.isis.applib.services.wrapper.WrapperFactory;
-import org.apache.isis.core.integtestsupport.IsisSystemForTest;
-import org.apache.isis.core.unittestsupport.scenarios.ScenarioExecution;
-
-
-/**
- * An extension of {@link ScenarioExecution} for use within (coarse grained)
- * integration tests and Cucumber specs where there is back-end database.
- *
- * <p>
- * To this end it provides the ability to 
- * {@link #install(InstallableFixture...) install arbitrary fixtures} to
- * tear down/setup data, and also to methods to {@link #beginTran() begin}
- * or {@link #endTran(boolean) end} transactions. 
- */
-public abstract class ScenarioExecutionIntegrationScopeAbstract extends ScenarioExecution  {
-
-    protected final IsisSystemForTest isft;
-    
-    public ScenarioExecutionIntegrationScopeAbstract(IsisSystemForTest isft) {
-        super(isft);
-        this.isft = isft;
-    }
-
-    // //////////////////////////////////////
-
-    /**
-     * Convenience
-     */
-    public DomainObjectContainer getContainer() {
-        return service(DomainObjectContainer.class);
-    }
-
-    /**
-     * Convenience
-     */
-    public WrapperFactory getWrapperFactory() {
-        return service(WrapperFactory.class);
-    }
-    
-
-    // //////////////////////////////////////
-
-    /**
-     * Install arbitrary fixtures, eg before an integration tests or as part of a 
-     * Cucumber step definitions or hook.
-     */
-    public void install(InstallableFixture... fixtures) {
-        isft.installFixtures(fixtures);
-    }
-
-    // //////////////////////////////////////
-
-    /**
-     * For Cucumber hooks to call, performing transaction management around each step.
-     */
-    public void beginTran() {
-        isft.beginTran();
-    }
-
-    /**
-     * For Cucumber hooks to call, performing transaction management around each step.
-     */
-    public void endTran(boolean ok) {
-        if(ok) {
-            isft.commitTran();
-        } else {
-            isft.abortTran();
-        }
-    }
-
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/specs/CukeStepDefsIntegrationScopeAbstract.java
----------------------------------------------------------------------
diff --git a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/specs/CukeStepDefsIntegrationScopeAbstract.java b/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/specs/CukeStepDefsIntegrationScopeAbstract.java
deleted file mode 100644
index 42ebce2..0000000
--- a/core/integtestsupport/src/main/java/org/apache/isis/core/integtestsupport/scenarios/specs/CukeStepDefsIntegrationScopeAbstract.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- *  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.apache.isis.core.integtestsupport.scenarios.specs;
-
-import cucumber.api.java.After;
-import cucumber.api.java.Before;
-
-import org.apache.isis.core.integtestsupport.scenarios.ScenarioExecutionIntegrationScopeAbstract;
-import org.apache.isis.core.unittestsupport.scenarios.specs.CukeStepDefsAbstract;
-
-
-/**
- * Base class for integration-scope Cucumber step definitions.
- */
-public abstract class CukeStepDefsIntegrationScopeAbstract extends CukeStepDefsAbstract<ScenarioExecutionIntegrationScopeAbstract> {
-
-    public CukeStepDefsIntegrationScopeAbstract(ScenarioExecutionIntegrationScopeAbstract scenarioExecution) {
-        super(scenarioExecution);
-    }
-
-    // //////////////////////////////////////
-
-    /**
-     * Convenience method to start transaction.
-     * 
-     * <p>
-     * Cukes does not allow this to be annotated with {@link Before Cucumber's Before}
-     * annotation.  Subclasses should therefore override, annotate, and delegate back up:
-     * 
-     * <pre>
-     *  &#64;cucumber.api.java.Before
-     *  &#64;Override
-     *  public void beginTran() {
-     *     super.beginTran();
-     *  }
-     * </pre>
-     */
-    public void beginTran() {
-        scenarioExecution.beginTran();
-    }
-
-    /**
-     * Convenience method to start transaction.
-     * 
-     * <p>
-     * Cukes does not allow this to be annotated with {@link After Cucumber's After}
-     * annotation.  Subclasses should therefore override, annotate, and delegate back up:
-     * 
-     * <pre>
-     *  &#64;cucumber.api.java.After
-     *  &#64;Override
-     *  public void endTran(cucumber.api.Scenario sc) {
-     *     super.endTran(sc);
-     *  }
-     * </pre>
-     */
-    public void endTran(cucumber.api.Scenario sc) {
-        scenarioExecution.endTran(!sc.isFailed());
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index 57d51c5..e945910 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -989,7 +989,7 @@ ${license.additional-notes}
     	<!-- is also for benefit of application developers, using scope=import -->
     	<dependencies>
     	
-            <!-- isis-unittestsupport -->
+            <!-- unittestsupport -->
             <dependency>
                 <groupId>org.apache.isis.core</groupId>
                 <artifactId>isis-core-unittestsupport</artifactId>
@@ -1069,7 +1069,14 @@ ${license.additional-notes}
                 <version>1.3.0-SNAPSHOT</version>
             </dependency>
 
-            <!-- isis-integtestsupport -->
+            <!-- specsupport -->
+            <dependency>
+                <groupId>org.apache.isis.core</groupId>
+                <artifactId>isis-core-specsupport</artifactId>
+                <version>1.3.0-SNAPSHOT</version>
+            </dependency>
+
+            <!-- integtestsupport -->
             <dependency>
                 <groupId>org.apache.isis.core</groupId>
                 <artifactId>isis-core-integtestsupport</artifactId>
@@ -1407,17 +1414,6 @@ ${license.additional-notes}
                 <artifactId>cucumber-junit</artifactId>
                 <version>1.1.4-SNAPSHOT</version>
             </dependency>
-            <dependency>
-                <groupId>info.cukes</groupId>
-                <artifactId>cucumber-picocontainer</artifactId>
-                <version>1.1.4-SNAPSHOT</version>
-            </dependency>
-
-           <dependency>
-               <groupId>org.picocontainer</groupId>
-               <artifactId>picocontainer</artifactId>
-               <version>2.14.3</version>
-           </dependency>
 
             <!-- Testing libraries (scope=test) -->
             <dependency>
@@ -1464,6 +1460,12 @@ ${license.additional-notes}
                 <version>1.7.1</version>
             </dependency>
 
+           <dependency>
+               <groupId>org.picocontainer</groupId>
+               <artifactId>picocontainer</artifactId>
+               <version>2.14.3</version>
+           </dependency>
+
         </dependencies>
     </dependencyManagement>
 
@@ -1820,6 +1822,7 @@ ${license.additional-notes}
         <module>security-noop</module>
 
         <module>unittestsupport</module>
+        <module>specsupport</module>
         <module>integtestsupport</module>
 
         <module>wrapper</module>

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/specsupport/pom.xml
----------------------------------------------------------------------
diff --git a/core/specsupport/pom.xml b/core/specsupport/pom.xml
new file mode 100644
index 0000000..ea1a304
--- /dev/null
+++ b/core/specsupport/pom.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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. -->
+<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>
+
+    <parent>
+        <groupId>org.apache.isis.core</groupId>
+        <artifactId>isis</artifactId>
+        <version>1.3.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>isis-core-specsupport</artifactId>
+    <name>Isis Core Spec Support</name>
+
+    <properties>
+        <siteBaseDir>..</siteBaseDir>
+        <relativeUrl>specsupport/</relativeUrl>
+    </properties>
+
+    <!-- used in Site generation for relative references. -->
+    <url>http://isis.apache.org/${relativeUrl}</url>
+
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-project-info-reports-plugin</artifactId>
+                <version>${maven-project-info-reports-plugin}</version>
+                <inherited>false</inherited>
+                <configuration>
+                    <dependencyLocationsEnabled>false</dependencyLocationsEnabled>
+                </configuration>
+                <reportSets>
+                    <reportSet>
+                        <inherited>false</inherited>
+                        <reports>
+                            <report>dependencies</report>
+                            <report>dependency-convergence</report>
+                            <report>plugins</report>
+                            <report>summary</report>
+                        </reports>
+                    </reportSet>
+                </reportSets>
+            </plugin>
+        </plugins>
+    </reporting>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-unittestsupport</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.isis.core</groupId>
+            <artifactId>isis-core-applib</artifactId>
+        </dependency>
+
+        <dependency>
+           <groupId>junit</groupId>
+           <artifactId>junit</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>info.cukes</groupId>
+            <artifactId>cucumber-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>info.cukes</groupId>
+            <artifactId>cucumber-junit</artifactId>
+        </dependency>
+
+    </dependencies>
+
+</project>

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/DomainServiceProvider.java
----------------------------------------------------------------------
diff --git a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/DomainServiceProvider.java b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/DomainServiceProvider.java
new file mode 100644
index 0000000..e630f39
--- /dev/null
+++ b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/DomainServiceProvider.java
@@ -0,0 +1,37 @@
+/**
+ *  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.apache.isis.core.specsupport.scenarios;
+
+import org.apache.isis.applib.DomainObjectContainer;
+
+
+/**
+ * Provides access to the {@link DomainObjectContainer} and any other domain services 
+ * that may have been configured.
+ * 
+ * <p>
+ * For {@link ScenarioExecution scenario}s with integration-scope, these will be
+ * configured services for an end-to-end running system.  For scenarios with
+ * unit-scope, these will typically be mocks.
+ */
+public interface DomainServiceProvider {
+
+    public abstract DomainObjectContainer getContainer();
+
+    public abstract <T> T getService(Class<T> serviceClass);
+
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecution.java
----------------------------------------------------------------------
diff --git a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecution.java b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecution.java
new file mode 100644
index 0000000..04cc378
--- /dev/null
+++ b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecution.java
@@ -0,0 +1,287 @@
+/**
+ *  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.apache.isis.core.specsupport.scenarios;
+
+import java.util.Map;
+
+import com.google.common.collect.Maps;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.fixtures.InstallableFixture;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+
+
+/**
+ * Represents the currently executing scenario, allowing information to be shared 
+ * between Cucumber step definitions (for unit- or integration- scoped), and also for
+ * integration tests.
+ * 
+ * <p>
+ * Two types of information are available:
+ * <ul>
+ * <li>First, there are the domain services, provided using the {@link #service(Class) method}.  
+ * If running at unit-scope, then these will most likely be mocked services (and not all services
+ * will necessarily be available).  If running at integration-scope, then these will most likely
+ * be real instances, eg wired to the backend database.</li>
+ * <li>Second, there is a map of identified objects.  This is predominantly for Cucumber
+ * step definitions (either unit- or integration-scoped), such that information can be passed
+ * between steps in a decoupled fashion.
+ * </ul>
+ * 
+ * <p>
+ * When instantiated, this object binds itself to the current thread (using a {@link ThreadLocal}).  
+ * 
+ * <p>
+ * Subclasses may tailor the world for specific types of tests; for example the
+ * <tt>IntegrationScenarioExecution</tt> provides additional support for fixtures and 
+ * transaction management, used both by integration-scoped specs and by integration tests.
+ */
+public class ScenarioExecution {
+    
+    private static ThreadLocal<ScenarioExecution> current = new ThreadLocal<ScenarioExecution>();
+    
+    public static ScenarioExecution current() {
+        final ScenarioExecution execution = current.get();
+        if(execution == null) {
+            throw new IllegalStateException("Scenario has not yet been instantiated by Cukes");
+        } 
+        return execution;
+    }
+
+
+    // //////////////////////////////////////
+
+    protected final DomainServiceProvider dsp;
+    
+    public ScenarioExecution(final DomainServiceProvider dsp) {
+        this.dsp = dsp;
+        current.set(this);
+    }
+
+    /**
+     * Returns a domain service of the specified type, ensuring that
+     * it is available.
+     * 
+     * @throws IllegalStateException if not available
+     */
+    public <T> T service(Class<T> cls) {
+        final T service = dsp.getService(cls);
+        if(service == null) {
+            throw new IllegalStateException(
+                    "No service of type "
+                    + cls.getSimpleName()
+                    + " available");
+        }
+        return service;
+    }
+
+    /**
+     * Convenience method, returning the {@link DomainObjectContainer},
+     * first ensuring that it is available.
+     * 
+     * @throws IllegalStateException if not available
+     */
+    public DomainObjectContainer container() {
+        final DomainObjectContainer container = dsp.getContainer();
+        if(container == null) {
+            throw new IllegalStateException(
+                    "No DomainObjectContainer available");
+        }
+        return container;
+    }
+
+    /**
+     * Convenience method, returning the {@link WrapperFactory} domain service,
+     * first ensuring that it is available.
+     * 
+     * @throws IllegalStateException if not available
+     */
+    public WrapperFactory wrapperFactory() {
+        return service(WrapperFactory.class);
+    }
+
+
+    // //////////////////////////////////////
+
+    /**
+     * Key for objects stored by steps in the scenario.
+     * 
+     * <p>
+     * Objects can be identified in a variety of manners:
+     * <ul>
+     * <li>a fully qualified object provides both its type and a (unique) id; for example 'lease OXF-TOPMODEL-001'</li>
+     * <li>a named object provides only its id; for example 'OXF-TOPMODEL-001'</li>
+     * <li>a typed object provides only its type; for example 'the lease'.</li>
+     * </ul>
+     * 
+     * <p>
+     * Because of the second rule, the id should be unique in and of itself.
+     * 
+     * <p>
+     * The expectation is that scenarios will use the first form (fully qualified) the first time that an
+     * object is introduced within a scenario.  Thereafter either of the other forms may be used.
+     * In the case of a typed object (eg "the lease"), the most recently "touched" object of that type
+     * is returned.
+     */
+    public static class VariableId {
+        private final String type;
+        private final String id;
+        public VariableId(String type, String id) {
+            this.type = type;
+            this.id = id;
+        }
+
+        /**
+         * eg 'lease'
+         */
+        public String getType() {
+            return type;
+        }
+        /**
+         * eg 'OXF-TOPMODEL-001'
+         */
+        public String getId() {
+            return id;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((id == null) ? 0 : id.hashCode());
+            result = prime * result + ((type == null) ? 0 : type.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            VariableId other = (VariableId) obj;
+            if (id == null) {
+                if (other.id != null)
+                    return false;
+            } else if (!id.equals(other.id))
+                return false;
+            if (type == null) {
+                if (other.type != null)
+                    return false;
+            } else if (!type.equals(other.type))
+                return false;
+            return true;
+        }
+
+        @Override
+        public String toString() {
+            return "VariableId [type=" + type + ", id=" + id + "]";
+        }
+    }
+
+    private final Map<VariableId, Object> objectByVariableId = Maps.newLinkedHashMap();
+    private final Map<String, Object> objectsById = Maps.newLinkedHashMap();
+    
+    private final Map<String, Object> mostRecent = Maps.newHashMap();
+
+    public void put(String type, String id, Object value) {
+        objectByVariableId.put(new VariableId(type, id), value);
+        mostRecent.put(type, value);
+    }
+
+    /**
+     * Retrieve an object previously used in the scenario.
+     * 
+     * <p>
+     * Must specify type and/or id.
+     * 
+     * @see VariableId - for rules on what constitutes an identifier.
+     */
+    public Object get(String type, String id) {
+        if(type != null && id != null) {
+            final VariableId variableId = new VariableId(type,id);
+            final Object value = objectByVariableId.get(variableId);
+            if(value != null) {
+                mostRecent.put(type, value);
+                return value;
+            } 
+            throw new IllegalStateException("No such " + variableId);
+        }
+        if(type != null && id == null) {
+            return mostRecent.get(type);
+        }
+        if(type == null && id != null) {
+            final Object value = objectsById.get(id);
+            if(value != null) {
+                mostRecent.put(type, value);
+            } 
+            return value;
+        }
+        throw new IllegalArgumentException("Must specify type and/or id");
+    }
+
+    /**
+     * As {@link #get(String, String)}, but downcasting to the provided class.
+     */
+    @SuppressWarnings("unchecked")
+    public <X> X get(String type, String id, Class<X> cls) {
+        return (X) get(type, id);
+    }
+
+    // //////////////////////////////////////
+
+    /**
+     * Install arbitrary fixtures, eg before an integration tests or as part of a 
+     * Cucumber step definitions or hook.
+     * 
+     * <p>
+     * This implementation has a no-op, but subclasses of this class tailored to
+     * supporting integration specs/tests are expected to override.
+     */
+    public void install(InstallableFixture... fixtures) {
+        // do nothing
+    }
+
+    // //////////////////////////////////////
+
+    /**
+     * For Cucumber hooks to call, performing transaction management around each step.
+     * 
+     * <p>
+     * This implementation has a no-op, but subclasses of this class tailored to
+     * supporting integration specs are expected to override.  (Integration tests can use
+     * the <tt>IsisTransactionRule</tt> to do transaction management transparently).
+     */
+    public void beginTran() {
+        // do nothing
+    }
+
+    /**
+     * For Cucumber hooks to call, performing transaction management around each step.
+     * 
+     * <p>
+     * This implementation has a no-op, but subclasses of this class tailored to
+     * supporting integration specs are expected to override.  (Integration tests can use
+     * the <tt>IsisTransactionRule</tt> to do transaction management transparently).
+     */
+    public void endTran(boolean ok) {
+        // do nothing
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecutionScope.java
----------------------------------------------------------------------
diff --git a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecutionScope.java b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecutionScope.java
new file mode 100644
index 0000000..3c8f487
--- /dev/null
+++ b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/scenarios/ScenarioExecutionScope.java
@@ -0,0 +1,51 @@
+/**
+ *  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.apache.isis.core.specsupport.scenarios;
+
+
+/**
+ * The scope at which the specification will run; acts as a factory to create
+ * an instance of the appropriate subclass of {@link ScenarioExecution}.
+ */
+public class ScenarioExecutionScope {
+    
+    public final static ScenarioExecutionScope UNIT = new ScenarioExecutionScope(ScenarioExecution.class);
+    
+    private final Class<? extends ScenarioExecution> scenarioExecutionClass;
+
+    public ScenarioExecutionScope(Class<? extends ScenarioExecution> scenarioExecutionClass) {
+        this.scenarioExecutionClass = scenarioExecutionClass;
+    }
+    
+    public ScenarioExecution instantiate() {
+        try {
+            return scenarioExecutionClass.newInstance();
+        } catch (InstantiationException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /* (non-Javadoc)
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return scenarioExecutionClass.getName();
+    }
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeSpecsAbstract.java
----------------------------------------------------------------------
diff --git a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeSpecsAbstract.java b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeSpecsAbstract.java
new file mode 100644
index 0000000..7d6295d
--- /dev/null
+++ b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeSpecsAbstract.java
@@ -0,0 +1,38 @@
+/**
+ *  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.apache.isis.core.specsupport.specs;
+
+import cucumber.api.junit.Cucumber;
+
+import org.junit.runner.RunWith;
+
+/**
+ * Base class for all Cucumber specs run at unit-scope; runs the spec as a JUnit test.
+ */
+@RunWith(Cucumber.class)
+@Cucumber.Options(
+        format = {
+                "html:target/cucumber-html-report"
+                // addHook causes an exception to be thrown if this reporter is registered...
+                // ,"json-pretty:target/cucumber-json-report.json"
+        },
+        strict = true,
+        tags = { "~@backlog" })
+public abstract class CukeSpecsAbstract {
+
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeStepDefsAbstract.java
----------------------------------------------------------------------
diff --git a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeStepDefsAbstract.java b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeStepDefsAbstract.java
new file mode 100644
index 0000000..45de214
--- /dev/null
+++ b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/CukeStepDefsAbstract.java
@@ -0,0 +1,148 @@
+/**
+ *  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.apache.isis.core.specsupport.specs;
+
+import cucumber.api.java.Before;
+
+import org.apache.isis.applib.DomainObjectContainer;
+import org.apache.isis.applib.services.wrapper.WrapperFactory;
+import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
+import org.apache.isis.core.specsupport.scenarios.ScenarioExecutionScope;
+
+/**
+ * Base class for unit-scope Cucumber step definitions.
+ * 
+ * <p>
+ * Simply declares that an instance of {@link ScenarioExecution} (or a subclass)
+ * must be instantiated by the Cucumber-JVM runtime and injected into the step definitions.
+ */
+public abstract class CukeStepDefsAbstract {
+
+    private ScenarioExecution scenarioExecution;
+    
+    /**
+     * Access the {@link ScenarioExecution} as setup through a previous call to {@link #before(ScenarioExecutionScope)}.
+     * 
+     * <p>
+     * This corresponds, broadly, to the (Ruby) Cucumber's &quot;World&quot; object.
+     */
+    protected ScenarioExecution scenarioExecution() {
+        if(scenarioExecution == null) {
+            throw new IllegalStateException("The scenario execution has not been set up; call #before(ScenarioExecutionScope) first");
+        }
+        return scenarioExecution;
+    }
+    
+    /**
+     * Convenience
+     */
+    protected <T> T service(Class<T> cls) {
+        return scenarioExecution().service(cls);
+    }
+    
+    /**
+     * Convenience
+     */
+    protected DomainObjectContainer container() {
+        return scenarioExecution().container();
+    }
+    
+    /**
+     * Convenience
+     */
+    protected WrapperFactory wrapperFactory() {
+        return scenarioExecution().wrapperFactory()
+    }
+    
+    // //////////////////////////////////////
+
+    /**
+     * Indicate that a scenario is starting, and specify the {@link ScenarioExecutionScope scope} 
+     * at which to run the scenario.
+     * 
+     * <p>
+     * This method should be called from a &quot;before&quot; hook (a method annotated with
+     * Cucumber's {@link Before} annotation, in a step definition subclass.  The tag
+     * should be appropriate for the scope specified.  Typically this method should be delegated to 
+     * twice, in two mutually exclusive before hooks.
+     * 
+     * <p>
+     * Calling this method makes the {@link ScenarioExecution} available (via {@link #scenarioExecution()}).
+     * It also delegates to the scenario to {@link ScenarioExecution#beginTran() begin the transaction}.  
+     * (Whether this actually does anything depends in implementation of the {@link ScenarioExecution}). 
+     * 
+     * <p>
+     * The boilerplate (to copy-n-paste as required) is:
+     * <pre>
+     *  &#64;cucumber.api.java.Before("@unit")
+     *  public void beforeScenarioUnitScope() {
+     *     before(ScenarioExecutionScope.UNIT);
+     *  }
+     *  &#64;cucumber.api.java.Before("@integration")
+     *  public void beforeScenarioIntegrationScope() {
+     *     before(new ScenarioExecutionScope(ScenarioExecutionForMyAppIntegration.class));
+     *  }
+     * </pre>
+     * where <tt>ScenarioExecutionForMyAppIntegration</tt> is an application-specific subclass of
+     * {@link ScenarioExecution} for integration-testing.  Typically this is done using the 
+     * <tt>IsisSystemForTest</tt> class provided in the <tt>isis-core-integtestsupport</tt> module).
+     * 
+     * <p>
+     * Not every class holding step definitions should have these hooks, only those that correspond to the logical
+     * beginning and end of scenario.  As such, this method may only be called once per scenario execution
+     * (and fails fast if called more than once).
+     */
+    protected void before(ScenarioExecutionScope scope) {
+        if(scenarioExecution != null) {
+            throw new IllegalStateException("Scenario execution scope has already been set");
+        }
+        scenarioExecution = scope.instantiate();
+        scenarioExecution.beginTran();
+    }
+
+    /**
+     * Indicate that a scenario is ending; the {@link ScenarioExecution} is discarded and no
+     * longer {@link #scenarioExecution() available}.
+     * 
+     * <p>
+     * Before being discarded, the {@link ScenarioExecution} is delegated to
+     * in order to {@link ScenarioExecution#endTran(boolean) end the transaction}.
+     * (Whether this actually does anything depends in implementation of the {@link ScenarioExecution}).
+     *  
+     * <p>
+     * The boilerplate (to copy-n-paste as required) is:
+     * <pre>
+     *  &#64;cucumber.api.java.After
+     *  public void afterScenario(cucumber.api.Scenario sc) {
+     *     after(sc);
+     *  }
+     * </pre>
+     * 
+     * <p>
+     * Not every class holding step definitions should have this hook, only those that correspond to the logical
+     * beginning and end of scenario.  As such, this method may only be called once per scenario execution
+     * (and fails fast if called more than once).
+     */
+    public void after(cucumber.api.Scenario sc) {
+        if(scenarioExecution == null) {
+            throw new IllegalStateException("Scenario execution is not set");
+        }
+        scenarioExecution.endTran(!sc.isFailed());
+        scenarioExecution = null;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/V.java
----------------------------------------------------------------------
diff --git a/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/V.java b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/V.java
new file mode 100644
index 0000000..a67ce51
--- /dev/null
+++ b/core/specsupport/src/main/java/org/apache/isis/core/specsupport/specs/V.java
@@ -0,0 +1,332 @@
+/**
+ *  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.apache.isis.core.specsupport.specs;
+
+import cucumber.api.Transformer;
+
+import org.joda.time.format.DateTimeFormat;
+
+/**
+ * A set of converters for built-in value types; for use in Cucumber step definitions.
+ */
+public class V {
+    
+    private V() {
+    }
+
+    /**
+     * Converts {@link java.lang.String}s to {@link java.lang.Byte}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class Byte extends Transformer<java.lang.Byte> {
+        
+        @Override
+        public java.lang.Byte transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : java.lang.Byte.parseByte(value);
+        }
+        
+        public static java.lang.Byte as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new Byte().transform((java.lang.String) value)
+                            : null;
+        }
+    }
+
+    /**
+     * Converts {@link java.lang.String}s to {@link java.lang.Short}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class Short extends Transformer<java.lang.Short> {
+        
+        @Override
+        public java.lang.Short transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                            : java.lang.Short.parseShort(value);
+        }
+        
+        public static java.lang.Short as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new Short().transform((java.lang.String) value)
+                            : null;
+        }
+    }
+    
+    /**
+     * Converts {@link java.lang.String}s to {@link java.lang.Integer}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class Integer extends Transformer<java.lang.Integer> {
+
+        @Override
+        public java.lang.Integer transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : java.lang.Integer.parseInt(value);
+        }
+
+        public static java.lang.Integer as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new Integer().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+
+    /**
+     * Converts {@link java.lang.String}s to {@link java.lang.Long}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class Long extends Transformer<java.lang.Long> {
+        
+        @Override
+        public java.lang.Long transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : java.lang.Long.parseLong(value);
+        }
+        
+        public static java.lang.Long as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new Long().transform((java.lang.String) value)
+                            : null;
+        }
+    }
+
+    /**
+     * Converts {@link java.lang.String}s to {@link java.lang.Float}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class Float extends Transformer<java.lang.Float> {
+        
+        @Override
+        public java.lang.Float transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : java.lang.Float.parseFloat(value);
+        }
+        
+        public static java.lang.Float as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new Float().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+    
+    /**
+     * Converts {@link java.lang.String}s to {@link java.lang.Double}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class Double extends Transformer<java.lang.Double> {
+        
+        @Override
+        public java.lang.Double transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : java.lang.Double.parseDouble(value);
+        }
+        
+        public static java.lang.Double as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new Double().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+
+    /**
+     * Converts {@link java.lang.String}s to {@link java.lang.BigInteger}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class BigInteger extends Transformer<java.math.BigInteger> {
+        
+        @Override
+        public java.math.BigInteger transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : new java.math.BigInteger(value);
+        }
+        
+        public static java.math.BigInteger as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new BigInteger().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+    
+    /**
+     * Converts {@link java.lang.String}s to {@link java.lang.BigDecimal}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class BigDecimal extends Transformer<java.math.BigDecimal> {
+        
+        @Override
+        public java.math.BigDecimal transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : new java.math.BigDecimal(value);
+        }
+        
+        public static java.math.BigDecimal as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new BigDecimal().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+    
+    /**
+     * Converts {@link java.lang.String}s to {@link java.lang.Character}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class Character extends Transformer<java.lang.Character> {
+        
+        @Override
+        public java.lang.Character transform(java.lang.String value) {
+            return value == null || "null".equals(value) || value.length() <1
+                    ? null
+                    : value.charAt(0);
+        }
+        
+        public static java.lang.Character as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new Character().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+    
+    /**
+     * Converts {@link java.lang.String}s to {@link java.lang.String}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class String extends Transformer<java.lang.String> {
+        
+        @Override
+        public java.lang.String transform(java.lang.String value) {
+            return value == null || "null".equals(value) 
+                    ? null
+                    : value;
+        }
+        
+        public static java.lang.String as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new String().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+    
+    /**
+     * Converts {@link java.lang.String}s to {@link org.joda.time.LocalDate}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class LyyyyMMdd extends Transformer<org.joda.time.LocalDate> {
+
+        @Override
+        public org.joda.time.LocalDate transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : DateTimeFormat.forPattern("yyyy-MM-dd").parseLocalDate(value);
+        }
+
+        public static org.joda.time.LocalDate as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new LyyyyMMdd().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+    
+    /**
+     * Converts {@link java.lang.String}s to {@link org.joda.time.DateTime}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class yyyyMMddHHmmss extends Transformer<org.joda.time.DateTime> {
+        
+        @Override
+        public org.joda.time.DateTime transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").parseDateTime(value);
+        }
+        
+        public static org.joda.time.DateTime as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new yyyyMMddHHmmss().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+    
+    /**
+     * Converts {@link java.lang.String}s to {@link org.joda.time.DateTime}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class yyyyMMddHHmm extends Transformer<org.joda.time.DateTime> {
+        
+        @Override
+        public org.joda.time.DateTime transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : DateTimeFormat.forPattern("yyyy-MM-dd HH:mm").parseDateTime(value);
+        }
+        
+        public static org.joda.time.DateTime as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new yyyyMMddHHmmss().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+    
+    /**
+     * Converts {@link java.lang.String}s to {@link org.joda.time.LocalDateTime}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class LyyyyMMddHHmm extends Transformer<org.joda.time.LocalDateTime> {
+        
+        @Override
+        public org.joda.time.LocalDateTime transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : DateTimeFormat.forPattern("yyyy-MM-dd HH:mm").parseLocalDateTime(value);
+        }
+        
+        public static org.joda.time.DateTime as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new yyyyMMddHHmmss().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+    
+    /**
+     * Converts {@link java.lang.String}s to {@link org.joda.time.LocalDateTime}, but also recognizing the
+     * keyword 'null'.
+     */
+    public static class LyyyyMMddHHmmss extends Transformer<org.joda.time.LocalDateTime> {
+        
+        @Override
+        public org.joda.time.LocalDateTime transform(java.lang.String value) {
+            return value == null || "null".equals(value)
+                    ? null
+                    : DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").parseLocalDateTime(value);
+        }
+        
+        public static org.joda.time.DateTime as(Object value) {
+            return value != null && value instanceof java.lang.String
+                    ? new yyyyMMddHHmmss().transform((java.lang.String) value)
+                    : null;
+        }
+    }
+    
+    
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/unittestsupport/pom.xml
----------------------------------------------------------------------
diff --git a/core/unittestsupport/pom.xml b/core/unittestsupport/pom.xml
index 893b3a1..b329cb4 100644
--- a/core/unittestsupport/pom.xml
+++ b/core/unittestsupport/pom.xml
@@ -94,23 +94,6 @@
            </dependency>
 
             <dependency>
-                <groupId>joda-time</groupId>
-                <artifactId>joda-time</artifactId>
-            </dependency>
-            <dependency>
-                <groupId>info.cukes</groupId>
-                <artifactId>cucumber-java</artifactId>
-            </dependency>
-            <dependency>
-                <groupId>info.cukes</groupId>
-                <artifactId>cucumber-picocontainer</artifactId>
-            </dependency>
-            <dependency>
-                <groupId>info.cukes</groupId>
-                <artifactId>cucumber-junit</artifactId>
-            </dependency>
-
-            <dependency>
                 <groupId>javax.jdo</groupId>
                 <artifactId>jdo-api</artifactId>
                 <version>3.0.1</version>

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/DomainServiceProvider.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/DomainServiceProvider.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/DomainServiceProvider.java
deleted file mode 100644
index cbfaa72..0000000
--- a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/DomainServiceProvider.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- *  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.apache.isis.core.unittestsupport.scenarios;
-
-
-public interface DomainServiceProvider {
-
-    public abstract <T> T getService(Class<T> serviceClass);
-
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/ScenarioExecution.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/ScenarioExecution.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/ScenarioExecution.java
deleted file mode 100644
index 004d52f..0000000
--- a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/ScenarioExecution.java
+++ /dev/null
@@ -1,215 +0,0 @@
-/**
- *  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.apache.isis.core.unittestsupport.scenarios;
-
-import java.util.Map;
-
-import com.google.common.collect.Maps;
-
-
-/**
- * Represents the currently executing scenario, allowing information to be shared 
- * between Cucumber step definitions (for unit- or integration- scoped), and also for
- * integration tests.
- * 
- * <p>
- * Two types of information are available:
- * <ul>
- * <li>First, there are the domain services, provided using the {@link #service(Class) method}.  
- * If running at unit-scope, then these will most likely be mocked services (and not all services
- * will necessarily be available).  If running at integration-scope, then these will most likely
- * be real instances, eg wired to the backend database.</li>
- * <li>Second, there is a map of identified objects.  This is predominantly for Cucumber
- * step definitions (either unit- or integration-scoped), such that information can be passed
- * between steps in a decoupled fashion.
- * </ul>
- * 
- * <p>
- * When instantiated, this object binds itself to the current thread (using a {@link ThreadLocal}).  
- * 
- * <p>
- * Subclasses may tailor the world for specific types of tests; for example the
- * <tt>IntegrationScenarioExecution</tt> provides additional support for fixtures and 
- * transaction management, used both by integration-scoped specs and by integration tests.
- */
-public class ScenarioExecution {
-    
-    private static ThreadLocal<ScenarioExecution> current = new ThreadLocal<ScenarioExecution>();
-    
-    public static ScenarioExecution current() {
-        final ScenarioExecution world = current.get();
-        if(world == null) {
-            throw new IllegalStateException("Scenario has not yet been instantiated by Cukes");
-        } 
-        return world;
-    }
-
-
-    // //////////////////////////////////////
-
-    protected final DomainServiceProvider dsp;
-    
-    /**
-     * For instantiation by Cucumber-JVM only.
-     */
-    public ScenarioExecution(final DomainServiceProvider dsp) {
-        this.dsp = dsp;
-        current.set(this);
-    }
-
-    public <T> T service(Class<T> cls) {
-        final T service = dsp.getService(cls);
-        if(service == null) {
-            throw new IllegalStateException(
-                    "No service of type "
-                    + cls.getSimpleName()
-                    + " available");
-        }
-        return service;
-    }
-
-    
-    // //////////////////////////////////////
-
-    /**
-     * Key for objects stored by steps in the scenario.
-     * 
-     * <p>
-     * Objects can be identified in a variety of manners:
-     * <ul>
-     * <li>a fully qualified object provides both its type and a (unique) id; for example 'lease OXF-TOPMODEL-001'</li>
-     * <li>a named object provides only its id; for example 'OXF-TOPMODEL-001'</li>
-     * <li>a typed object provides only its type; for example 'the lease'.</li>
-     * </ul>
-     * 
-     * <p>
-     * Because of the second rule, the id should be unique in and of itself.
-     * 
-     * <p>
-     * The expectation is that scenarios will use the first form (fully qualified) the first time that an
-     * object is introduced within a scenario.  Thereafter either of the other forms may be used.
-     * In the case of a typed object (eg "the lease"), the most recently "touched" object of that type
-     * is returned.
-     */
-    public static class VariableId {
-        private final String type;
-        private final String id;
-        public VariableId(String type, String id) {
-            this.type = type;
-            this.id = id;
-        }
-
-        /**
-         * eg 'lease'
-         */
-        public String getType() {
-            return type;
-        }
-        /**
-         * eg 'OXF-TOPMODEL-001'
-         */
-        public String getId() {
-            return id;
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + ((id == null) ? 0 : id.hashCode());
-            result = prime * result + ((type == null) ? 0 : type.hashCode());
-            return result;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
-                return true;
-            if (obj == null)
-                return false;
-            if (getClass() != obj.getClass())
-                return false;
-            VariableId other = (VariableId) obj;
-            if (id == null) {
-                if (other.id != null)
-                    return false;
-            } else if (!id.equals(other.id))
-                return false;
-            if (type == null) {
-                if (other.type != null)
-                    return false;
-            } else if (!type.equals(other.type))
-                return false;
-            return true;
-        }
-
-        @Override
-        public String toString() {
-            return "VariableId [type=" + type + ", id=" + id + "]";
-        }
-    }
-
-    private final Map<VariableId, Object> objectByVariableId = Maps.newLinkedHashMap();
-    private final Map<String, Object> objectsById = Maps.newLinkedHashMap();
-    
-    private final Map<String, Object> mostRecent = Maps.newHashMap();
-
-    public void put(String type, String id, Object value) {
-        objectByVariableId.put(new VariableId(type, id), value);
-        mostRecent.put(type, value);
-    }
-
-    /**
-     * Retrieve an object previously used in the scenario.
-     * 
-     * <p>
-     * Must specify type and/or id.
-     * 
-     * @see VariableId - for rules on what constitutes an identifier.
-     */
-    public Object get(String type, String id) {
-        if(type != null && id != null) {
-            final VariableId variableId = new VariableId(type,id);
-            final Object value = objectByVariableId.get(variableId);
-            if(value != null) {
-                mostRecent.put(type, value);
-                return value;
-            } 
-            throw new IllegalStateException("No such " + variableId);
-        }
-        if(type != null && id == null) {
-            return mostRecent.get(type);
-        }
-        if(type == null && id != null) {
-            final Object value = objectsById.get(id);
-            if(value != null) {
-                mostRecent.put(type, value);
-            } 
-            return value;
-        }
-        throw new IllegalArgumentException("Must specify type and/or id");
-    }
-
-    /**
-     * As {@link #get(String, String)}, but downcasting to the provided class.
-     */
-    @SuppressWarnings("unchecked")
-    public <X> X get(String type, String id, Class<X> cls) {
-        return (X) get(type, id);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/CukeSpecsAbstract.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/CukeSpecsAbstract.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/CukeSpecsAbstract.java
deleted file mode 100644
index 57b4dcd..0000000
--- a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/CukeSpecsAbstract.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- *  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.apache.isis.core.unittestsupport.scenarios.specs;
-
-import cucumber.api.junit.Cucumber;
-
-import org.junit.runner.RunWith;
-
-/**
- * Base class for all Cucumber specs run at unit-scope; runs the spec as a JUnit test.
- */
-@RunWith(Cucumber.class)
-@Cucumber.Options(
-        format = {
-                "html:target/cucumber-html-report"
-                // addHook causes an exception to be thrown if this reporter is registered...
-                // ,"json-pretty:target/cucumber-json-report.json"
-        },
-        strict = true,
-        tags = { "~@backlog" })
-public abstract class CukeSpecsAbstract {
-
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/CukeStepDefsAbstract.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/CukeStepDefsAbstract.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/CukeStepDefsAbstract.java
deleted file mode 100644
index 0a0693e..0000000
--- a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/CukeStepDefsAbstract.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/**
- *  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.apache.isis.core.unittestsupport.scenarios.specs;
-
-import org.apache.isis.core.unittestsupport.scenarios.ScenarioExecution;
-
-/**
- * Base class for unit-scope Cucumber step definitions.
- * 
- * <p>
- * Simply declares that an instance of (a concrete subclass of) 
- * {@link ScenarioExecution} must be instantiated by the Cucumber-JVM
- * runtime and injected into the step definitions.
- */
-public abstract class CukeStepDefsAbstract<T extends ScenarioExecution> {
-
-    protected final T scenarioExecution;
-    
-    public CukeStepDefsAbstract(T scenarioExecution) {
-        this.scenarioExecution = scenarioExecution;
-    }
-
-
-}

http://git-wip-us.apache.org/repos/asf/isis/blob/dd7f283c/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/V.java
----------------------------------------------------------------------
diff --git a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/V.java b/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/V.java
deleted file mode 100644
index a4691f8..0000000
--- a/core/unittestsupport/src/main/java/org/apache/isis/core/unittestsupport/scenarios/specs/V.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/**
- *  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.apache.isis.core.unittestsupport.scenarios.specs;
-
-import cucumber.api.Transformer;
-
-import org.joda.time.format.DateTimeFormat;
-
-/**
- * A set of converters for built-in value types; for use in Cucumber step definitions.
- */
-public class V {
-    
-    private V() {
-    }
-
-    /**
-     * Converts {@link java.lang.String}s to {@link java.lang.Byte}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class Byte extends Transformer<java.lang.Byte> {
-        
-        @Override
-        public java.lang.Byte transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : java.lang.Byte.parseByte(value);
-        }
-        
-        public static java.lang.Byte as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new Byte().transform((java.lang.String) value)
-                            : null;
-        }
-    }
-
-    /**
-     * Converts {@link java.lang.String}s to {@link java.lang.Short}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class Short extends Transformer<java.lang.Short> {
-        
-        @Override
-        public java.lang.Short transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                            : java.lang.Short.parseShort(value);
-        }
-        
-        public static java.lang.Short as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new Short().transform((java.lang.String) value)
-                            : null;
-        }
-    }
-    
-    /**
-     * Converts {@link java.lang.String}s to {@link java.lang.Integer}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class Integer extends Transformer<java.lang.Integer> {
-
-        @Override
-        public java.lang.Integer transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : java.lang.Integer.parseInt(value);
-        }
-
-        public static java.lang.Integer as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new Integer().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-
-    /**
-     * Converts {@link java.lang.String}s to {@link java.lang.Long}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class Long extends Transformer<java.lang.Long> {
-        
-        @Override
-        public java.lang.Long transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : java.lang.Long.parseLong(value);
-        }
-        
-        public static java.lang.Long as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new Long().transform((java.lang.String) value)
-                            : null;
-        }
-    }
-
-    /**
-     * Converts {@link java.lang.String}s to {@link java.lang.Float}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class Float extends Transformer<java.lang.Float> {
-        
-        @Override
-        public java.lang.Float transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : java.lang.Float.parseFloat(value);
-        }
-        
-        public static java.lang.Float as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new Float().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-    
-    /**
-     * Converts {@link java.lang.String}s to {@link java.lang.Double}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class Double extends Transformer<java.lang.Double> {
-        
-        @Override
-        public java.lang.Double transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : java.lang.Double.parseDouble(value);
-        }
-        
-        public static java.lang.Double as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new Double().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-
-    /**
-     * Converts {@link java.lang.String}s to {@link java.lang.BigInteger}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class BigInteger extends Transformer<java.math.BigInteger> {
-        
-        @Override
-        public java.math.BigInteger transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : new java.math.BigInteger(value);
-        }
-        
-        public static java.math.BigInteger as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new BigInteger().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-    
-    /**
-     * Converts {@link java.lang.String}s to {@link java.lang.BigDecimal}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class BigDecimal extends Transformer<java.math.BigDecimal> {
-        
-        @Override
-        public java.math.BigDecimal transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : new java.math.BigDecimal(value);
-        }
-        
-        public static java.math.BigDecimal as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new BigDecimal().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-    
-    /**
-     * Converts {@link java.lang.String}s to {@link java.lang.Character}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class Character extends Transformer<java.lang.Character> {
-        
-        @Override
-        public java.lang.Character transform(java.lang.String value) {
-            return value == null || "null".equals(value) || value.length() <1
-                    ? null
-                    : value.charAt(0);
-        }
-        
-        public static java.lang.Character as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new Character().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-    
-    /**
-     * Converts {@link java.lang.String}s to {@link java.lang.String}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class String extends Transformer<java.lang.String> {
-        
-        @Override
-        public java.lang.String transform(java.lang.String value) {
-            return value == null || "null".equals(value) 
-                    ? null
-                    : value;
-        }
-        
-        public static java.lang.String as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new String().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-    
-    /**
-     * Converts {@link java.lang.String}s to {@link org.joda.time.LocalDate}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class LyyyyMMdd extends Transformer<org.joda.time.LocalDate> {
-
-        @Override
-        public org.joda.time.LocalDate transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : DateTimeFormat.forPattern("yyyy-MM-dd").parseLocalDate(value);
-        }
-
-        public static org.joda.time.LocalDate as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new LyyyyMMdd().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-    
-    /**
-     * Converts {@link java.lang.String}s to {@link org.joda.time.DateTime}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class yyyyMMddHHmmss extends Transformer<org.joda.time.DateTime> {
-        
-        @Override
-        public org.joda.time.DateTime transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").parseDateTime(value);
-        }
-        
-        public static org.joda.time.DateTime as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new yyyyMMddHHmmss().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-    
-    /**
-     * Converts {@link java.lang.String}s to {@link org.joda.time.DateTime}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class yyyyMMddHHmm extends Transformer<org.joda.time.DateTime> {
-        
-        @Override
-        public org.joda.time.DateTime transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : DateTimeFormat.forPattern("yyyy-MM-dd HH:mm").parseDateTime(value);
-        }
-        
-        public static org.joda.time.DateTime as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new yyyyMMddHHmmss().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-    
-    /**
-     * Converts {@link java.lang.String}s to {@link org.joda.time.LocalDateTime}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class LyyyyMMddHHmm extends Transformer<org.joda.time.LocalDateTime> {
-        
-        @Override
-        public org.joda.time.LocalDateTime transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : DateTimeFormat.forPattern("yyyy-MM-dd HH:mm").parseLocalDateTime(value);
-        }
-        
-        public static org.joda.time.DateTime as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new yyyyMMddHHmmss().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-    
-    /**
-     * Converts {@link java.lang.String}s to {@link org.joda.time.LocalDateTime}, but also recognizing the
-     * keyword 'null'.
-     */
-    public static class LyyyyMMddHHmmss extends Transformer<org.joda.time.LocalDateTime> {
-        
-        @Override
-        public org.joda.time.LocalDateTime transform(java.lang.String value) {
-            return value == null || "null".equals(value)
-                    ? null
-                    : DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss").parseLocalDateTime(value);
-        }
-        
-        public static org.joda.time.DateTime as(Object value) {
-            return value != null && value instanceof java.lang.String
-                    ? new yyyyMMddHHmmss().transform((java.lang.String) value)
-                    : null;
-        }
-    }
-    
-    
-}
\ No newline at end of file