You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by th...@apache.org on 2014/12/21 20:56:57 UTC

[31/35] tapestry-5 git commit: First final version of the BeanModel and Commons packages.

First final version of the BeanModel and Commons packages.


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/97bd4d5e
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/97bd4d5e
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/97bd4d5e

Branch: refs/heads/master
Commit: 97bd4d5eff6af1a4f970b81246c8689c3a25687a
Parents: 3120870
Author: Thiago H. de Paula Figueiredo <th...@apache.org>
Authored: Sun Dec 7 00:22:27 2014 -0200
Committer: Thiago H. de Paula Figueiredo <th...@apache.org>
Committed: Sun Dec 7 00:22:27 2014 -0200

----------------------------------------------------------------------
 beanmodel/build.gradle                          |   3 +
 .../beaneditor/BeanModelSourceBuilder.java      | 150 +++-
 .../services/DefaultDataTypeAnalyzer.java       |  61 ++
 .../ioc/internal/BasicDataTypeAnalyzers.java    |  45 +-
 .../tapestry5/ioc/util/StrategyRegistry.java    | 172 +++++
 .../services/DefaultDataTypeAnalyzer.java       |  61 --
 .../AbstractBeanModelSourceImplTest.java        | 757 +++++++++++++++++++
 .../services/BeanModelSourceBuilderTest.java    |  33 +
 .../services/BeanModelSourceImplTest.java       | 736 +-----------------
 .../tapestry5/ioc/util/StrategyRegistry.java    | 172 -----
 10 files changed, 1182 insertions(+), 1008 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/97bd4d5e/beanmodel/build.gradle
----------------------------------------------------------------------
diff --git a/beanmodel/build.gradle b/beanmodel/build.gradle
index fba43b7..9ff0fb3 100644
--- a/beanmodel/build.gradle
+++ b/beanmodel/build.gradle
@@ -35,6 +35,9 @@ dependencies {
 	compile "org.antlr:antlr-runtime:3.5.2", {
 		exclude group: "org.antlr", module: "stringtemplate"
 	}
+    
+    testCompile "org.testng:testng:${versions.testng}", { transitive = false }
+    
 }
 
 // This may spin out as a plugin once we've got the details down pat

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/97bd4d5e/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModelSourceBuilder.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModelSourceBuilder.java b/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModelSourceBuilder.java
index 4ac3373..58537b5 100644
--- a/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModelSourceBuilder.java
+++ b/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModelSourceBuilder.java
@@ -13,32 +13,26 @@
 // limitations under the License.
 package org.apache.tapestry5.beaneditor;
 
+import java.lang.annotation.Annotation;
 import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Set;
-
-import javax.naming.OperationNotSupportedException;
-import javax.swing.JFrame;
 
 import org.apache.tapestry5.internal.services.BeanModelSourceImpl;
 import org.apache.tapestry5.internal.services.PropertyConduitSourceImpl;
 import org.apache.tapestry5.internal.services.StringInterner;
 import org.apache.tapestry5.internal.services.StringInternerImpl;
+import org.apache.tapestry5.ioc.AnnotationProvider;
 import org.apache.tapestry5.ioc.Configuration;
-import org.apache.tapestry5.ioc.MessageFormatter;
-import org.apache.tapestry5.ioc.Messages;
 import org.apache.tapestry5.ioc.ObjectLocator;
 import org.apache.tapestry5.ioc.internal.BasicDataTypeAnalyzers;
 import org.apache.tapestry5.ioc.internal.BasicTypeCoercions;
 import org.apache.tapestry5.ioc.internal.services.PlasticProxyFactoryImpl;
 import org.apache.tapestry5.ioc.internal.services.PropertyAccessImpl;
 import org.apache.tapestry5.ioc.internal.services.TypeCoercerImpl;
+import org.apache.tapestry5.ioc.internal.util.TapestryException;
 import org.apache.tapestry5.ioc.services.CoercionTuple;
 import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
 import org.apache.tapestry5.ioc.services.PropertyAccess;
-import org.apache.tapestry5.ioc.services.PropertyAdapter;
 import org.apache.tapestry5.ioc.services.TypeCoercer;
 import org.apache.tapestry5.services.BeanModelSource;
 import org.apache.tapestry5.services.DataTypeAnalyzer;
@@ -46,8 +40,12 @@ import org.apache.tapestry5.services.PropertyConduitSource;
 import org.slf4j.LoggerFactory;
 
 /**
- * Utility class for creating {@link BeanModelSource} instances without
+ * <p>Utility class for creating {@link BeanModelSource} instances without
  * Tapestry-IoC. Usage of Tapestry-IoC is still recommended.
+ * </p>
+ * <p>The setter methods can be used to customize the BeanModelSource to be created and can be 
+ * (and usually are skipped), so <code>BeanModelSource beanModelSource = new BeanModelSourceBuilder().build()</code>
+ * is all you need to do. 
  */
 public class BeanModelSourceBuilder {
 
@@ -60,14 +58,8 @@ public class BeanModelSourceBuilder {
     private StringInterner stringInterner;
 
     /**
-     * Sets the {@link TypeCoercer} to be used.
+     * Creates and returns a {@link BeanModelSource} instance.
      */
-    public BeanModelSourceBuilder setTypeCoercer(TypeCoercer typeCoercer)
-    {
-        this.typeCoercer = typeCoercer;
-        return this;
-    }
-
     public BeanModelSource build() 
     {
         
@@ -101,9 +93,80 @@ public class BeanModelSourceBuilder {
             propertyConduitSource = new PropertyConduitSourceImpl(propertyAccess, plasticProxyFactory, typeCoercer, stringInterner);
         }
         
+        if (objectLocator == null)
+        {
+            objectLocator = new AutobuildOnlyObjectLocator();
+        }
+        
         return new BeanModelSourceImpl(typeCoercer, propertyAccess, propertyConduitSource, plasticProxyFactory, dataTypeAnalyzer, objectLocator);
         
     }
+    
+    /**
+     * Sets the {@link TypeCoercer} to be used.
+     */
+    public BeanModelSourceBuilder setTypeCoercer(TypeCoercer typeCoercer)
+    {
+        this.typeCoercer = typeCoercer;
+        return this;
+    }
+
+    /**
+     * Sets the {@link PropertyAccess} to be used.
+     */
+    public BeanModelSourceBuilder setPropertyAccess(PropertyAccess propertyAccess)
+    {
+        this.propertyAccess = propertyAccess;
+        return this;
+    }
+
+    /**
+     * Sets the {@link PropertyConduitSource} to be used.
+     */
+    public BeanModelSourceBuilder setPropertyConduitSource(PropertyConduitSource propertyConduitSource)
+    {
+        this.propertyConduitSource = propertyConduitSource;
+        return this;
+    }
+
+    /**
+     * Sets the {@link PlasticProxyFactory} to be used.
+     */
+    public BeanModelSourceBuilder setPlasticProxyFactory(PlasticProxyFactory plasticProxyFactory)
+    {
+        this.plasticProxyFactory = plasticProxyFactory;
+        return this;
+    }
+
+    /**
+     * Sets the {@link DataTypeAnalyzer} to be used.
+     */
+    public BeanModelSourceBuilder setDataTypeAnalyzer(DataTypeAnalyzer dataTypeAnalyzer)
+    {
+        this.dataTypeAnalyzer = dataTypeAnalyzer;
+        return this;
+    }
+
+    /**
+     * Sets the {@link ObjectLocator} to be used. Actually, the only method of it actually used is
+     * {@link ObjectLocator#autobuild(Class)}, for creating objects of the class described by the
+     * {@link BeanModel}.
+     */
+    public BeanModelSourceBuilder setObjectLocator(ObjectLocator objectLocator)
+    {
+        this.objectLocator = objectLocator;
+        return this;
+    }
+
+    /**
+     * Sets the {@link StringInterner} to be used.
+     */
+    public BeanModelSourceBuilder setStringInterner(StringInterner stringInterner)
+    {
+        this.stringInterner = stringInterner;
+        return this;
+    }
+    
     private void createTypeCoercer() 
     {
         CoercionTupleConfiguration configuration = new CoercionTupleConfiguration();
@@ -134,5 +197,58 @@ public class BeanModelSourceBuilder {
         }
 
     }
+    
+    final private static class AutobuildOnlyObjectLocator implements ObjectLocator {
+
+        @Override
+        public <T> T getService(String serviceId, Class<T> serviceInterface)
+        {
+            throw new RuntimeException("Not implemented");
+        }
+
+        @Override
+        public <T> T getService(Class<T> serviceInterface)
+        {
+            throw new RuntimeException("Not implemented");
+        }
+
+        @Override
+        public <T> T getService(Class<T> serviceInterface,
+                Class<? extends Annotation>... markerTypes)
+        {
+            throw new RuntimeException("Not implemented");
+        }
+
+        @Override
+        public <T> T getObject(Class<T> objectType, AnnotationProvider annotationProvider)
+        {
+            throw new RuntimeException("Not implemented");
+        }
+
+        @Override
+        public <T> T autobuild(Class<T> clazz)
+        {
+            try
+            {
+                return clazz.newInstance();
+            }
+            catch (Exception e)
+            {
+                throw new TapestryException("Couldn't instantiate class " + clazz.getName(), e);
+            }
+        }
+
+        @Override
+        public <T> T autobuild(String description, Class<T> clazz)
+        {
+            return autobuild(clazz);
+        }
+
+        public <T> T proxy(Class<T> interfaceClass, Class<? extends T> implementationClass)
+        {
+            throw new RuntimeException("Not implemented");
+        }
+        
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/97bd4d5e/commons/src/main/java/org/apache/tapestry5/internal/services/DefaultDataTypeAnalyzer.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/org/apache/tapestry5/internal/services/DefaultDataTypeAnalyzer.java b/commons/src/main/java/org/apache/tapestry5/internal/services/DefaultDataTypeAnalyzer.java
new file mode 100644
index 0000000..cdf98e5
--- /dev/null
+++ b/commons/src/main/java/org/apache/tapestry5/internal/services/DefaultDataTypeAnalyzer.java
@@ -0,0 +1,61 @@
+// Copyright 2007, 2008, 2012 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.
+
+package org.apache.tapestry5.internal.services;
+
+import org.apache.tapestry5.ioc.services.PropertyAdapter;
+import org.apache.tapestry5.ioc.util.StrategyRegistry;
+import org.apache.tapestry5.services.DataTypeAnalyzer;
+import org.apache.tapestry5.services.InvalidationListener;
+
+import java.util.Map;
+
+/**
+ * The default data type analyzer, which is based entirely on the type of the property (and not on annotations or naming
+ * conventions). This is based on a configuration of property type class to string provided as an IoC service
+ * configuration.
+ */
+public class DefaultDataTypeAnalyzer implements DataTypeAnalyzer, Runnable
+{
+    private final StrategyRegistry<String> registry;
+
+    public DefaultDataTypeAnalyzer(Map<Class, String> configuration)
+    {
+        registry = StrategyRegistry.newInstance(String.class, configuration);
+    }
+
+    /**
+     * Clears the registry on an invalidation event (this is because the registry caches results, and the keys are
+     * classes that may be component classes from the invalidated component class loader).
+     */
+    public void run()
+    {
+        registry.clearCache();
+    }
+
+    public String identifyDataType(PropertyAdapter adapter)
+    {
+        Class propertyType = adapter.getType();
+
+        String dataType = registry.get(propertyType);
+
+        // To avoid "no strategy" exceptions, we expect a contribution of Object.class to the empty
+        // string. We convert that back to a null.
+
+        if (dataType.equals(""))
+            return null;
+
+        return dataType;
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/97bd4d5e/commons/src/main/java/org/apache/tapestry5/ioc/internal/BasicDataTypeAnalyzers.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/org/apache/tapestry5/ioc/internal/BasicDataTypeAnalyzers.java b/commons/src/main/java/org/apache/tapestry5/ioc/internal/BasicDataTypeAnalyzers.java
index df7564f..4d603f5 100644
--- a/commons/src/main/java/org/apache/tapestry5/ioc/internal/BasicDataTypeAnalyzers.java
+++ b/commons/src/main/java/org/apache/tapestry5/ioc/internal/BasicDataTypeAnalyzers.java
@@ -16,10 +16,12 @@ package org.apache.tapestry5.ioc.internal;
 import java.util.Calendar;
 import java.util.Date;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.tapestry5.beaneditor.DataTypeConstants;
 import org.apache.tapestry5.internal.services.AnnotationDataTypeAnalyzer;
+import org.apache.tapestry5.internal.services.DefaultDataTypeAnalyzer;
 import org.apache.tapestry5.ioc.MappedConfiguration;
 import org.apache.tapestry5.ioc.OrderedConfiguration;
 import org.apache.tapestry5.ioc.services.PropertyAdapter;
@@ -46,7 +48,8 @@ public class BasicDataTypeAnalyzers
     {
         DefaultDataTypeAnalyzerMappedConfiguration mappedConfiguration = new DefaultDataTypeAnalyzerMappedConfiguration();
         provideDefaultDataTypeAnalyzers(mappedConfiguration);
-        return new CombinedDataTypeAnalyzer(new AnnotationDataTypeAnalyzer(), new MapDataTypeAnalyzer(mappedConfiguration.getMap()));
+        
+        return new CombinedDataTypeAnalyzer(new AnnotationDataTypeAnalyzer(), new DefaultDataTypeAnalyzer(mappedConfiguration.getMap()));
     }
     
     /**
@@ -106,43 +109,29 @@ public class BasicDataTypeAnalyzers
         
     }
     
-    final private static class MapDataTypeAnalyzer implements DataTypeAnalyzer
-    {
-        
-        final Map<Class, String> map;
-
-        public MapDataTypeAnalyzer(Map<Class, String> map) {
-            this.map = map;
-        }
-
-        @Override
-        public String identifyDataType(PropertyAdapter adapter) {
-            return map.get(adapter.getType());
-        }
-        
-    }
-    
     final private static class CombinedDataTypeAnalyzer implements DataTypeAnalyzer 
     {
 
-        final private DataTypeAnalyzer first, second;
+        final private DataTypeAnalyzer[] analyzers;
 
-        public CombinedDataTypeAnalyzer(DataTypeAnalyzer first, DataTypeAnalyzer second) 
+        public CombinedDataTypeAnalyzer(DataTypeAnalyzer... analyzers) 
         {
-            super();
-            this.first = first;
-            this.second = second;
+        	this.analyzers = analyzers;
         }
 
         @Override
         public String identifyDataType(PropertyAdapter adapter) 
         {
-            String type = first.identifyDataType(adapter);
-            if (type == null) 
-            {
-                type = second.identifyDataType(adapter);
-            }
-            return type;
+        	String type = null;
+        	for (DataTypeAnalyzer analyzer : analyzers) 
+        	{
+				type = analyzer.identifyDataType(adapter);
+				if (type != null)
+				{
+					break;
+				}
+			}
+        	return type;
         }
 
     }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/97bd4d5e/commons/src/main/java/org/apache/tapestry5/ioc/util/StrategyRegistry.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/org/apache/tapestry5/ioc/util/StrategyRegistry.java b/commons/src/main/java/org/apache/tapestry5/ioc/util/StrategyRegistry.java
new file mode 100644
index 0000000..fbdfc6a
--- /dev/null
+++ b/commons/src/main/java/org/apache/tapestry5/ioc/util/StrategyRegistry.java
@@ -0,0 +1,172 @@
+// Copyright 2006, 2007, 2008, 2011, 2012 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.
+
+package org.apache.tapestry5.ioc.util;
+
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.InheritanceSearch;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A key component in implementing the "Gang of Four" Strategy pattern. A StrategyRegistry will match up a given input
+ * type with a registered strategy for that type.
+ *
+ * @param <A> the type of the strategy adapter
+ */
+public final class StrategyRegistry<A>
+{
+    private final Class<A> adapterType;
+
+    private final boolean allowNonMatch;
+
+    private final Map<Class, A> registrations = CollectionFactory.newMap();
+
+    private final Map<Class, A> cache = CollectionFactory.newConcurrentMap();
+
+    /**
+     * Used to identify types for which there is no matching adapter; we're using it as if it were a ConcurrentSet.
+     */
+    private final Map<Class, Boolean> unmatched = CollectionFactory.newConcurrentMap();
+
+    private StrategyRegistry(Class<A> adapterType, Map<Class, A> registrations, boolean allowNonMatch)
+    {
+        this.adapterType = adapterType;
+        this.allowNonMatch = allowNonMatch;
+
+        this.registrations.putAll(registrations);
+    }
+
+    /**
+     * Creates a strategy registry for the given adapter type. The registry will be configured to require matches.
+     *
+     * @param adapterType   the type of adapter retrieved from the registry
+     * @param registrations map of registrations (the contents of the map are copied)
+     */
+    public static <A> StrategyRegistry<A> newInstance(Class<A> adapterType,
+                                                      Map<Class, A> registrations)
+    {
+        return newInstance(adapterType, registrations, false);
+    }
+
+    /**
+     * Creates a strategy registry for the given adapter type.
+     *
+     * @param adapterType   the type of adapter retrieved from the registry
+     * @param registrations map of registrations (the contents of the map are copied)
+     * @param allowNonMatch if true, then the registry supports non-matches when retrieving an adapter
+     */
+    public static <A> StrategyRegistry<A> newInstance(
+            Class<A> adapterType,
+            Map<Class, A> registrations, boolean allowNonMatch)
+    {
+        return new StrategyRegistry<A>(adapterType, registrations, allowNonMatch);
+    }
+
+    public void clearCache()
+    {
+        cache.clear();
+        unmatched.clear();
+    }
+
+    public Class<A> getAdapterType()
+    {
+        return adapterType;
+    }
+
+    /**
+     * Gets an adapter for an object. Searches based on the value's class, unless the value is null, in which case, a
+     * search on class void is used.
+     *
+     * @param value for which an adapter is needed
+     * @return the adapter for the value or null if not found (and allowNonMatch is true)
+     * @throws IllegalArgumentException if no matching adapter may be found and allowNonMatch is false
+     */
+
+    public A getByInstance(Object value)
+    {
+        return get(value == null ? void.class : value.getClass());
+    }
+
+    /**
+     * Searches for an adapter corresponding to the given input type.
+     *
+     * @param type the type to search
+     * @return the adapter for the type or null if not found (and allowNonMatch is true)
+     * @throws IllegalArgumentException if no matching adapter may be found   and allowNonMatch is false
+     */
+    public A get(Class type)
+    {
+
+        A result = cache.get(type);
+
+        if (result != null) return result;
+
+        if (unmatched.containsKey(type)) return null;
+
+
+        result = findMatch(type);
+
+        // This may be null in the case that there is no match and we're allowing that to not
+        // be an error.  That's why we check via containsKey.
+
+        if (result != null)
+        {
+            cache.put(type, result);
+        } else
+        {
+            unmatched.put(type, true);
+        }
+
+        return result;
+    }
+
+    private A findMatch(Class type)
+    {
+        for (Class t : new InheritanceSearch(type))
+        {
+            A result = registrations.get(t);
+
+            if (result != null) return result;
+        }
+
+        if (allowNonMatch) return null;
+
+        // Report the error. These things really confused the hell out of people in Tap4, so we're
+        // going the extra mile on the exception message.
+
+        List<String> names = CollectionFactory.newList();
+        for (Class t : registrations.keySet())
+            names.add(t.getName());
+
+        throw new UnknownValueException(String.format("No adapter from type %s to type %s is available.", type.getName(), adapterType.getName()), null, null,
+                new AvailableValues("registered types", registrations));
+    }
+
+    /**
+     * Returns the registered types for which adapters are available.
+     */
+    public Collection<Class> getTypes()
+    {
+        return CollectionFactory.newList(registrations.keySet());
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("StrategyRegistry[%s]", adapterType.getName());
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/97bd4d5e/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultDataTypeAnalyzer.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultDataTypeAnalyzer.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultDataTypeAnalyzer.java
deleted file mode 100644
index cdf98e5..0000000
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/DefaultDataTypeAnalyzer.java
+++ /dev/null
@@ -1,61 +0,0 @@
-// Copyright 2007, 2008, 2012 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.
-
-package org.apache.tapestry5.internal.services;
-
-import org.apache.tapestry5.ioc.services.PropertyAdapter;
-import org.apache.tapestry5.ioc.util.StrategyRegistry;
-import org.apache.tapestry5.services.DataTypeAnalyzer;
-import org.apache.tapestry5.services.InvalidationListener;
-
-import java.util.Map;
-
-/**
- * The default data type analyzer, which is based entirely on the type of the property (and not on annotations or naming
- * conventions). This is based on a configuration of property type class to string provided as an IoC service
- * configuration.
- */
-public class DefaultDataTypeAnalyzer implements DataTypeAnalyzer, Runnable
-{
-    private final StrategyRegistry<String> registry;
-
-    public DefaultDataTypeAnalyzer(Map<Class, String> configuration)
-    {
-        registry = StrategyRegistry.newInstance(String.class, configuration);
-    }
-
-    /**
-     * Clears the registry on an invalidation event (this is because the registry caches results, and the keys are
-     * classes that may be component classes from the invalidated component class loader).
-     */
-    public void run()
-    {
-        registry.clearCache();
-    }
-
-    public String identifyDataType(PropertyAdapter adapter)
-    {
-        Class propertyType = adapter.getType();
-
-        String dataType = registry.get(propertyType);
-
-        // To avoid "no strategy" exceptions, we expect a contribution of Object.class to the empty
-        // string. We convert that back to a null.
-
-        if (dataType.equals(""))
-            return null;
-
-        return dataType;
-    }
-}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/97bd4d5e/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AbstractBeanModelSourceImplTest.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AbstractBeanModelSourceImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AbstractBeanModelSourceImplTest.java
new file mode 100644
index 0000000..1294934
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/AbstractBeanModelSourceImplTest.java
@@ -0,0 +1,757 @@
+// Copyright 2007, 2008, 2009, 2010, 2011 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.
+
+package org.apache.tapestry5.internal.services;
+
+import org.apache.tapestry5.PropertyConduit;
+import org.apache.tapestry5.beaneditor.BeanModel;
+import org.apache.tapestry5.beaneditor.BeanModelSourceBuilder;
+import org.apache.tapestry5.beaneditor.PropertyModel;
+import org.apache.tapestry5.beaneditor.RelativePosition;
+import org.apache.tapestry5.beaneditor.Sortable;
+import org.apache.tapestry5.internal.PropertyOrderBean;
+import org.apache.tapestry5.internal.test.InternalBaseTestCase;
+import org.apache.tapestry5.internal.transform.pages.ReadOnlyBean;
+import org.apache.tapestry5.ioc.Messages;
+import org.apache.tapestry5.ioc.util.UnknownValueException;
+import org.apache.tapestry5.services.BeanModelSource;
+import org.easymock.EasyMock;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.Test;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+/**
+ * Tests for the bean editor model source itself, as well as the model classes.
+ */
+public abstract class AbstractBeanModelSourceImplTest extends InternalBaseTestCase
+{
+    private BeanModelSource source;
+
+    protected abstract BeanModelSource create();
+
+    @BeforeClass
+    public void setup()
+    {
+        source = create();
+    }
+
+    /**
+     * Tests defaults for property names, labels and conduits.
+     */
+    @Test
+    public void default_model_for_bean()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        assertSame(model.getBeanType(), SimpleBean.class);
+
+        // Based on order of the getter methods (no longer alphabetical)
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
+
+        assertEquals(model.toString(),
+                "BeanModel[org.apache.tapestry5.internal.services.SimpleBean properties:firstName, lastName, age]");
+
+        PropertyModel age = model.get("age");
+
+        assertEquals(age.getLabel(), "Age");
+        assertSame(age.getPropertyType(), int.class);
+        assertEquals(age.getDataType(), "number");
+
+        PropertyModel firstName = model.get("firstName");
+
+        assertEquals(firstName.getLabel(), "First Name");
+        assertEquals(firstName.getPropertyType(), String.class);
+        assertEquals(firstName.getDataType(), "text");
+
+        assertEquals(model.get("lastName").getLabel(), "Last Name");
+
+        PropertyConduit conduit = model.get("lastName").getConduit();
+
+        SimpleBean instance = new SimpleBean();
+
+        instance.setLastName("Lewis Ship");
+
+        assertEquals(conduit.get(instance), "Lewis Ship");
+
+        conduit.set(instance, "TapestryDude");
+
+        assertEquals(instance.getLastName(), "TapestryDude");
+
+        // Now, one with some type coercion.
+
+        age.getConduit().set(instance, "40");
+
+        assertEquals(instance.getAge(), 40);
+
+        verify();
+    }
+
+    @Test
+    public void include_properties()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        assertSame(model.getBeanType(), SimpleBean.class);
+
+        model.include("lastname", "firstname");
+
+        // Based on order of the getter methods (no longer alphabetical)
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("lastName", "firstName"));
+
+        verify();
+    }
+
+    @Test
+    public void add_before()
+    {
+        Messages messages = mockMessages();
+        PropertyConduit conduit = mockPropertyConduit();
+
+        Class propertyType = String.class;
+
+        stub_contains(messages, false);
+
+        expect(conduit.getPropertyType()).andReturn(propertyType).atLeastOnce();
+        expect(conduit.getAnnotation(EasyMock.isA(Class.class))).andStubReturn(null);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
+
+        // Note the use of case insensitivity here.
+
+        PropertyModel property = model.add(RelativePosition.BEFORE, "lastname", "middleInitial", conduit);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "middleInitial", "lastName", "age"));
+
+        assertEquals(property.getPropertyName(), "middleInitial");
+        assertSame(property.getConduit(), conduit);
+        assertSame(property.getPropertyType(), propertyType);
+
+        verify();
+    }
+
+    /**
+     * TAPESTRY-2202
+     */
+    @Test
+    public void new_instance()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel<SimpleBean> model = source.create(SimpleBean.class, true, messages);
+
+        SimpleBean s1 = model.newInstance();
+
+        assertNotNull(s1);
+
+        SimpleBean s2 = model.newInstance();
+
+        assertNotNull(s2);
+        assertNotSame(s1, s2);
+
+        verify();
+    }
+
+    @Test
+    public void add_before_using_default_conduit()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        model.exclude("firstname");
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("lastName", "age"));
+
+        // Note the use of case insensitivity here.
+
+        PropertyModel property = model.add(RelativePosition.BEFORE, "lastname", "firstName");
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
+
+        assertEquals(property.getPropertyName(), "firstName");
+        assertSame(property.getPropertyType(), String.class);
+
+        verify();
+    }
+
+    @Test
+    public void add_after()
+    {
+        Messages messages = mockMessages();
+        PropertyConduit conduit = mockPropertyConduit();
+
+        Class propertyType = String.class;
+
+        stub_contains(messages, false);
+
+        expect(conduit.getPropertyType()).andReturn(propertyType).atLeastOnce();
+
+        expect(conduit.getAnnotation(EasyMock.isA(Class.class))).andStubReturn(null);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
+
+        PropertyModel property = model.add(RelativePosition.AFTER, "firstname", "middleInitial", conduit);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "middleInitial", "lastName", "age"));
+
+        assertEquals(property.getPropertyName(), "middleInitial");
+        assertSame(property.getConduit(), conduit);
+        assertSame(property.getPropertyType(), propertyType);
+
+        verify();
+    }
+
+    @Test
+    public void filtering_out_read_only_properties()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(ReadOnlyBean.class, true, messages);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("value"));
+
+        model = source.create(ReadOnlyBean.class, false, messages);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("value", "readOnly"));
+
+        verify();
+    }
+
+    @Test
+    public void non_text_property()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(EnumBean.class, true, messages);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("token"));
+
+        assertEquals(model.get("token").getDataType(), "enum");
+
+        verify();
+    }
+
+    @Test
+    public void add_duplicate_property_name_is_failure()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        try
+        {
+            model.add("age");
+            unreachable();
+        } catch (RuntimeException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "Bean editor model for org.apache.tapestry5.internal.services.SimpleBean already contains a property model for property \'age\'.");
+        }
+
+        verify();
+    }
+
+    @Test
+    public void unknown_property_name()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        try
+        {
+            model.get("frobozz");
+            unreachable();
+        } catch (UnknownValueException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "Bean editor model for org.apache.tapestry5.internal.services.SimpleBean does not contain a property named \'frobozz\'.");
+
+            assertListsEquals(ex.getAvailableValues().getValues(), "age", "firstName", "lastName");
+        }
+
+        verify();
+    }
+
+    @Test
+    public void unknown_property_id()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        model.addEmpty("shrub.foo()");
+
+        try
+        {
+            model.getById("frobozz");
+            unreachable();
+        } catch (UnknownValueException ex)
+        {
+            assertEquals(
+                    ex.getMessage(),
+                    "Bean editor model for org.apache.tapestry5.internal.services.SimpleBean does not contain a property with id \'frobozz\'.");
+
+            assertListsEquals(ex.getAvailableValues().getValues(), "age", "firstName", "lastName", "shrubfoo");
+        }
+
+        verify();
+    }
+
+    @Test
+    public void get_added_property_by_name()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        PropertyModel pm = model.addEmpty("shrub.foo()");
+
+        assertSame(model.get("Shrub.Foo()"), pm);
+
+        verify();
+    }
+
+    @Test
+    public void get_added_property_by_id()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        PropertyModel pm = model.addEmpty("shrub.foo()");
+
+        assertSame(model.getById("ShrubFoo"), pm);
+
+        verify();
+
+    }
+
+    @Test
+    public void order_via_annotation()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(StoogeBean.class, true, messages);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("larry", "moe", "shemp", "curly"));
+
+        verify();
+    }
+
+    @Test
+    public void edit_property_label()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages).get("age").label("Decrepitude").model();
+
+        assertEquals(model.get("age").getLabel(), "Decrepitude");
+
+        verify();
+    }
+
+    @Test
+    public void label_from_component_messages()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        train_contains(messages, "age-label", true);
+        train_get(messages, "age-label", "Decrepitude");
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        assertEquals(model.get("age").getLabel(), "Decrepitude");
+
+        verify();
+    }
+
+    @Test
+    public void array_type_bean()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(StringArrayBean.class, true, messages);
+
+        // There's not editor for string arrays yet, so it won't show up normally.
+
+        PropertyModel propertyModel = model.add("array");
+
+        assertSame(propertyModel.getPropertyType(), String[].class);
+
+        String[] value =
+                {"foo", "bar"};
+
+        StringArrayBean bean = new StringArrayBean();
+
+        PropertyConduit conduit = propertyModel.getConduit();
+
+        conduit.set(bean, value);
+
+        assertSame(bean.getArray(), value);
+
+        assertSame(conduit.get(bean), value);
+
+        verify();
+    }
+
+    @Test
+    public void composite_bean()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        train_contains(messages, "simpleage-label", true);
+        train_get(messages, "simpleage-label", "Years of Age");
+
+        replay();
+
+        BeanModel model = source.create(CompositeBean.class, true, messages);
+
+        // No editor for CompositeBean, so this will be empty.
+
+        assertEquals(model.getPropertyNames(), Collections.emptyList());
+
+        // There's not editor for string arrays yet, so it won't show up normally.
+
+        PropertyModel firstName = model.add("simple.firstName");
+
+        assertEquals(firstName.getLabel(), "First Name");
+
+        PropertyModel age = model.add("simple.age");
+        assertEquals(age.getLabel(), "Years of Age");
+
+        CompositeBean bean = new CompositeBean();
+
+        firstName.getConduit().set(bean, "Fred");
+        age.getConduit().set(bean, "97");
+
+        assertEquals(bean.getSimple().getFirstName(), "Fred");
+        assertEquals(bean.getSimple().getAge(), 97);
+
+        bean.getSimple().setAge(24);
+
+        assertEquals(age.getConduit().get(bean), new Integer(24));
+
+        verify();
+    }
+
+    @Test
+    public void default_properties_exclude_write_only()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(WriteOnlyBean.class, false, messages);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("readOnly", "readWrite"));
+
+        verify();
+    }
+
+    @Test
+    public void add_synthetic_property()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        PropertyModel property = model.addEmpty("placeholder");
+
+        assertFalse(property.isSortable());
+        assertSame(property.getPropertyType(), Object.class);
+        assertEquals(property.getLabel(), "Placeholder");
+
+        verify();
+    }
+
+    @Test
+    public void add_missing_property_is_failure()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        try
+        {
+            model.add("doesNotExist");
+            unreachable();
+        } catch (PropertyExpressionException ex)
+        {
+            assertMessageContains(ex, "does not contain", "doesNotExist");
+        }
+
+        verify();
+    }
+
+    @Test
+    public void exclude_property()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        assertSame(model.exclude("age"), model);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName"));
+
+        verify();
+    }
+
+    @Test
+    public void exclude_unknown_property_is_noop()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        assertSame(model.exclude("frobozz"), model);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
+
+        verify();
+    }
+
+    @Test
+    public void nonvisual_properties_are_excluded()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(NonVisualBean.class, true, messages);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("name"));
+
+        verify();
+    }
+
+    @Test
+    public void reorder()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(SimpleBean.class, true, messages);
+
+        assertSame(model.getBeanType(), SimpleBean.class);
+
+        // Based on order of the getter methods (no longer alphabetical)
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
+
+        // Testing a couple of things here:
+        // 1) case insensitive
+        // 2) unreferenced property names added to the end.
+
+        model.reorder("lastname", "AGE");
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("lastName", "age", "firstName"));
+
+        verify();
+    }
+
+    @Test
+    public void reoder_from_annotation()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel model = source.create(PropertyOrderBean.class, true, messages);
+
+        assertEquals(model.getPropertyNames(), Arrays.asList("third", "first", "second"));
+
+        verify();
+    }
+
+    // https://issues.apache.org/jira/browse/TAP5-1798
+    @Test
+    public void static_fields_are_ignored()
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel<BeanWithStaticField> model = source.createDisplayModel(BeanWithStaticField.class,  messages);
+
+        assertListsEquals(model.getPropertyNames(), "name");
+
+        verify();
+    }
+    
+    // https://issues.apache.org/jira/browse/TAP5-2305
+    @Test
+    public void sortable_annotation() 
+    {
+        Messages messages = mockMessages();
+
+        stub_contains(messages, false);
+
+        replay();
+
+        BeanModel<SortableBean> model = source.createDisplayModel(SortableBean.class,  messages);
+        model.add("nonSortableByDefault");
+        model.add("sortable");
+        
+        // checking whether non-@Sortable annotated properties still behave in the old ways
+        assertTrue(model.get("sortableByDefault").isSortable());
+        assertFalse(model.get("nonSortableByDefault").isSortable());
+        
+        // checking @Sortable itself
+        assertFalse(model.get("nonSortable").isSortable());
+        assertTrue(model.get("sortable").isSortable());
+
+        verify();
+    }
+    
+    final private static class SortableBean
+    {
+        private int sortableByDefault;
+        private int nonSortable;
+        private SimpleBean sortable;
+        private SimpleBean nonSortableByDefault;
+        
+        public int getSortableByDefault()
+        {
+            return sortableByDefault;
+        }
+        
+        @Sortable(false)
+        public int getNonSortable()
+        {
+            return nonSortable;
+        }
+        
+        @Sortable(true)
+        public SimpleBean getSortable()
+        {
+            return sortable;
+        }
+        
+        public SimpleBean getNonSortableByDefault()
+        {
+            return nonSortableByDefault;
+        }
+        
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/97bd4d5e/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/BeanModelSourceBuilderTest.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/BeanModelSourceBuilderTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/BeanModelSourceBuilderTest.java
new file mode 100644
index 0000000..78502e6
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/BeanModelSourceBuilderTest.java
@@ -0,0 +1,33 @@
+// Copyright 2014 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.
+package org.apache.tapestry5.internal.services;
+
+import org.apache.tapestry5.beaneditor.BeanModelSourceBuilder;
+import org.apache.tapestry5.services.BeanModelSource;
+import org.testng.annotations.Test;
+
+/**
+ * Tests a BeanModelSource created using {@link BeanModelSourceBuilder}.
+ */
+@Test
+public class BeanModelSourceBuilderTest extends AbstractBeanModelSourceImplTest
+{
+
+    @Override
+    protected BeanModelSource create()
+    {
+        return new BeanModelSourceBuilder().build();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/97bd4d5e/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/BeanModelSourceImplTest.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/BeanModelSourceImplTest.java b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/BeanModelSourceImplTest.java
index 17c9480..a23a3a5 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/BeanModelSourceImplTest.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/internal/services/BeanModelSourceImplTest.java
@@ -1,4 +1,4 @@
-// Copyright 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2014 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.
@@ -11,744 +11,20 @@
 // WITHOUT WARRANTIES OR 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.tapestry5.internal.services;
 
-import org.apache.tapestry5.PropertyConduit;
-import org.apache.tapestry5.beaneditor.BeanModel;
-import org.apache.tapestry5.beaneditor.PropertyModel;
-import org.apache.tapestry5.beaneditor.RelativePosition;
-import org.apache.tapestry5.beaneditor.Sortable;
-import org.apache.tapestry5.internal.PropertyOrderBean;
-import org.apache.tapestry5.internal.test.InternalBaseTestCase;
-import org.apache.tapestry5.internal.transform.pages.ReadOnlyBean;
-import org.apache.tapestry5.ioc.Messages;
-import org.apache.tapestry5.ioc.util.UnknownValueException;
 import org.apache.tapestry5.services.BeanModelSource;
-import org.easymock.EasyMock;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.Test;
-
-import java.util.Arrays;
-import java.util.Collections;
 
 /**
- * Tests for the bean editor model source itself, as well as the model classes.
+ * Tests a BeanModelSource created using Tapestry-IoC.
  */
-public class BeanModelSourceImplTest extends InternalBaseTestCase
+public class BeanModelSourceImplTest extends AbstractBeanModelSourceImplTest
 {
-    private BeanModelSource source;
-
-    @BeforeClass
-    public void setup()
-    {
-        source = getObject(BeanModelSource.class, null);
-    }
-
-    /**
-     * Tests defaults for property names, labels and conduits.
-     */
-    @Test
-    public void default_model_for_bean()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        assertSame(model.getBeanType(), SimpleBean.class);
-
-        // Based on order of the getter methods (no longer alphabetical)
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
-
-        assertEquals(model.toString(),
-                "BeanModel[org.apache.tapestry5.internal.services.SimpleBean properties:firstName, lastName, age]");
-
-        PropertyModel age = model.get("age");
-
-        assertEquals(age.getLabel(), "Age");
-        assertSame(age.getPropertyType(), int.class);
-        assertEquals(age.getDataType(), "number");
-
-        PropertyModel firstName = model.get("firstName");
-
-        assertEquals(firstName.getLabel(), "First Name");
-        assertEquals(firstName.getPropertyType(), String.class);
-        assertEquals(firstName.getDataType(), "text");
-
-        assertEquals(model.get("lastName").getLabel(), "Last Name");
-
-        PropertyConduit conduit = model.get("lastName").getConduit();
-
-        SimpleBean instance = new SimpleBean();
-
-        instance.setLastName("Lewis Ship");
-
-        assertEquals(conduit.get(instance), "Lewis Ship");
-
-        conduit.set(instance, "TapestryDude");
-
-        assertEquals(instance.getLastName(), "TapestryDude");
-
-        // Now, one with some type coercion.
-
-        age.getConduit().set(instance, "40");
-
-        assertEquals(instance.getAge(), 40);
-
-        verify();
-    }
-
-    @Test
-    public void include_properties()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        assertSame(model.getBeanType(), SimpleBean.class);
-
-        model.include("lastname", "firstname");
-
-        // Based on order of the getter methods (no longer alphabetical)
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("lastName", "firstName"));
-
-        verify();
-    }
-
-    @Test
-    public void add_before()
-    {
-        Messages messages = mockMessages();
-        PropertyConduit conduit = mockPropertyConduit();
-
-        Class propertyType = String.class;
-
-        stub_contains(messages, false);
-
-        expect(conduit.getPropertyType()).andReturn(propertyType).atLeastOnce();
-        expect(conduit.getAnnotation(EasyMock.isA(Class.class))).andStubReturn(null);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
-
-        // Note the use of case insensitivity here.
-
-        PropertyModel property = model.add(RelativePosition.BEFORE, "lastname", "middleInitial", conduit);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "middleInitial", "lastName", "age"));
-
-        assertEquals(property.getPropertyName(), "middleInitial");
-        assertSame(property.getConduit(), conduit);
-        assertSame(property.getPropertyType(), propertyType);
-
-        verify();
-    }
-
-    /**
-     * TAPESTRY-2202
-     */
-    @Test
-    public void new_instance()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel<SimpleBean> model = source.create(SimpleBean.class, true, messages);
-
-        SimpleBean s1 = model.newInstance();
-
-        assertNotNull(s1);
-
-        SimpleBean s2 = model.newInstance();
-
-        assertNotNull(s2);
-        assertNotSame(s1, s2);
-
-        verify();
-    }
-
-    @Test
-    public void add_before_using_default_conduit()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        model.exclude("firstname");
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("lastName", "age"));
-
-        // Note the use of case insensitivity here.
-
-        PropertyModel property = model.add(RelativePosition.BEFORE, "lastname", "firstName");
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
-
-        assertEquals(property.getPropertyName(), "firstName");
-        assertSame(property.getPropertyType(), String.class);
-
-        verify();
-    }
-
-    @Test
-    public void add_after()
-    {
-        Messages messages = mockMessages();
-        PropertyConduit conduit = mockPropertyConduit();
-
-        Class propertyType = String.class;
-
-        stub_contains(messages, false);
-
-        expect(conduit.getPropertyType()).andReturn(propertyType).atLeastOnce();
-
-        expect(conduit.getAnnotation(EasyMock.isA(Class.class))).andStubReturn(null);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
-
-        PropertyModel property = model.add(RelativePosition.AFTER, "firstname", "middleInitial", conduit);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "middleInitial", "lastName", "age"));
-
-        assertEquals(property.getPropertyName(), "middleInitial");
-        assertSame(property.getConduit(), conduit);
-        assertSame(property.getPropertyType(), propertyType);
-
-        verify();
-    }
-
-    @Test
-    public void filtering_out_read_only_properties()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(ReadOnlyBean.class, true, messages);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("value"));
-
-        model = source.create(ReadOnlyBean.class, false, messages);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("value", "readOnly"));
-
-        verify();
-    }
-
-    @Test
-    public void non_text_property()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(EnumBean.class, true, messages);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("token"));
-
-        assertEquals(model.get("token").getDataType(), "enum");
-
-        verify();
-    }
-
-    @Test
-    public void add_duplicate_property_name_is_failure()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        try
-        {
-            model.add("age");
-            unreachable();
-        } catch (RuntimeException ex)
-        {
-            assertEquals(
-                    ex.getMessage(),
-                    "Bean editor model for org.apache.tapestry5.internal.services.SimpleBean already contains a property model for property \'age\'.");
-        }
-
-        verify();
-    }
-
-    @Test
-    public void unknown_property_name()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        try
-        {
-            model.get("frobozz");
-            unreachable();
-        } catch (UnknownValueException ex)
-        {
-            assertEquals(
-                    ex.getMessage(),
-                    "Bean editor model for org.apache.tapestry5.internal.services.SimpleBean does not contain a property named \'frobozz\'.");
-
-            assertListsEquals(ex.getAvailableValues().getValues(), "age", "firstName", "lastName");
-        }
-
-        verify();
-    }
-
-    @Test
-    public void unknown_property_id()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        model.addEmpty("shrub.foo()");
-
-        try
-        {
-            model.getById("frobozz");
-            unreachable();
-        } catch (UnknownValueException ex)
-        {
-            assertEquals(
-                    ex.getMessage(),
-                    "Bean editor model for org.apache.tapestry5.internal.services.SimpleBean does not contain a property with id \'frobozz\'.");
-
-            assertListsEquals(ex.getAvailableValues().getValues(), "age", "firstName", "lastName", "shrubfoo");
-        }
-
-        verify();
-    }
-
-    @Test
-    public void get_added_property_by_name()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        PropertyModel pm = model.addEmpty("shrub.foo()");
-
-        assertSame(model.get("Shrub.Foo()"), pm);
-
-        verify();
-    }
-
-    @Test
-    public void get_added_property_by_id()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
 
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        PropertyModel pm = model.addEmpty("shrub.foo()");
-
-        assertSame(model.getById("ShrubFoo"), pm);
-
-        verify();
-
-    }
-
-    @Test
-    public void order_via_annotation()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(StoogeBean.class, true, messages);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("larry", "moe", "shemp", "curly"));
-
-        verify();
-    }
-
-    @Test
-    public void edit_property_label()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages).get("age").label("Decrepitude").model();
-
-        assertEquals(model.get("age").getLabel(), "Decrepitude");
-
-        verify();
-    }
-
-    @Test
-    public void label_from_component_messages()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        train_contains(messages, "age-label", true);
-        train_get(messages, "age-label", "Decrepitude");
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        assertEquals(model.get("age").getLabel(), "Decrepitude");
-
-        verify();
-    }
-
-    @Test
-    public void array_type_bean()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(StringArrayBean.class, true, messages);
-
-        // There's not editor for string arrays yet, so it won't show up normally.
-
-        PropertyModel propertyModel = model.add("array");
-
-        assertSame(propertyModel.getPropertyType(), String[].class);
-
-        String[] value =
-                {"foo", "bar"};
-
-        StringArrayBean bean = new StringArrayBean();
-
-        PropertyConduit conduit = propertyModel.getConduit();
-
-        conduit.set(bean, value);
-
-        assertSame(bean.getArray(), value);
-
-        assertSame(conduit.get(bean), value);
-
-        verify();
-    }
-
-    @Test
-    public void composite_bean()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        train_contains(messages, "simpleage-label", true);
-        train_get(messages, "simpleage-label", "Years of Age");
-
-        replay();
-
-        BeanModel model = source.create(CompositeBean.class, true, messages);
-
-        // No editor for CompositeBean, so this will be empty.
-
-        assertEquals(model.getPropertyNames(), Collections.emptyList());
-
-        // There's not editor for string arrays yet, so it won't show up normally.
-
-        PropertyModel firstName = model.add("simple.firstName");
-
-        assertEquals(firstName.getLabel(), "First Name");
-
-        PropertyModel age = model.add("simple.age");
-        assertEquals(age.getLabel(), "Years of Age");
-
-        CompositeBean bean = new CompositeBean();
-
-        firstName.getConduit().set(bean, "Fred");
-        age.getConduit().set(bean, "97");
-
-        assertEquals(bean.getSimple().getFirstName(), "Fred");
-        assertEquals(bean.getSimple().getAge(), 97);
-
-        bean.getSimple().setAge(24);
-
-        assertEquals(age.getConduit().get(bean), new Integer(24));
-
-        verify();
-    }
-
-    @Test
-    public void default_properties_exclude_write_only()
+    @Override
+    protected BeanModelSource create()
     {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(WriteOnlyBean.class, false, messages);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("readOnly", "readWrite"));
-
-        verify();
+        return getObject(BeanModelSource.class, null);
     }
 
-    @Test
-    public void add_synthetic_property()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        PropertyModel property = model.addEmpty("placeholder");
-
-        assertFalse(property.isSortable());
-        assertSame(property.getPropertyType(), Object.class);
-        assertEquals(property.getLabel(), "Placeholder");
-
-        verify();
-    }
-
-    @Test
-    public void add_missing_property_is_failure()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        try
-        {
-            model.add("doesNotExist");
-            unreachable();
-        } catch (PropertyExpressionException ex)
-        {
-            assertMessageContains(ex, "does not contain", "doesNotExist");
-        }
-
-        verify();
-    }
-
-    @Test
-    public void exclude_property()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        assertSame(model.exclude("age"), model);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName"));
-
-        verify();
-    }
-
-    @Test
-    public void exclude_unknown_property_is_noop()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        assertSame(model.exclude("frobozz"), model);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
-
-        verify();
-    }
-
-    @Test
-    public void nonvisual_properties_are_excluded()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(NonVisualBean.class, true, messages);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("name"));
-
-        verify();
-    }
-
-    @Test
-    public void reorder()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(SimpleBean.class, true, messages);
-
-        assertSame(model.getBeanType(), SimpleBean.class);
-
-        // Based on order of the getter methods (no longer alphabetical)
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("firstName", "lastName", "age"));
-
-        // Testing a couple of things here:
-        // 1) case insensitive
-        // 2) unreferenced property names added to the end.
-
-        model.reorder("lastname", "AGE");
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("lastName", "age", "firstName"));
-
-        verify();
-    }
-
-    @Test
-    public void reoder_from_annotation()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel model = source.create(PropertyOrderBean.class, true, messages);
-
-        assertEquals(model.getPropertyNames(), Arrays.asList("third", "first", "second"));
-
-        verify();
-    }
-
-    // https://issues.apache.org/jira/browse/TAP5-1798
-    @Test
-    public void static_fields_are_ignored()
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel<BeanWithStaticField> model = source.createDisplayModel(BeanWithStaticField.class,  messages);
-
-        assertListsEquals(model.getPropertyNames(), "name");
-
-        verify();
-    }
-    
-    // https://issues.apache.org/jira/browse/TAP5-2305
-    @Test
-    public void sortable_annotation() 
-    {
-        Messages messages = mockMessages();
-
-        stub_contains(messages, false);
-
-        replay();
-
-        BeanModel<SortableBean> model = source.createDisplayModel(SortableBean.class,  messages);
-        model.add("nonSortableByDefault");
-        model.add("sortable");
-        
-        // checking whether non-@Sortable annotated properties still behave in the old ways
-        assertTrue(model.get("sortableByDefault").isSortable());
-        assertFalse(model.get("nonSortableByDefault").isSortable());
-        
-        // checking @Sortable itself
-        assertFalse(model.get("nonSortable").isSortable());
-        assertTrue(model.get("sortable").isSortable());
-
-        verify();
-    }
-    
-    final private static class SortableBean
-    {
-        private int sortableByDefault;
-        private int nonSortable;
-        private SimpleBean sortable;
-        private SimpleBean nonSortableByDefault;
-        
-        public int getSortableByDefault()
-        {
-            return sortableByDefault;
-        }
-        
-        @Sortable(false)
-        public int getNonSortable()
-        {
-            return nonSortable;
-        }
-        
-        @Sortable(true)
-        public SimpleBean getSortable()
-        {
-            return sortable;
-        }
-        
-        public SimpleBean getNonSortableByDefault()
-        {
-            return nonSortableByDefault;
-        }
-        
-    }
-    
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/97bd4d5e/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/util/StrategyRegistry.java
----------------------------------------------------------------------
diff --git a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/util/StrategyRegistry.java b/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/util/StrategyRegistry.java
deleted file mode 100644
index fbdfc6a..0000000
--- a/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/util/StrategyRegistry.java
+++ /dev/null
@@ -1,172 +0,0 @@
-// Copyright 2006, 2007, 2008, 2011, 2012 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.
-
-package org.apache.tapestry5.ioc.util;
-
-import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.ioc.internal.util.InheritanceSearch;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * A key component in implementing the "Gang of Four" Strategy pattern. A StrategyRegistry will match up a given input
- * type with a registered strategy for that type.
- *
- * @param <A> the type of the strategy adapter
- */
-public final class StrategyRegistry<A>
-{
-    private final Class<A> adapterType;
-
-    private final boolean allowNonMatch;
-
-    private final Map<Class, A> registrations = CollectionFactory.newMap();
-
-    private final Map<Class, A> cache = CollectionFactory.newConcurrentMap();
-
-    /**
-     * Used to identify types for which there is no matching adapter; we're using it as if it were a ConcurrentSet.
-     */
-    private final Map<Class, Boolean> unmatched = CollectionFactory.newConcurrentMap();
-
-    private StrategyRegistry(Class<A> adapterType, Map<Class, A> registrations, boolean allowNonMatch)
-    {
-        this.adapterType = adapterType;
-        this.allowNonMatch = allowNonMatch;
-
-        this.registrations.putAll(registrations);
-    }
-
-    /**
-     * Creates a strategy registry for the given adapter type. The registry will be configured to require matches.
-     *
-     * @param adapterType   the type of adapter retrieved from the registry
-     * @param registrations map of registrations (the contents of the map are copied)
-     */
-    public static <A> StrategyRegistry<A> newInstance(Class<A> adapterType,
-                                                      Map<Class, A> registrations)
-    {
-        return newInstance(adapterType, registrations, false);
-    }
-
-    /**
-     * Creates a strategy registry for the given adapter type.
-     *
-     * @param adapterType   the type of adapter retrieved from the registry
-     * @param registrations map of registrations (the contents of the map are copied)
-     * @param allowNonMatch if true, then the registry supports non-matches when retrieving an adapter
-     */
-    public static <A> StrategyRegistry<A> newInstance(
-            Class<A> adapterType,
-            Map<Class, A> registrations, boolean allowNonMatch)
-    {
-        return new StrategyRegistry<A>(adapterType, registrations, allowNonMatch);
-    }
-
-    public void clearCache()
-    {
-        cache.clear();
-        unmatched.clear();
-    }
-
-    public Class<A> getAdapterType()
-    {
-        return adapterType;
-    }
-
-    /**
-     * Gets an adapter for an object. Searches based on the value's class, unless the value is null, in which case, a
-     * search on class void is used.
-     *
-     * @param value for which an adapter is needed
-     * @return the adapter for the value or null if not found (and allowNonMatch is true)
-     * @throws IllegalArgumentException if no matching adapter may be found and allowNonMatch is false
-     */
-
-    public A getByInstance(Object value)
-    {
-        return get(value == null ? void.class : value.getClass());
-    }
-
-    /**
-     * Searches for an adapter corresponding to the given input type.
-     *
-     * @param type the type to search
-     * @return the adapter for the type or null if not found (and allowNonMatch is true)
-     * @throws IllegalArgumentException if no matching adapter may be found   and allowNonMatch is false
-     */
-    public A get(Class type)
-    {
-
-        A result = cache.get(type);
-
-        if (result != null) return result;
-
-        if (unmatched.containsKey(type)) return null;
-
-
-        result = findMatch(type);
-
-        // This may be null in the case that there is no match and we're allowing that to not
-        // be an error.  That's why we check via containsKey.
-
-        if (result != null)
-        {
-            cache.put(type, result);
-        } else
-        {
-            unmatched.put(type, true);
-        }
-
-        return result;
-    }
-
-    private A findMatch(Class type)
-    {
-        for (Class t : new InheritanceSearch(type))
-        {
-            A result = registrations.get(t);
-
-            if (result != null) return result;
-        }
-
-        if (allowNonMatch) return null;
-
-        // Report the error. These things really confused the hell out of people in Tap4, so we're
-        // going the extra mile on the exception message.
-
-        List<String> names = CollectionFactory.newList();
-        for (Class t : registrations.keySet())
-            names.add(t.getName());
-
-        throw new UnknownValueException(String.format("No adapter from type %s to type %s is available.", type.getName(), adapterType.getName()), null, null,
-                new AvailableValues("registered types", registrations));
-    }
-
-    /**
-     * Returns the registered types for which adapters are available.
-     */
-    public Collection<Class> getTypes()
-    {
-        return CollectionFactory.newList(registrations.keySet());
-    }
-
-    @Override
-    public String toString()
-    {
-        return String.format("StrategyRegistry[%s]", adapterType.getName());
-    }
-}