XBEAN-310 introducing PropertyEditorRegistry as a replacement of PropertyEditors static utility

git-svn-id: https://svn.apache.org/repos/asf/geronimo/xbean/trunk@1837693 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractCollectionConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractCollectionConverter.java
index d5dcea7..27b3712 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractCollectionConverter.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/AbstractCollectionConverter.java
@@ -36,12 +36,14 @@
     public AbstractCollectionConverter(Class type, PropertyEditor editor) {
         super(type);
 
-        if (editor == null) throw new NullPointerException("editor is null");
+        if (editor == null) {
+            throw new NullPointerException("editor is null");
+        }
         this.editor = editor;
     }
 
     protected final Object toObjectImpl(String text) {
-        List list = CollectionUtil.toList(text, editor);
+        List list = CollectionUtil.toList(text, getEditor());
         if (list == null) {
             return null;
         }
@@ -62,7 +64,11 @@
             values = (Collection) value;
         }
 
-        String text = CollectionUtil.toString(values, editor);
+        String text = CollectionUtil.toString(values, getEditor());
         return text;
     }
+
+    protected PropertyEditor getEditor() {
+        return editor;
+    }
 }
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ConstructorConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ConstructorConverter.java
index 13fe849..2b09bc0 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ConstructorConverter.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/ConstructorConverter.java
@@ -43,7 +43,6 @@
         } catch (final NoSuchMethodException e) {
             // fine
         }
-
         return null;
     }
 }
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Converter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Converter.java
index d80df32..e696413 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Converter.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Converter.java
@@ -24,7 +24,7 @@
 public interface Converter extends PropertyEditor {
     /**
      * Gets the the type of object supported by this converter.
-     * @return
+     * @return the type used for that converter.
      */
     Class getType();
 
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/EnumConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/EnumConverter.java
index e4e39e5..b88c378 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/EnumConverter.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/EnumConverter.java
@@ -27,6 +27,12 @@
         super(type);
     }
 
+    @Override
+    protected String toStringImpl(final Object value) {
+        return Enum.class.cast(value).name();
+    }
+
+    @Override
     protected Object toObjectImpl(String text) {
         Class type = getType();
 
@@ -34,12 +40,13 @@
             return Enum.valueOf(type, text);
         } catch (Exception cause) {
             try {
-                int index = Integer.parseInt(text);
-                Method method = type.getMethod("values");
-                Object[] values = (Object[]) method.invoke(null);
+                final int index = Integer.parseInt(text);
+                final Method method = type.getMethod("values");
+                final Object[] values = (Object[]) method.invoke(null);
                 return values[index];
-            } catch (NumberFormatException e) {
-            } catch (Exception e) {
+            } catch (final NumberFormatException e) {
+                // no-op
+            } catch (final Exception e) {
                 cause = e;
             }
 
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Primitives.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Primitives.java
new file mode 100644
index 0000000..57530dd
--- /dev/null
+++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/Primitives.java
@@ -0,0 +1,71 @@
+/**
+ * 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.xbean.propertyeditor;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public final class Primitives {
+    private static final Map<Class, Class> PRIMITIVE_TO_WRAPPER;
+    private static final Map<Class, Class> WRAPPER_TO_PRIMITIVE;
+
+    static {
+        {
+            final Map<Class, Class> map = new HashMap<Class, Class>();
+            map.put(boolean.class, Boolean.class);
+            map.put(char.class, Character.class);
+            map.put(byte.class, Byte.class);
+            map.put(short.class, Short.class);
+            map.put(int.class, Integer.class);
+            map.put(long.class, Long.class);
+            map.put(float.class, Float.class);
+            map.put(double.class, Double.class);
+            PRIMITIVE_TO_WRAPPER = Collections.unmodifiableMap(map);
+        }
+
+        {
+            final Map<Class, Class> map = new HashMap<Class, Class>();
+            map.put(Boolean.class, boolean.class);
+            map.put(Character.class, char.class);
+            map.put(Byte.class, byte.class);
+            map.put(Short.class, short.class);
+            map.put(Integer.class, int.class);
+            map.put(Long.class, long.class);
+            map.put(Float.class, float.class);
+            map.put(Double.class, double.class);
+            WRAPPER_TO_PRIMITIVE = Collections.unmodifiableMap(map);
+        }
+    }
+
+    public static Class<?> findSibling(final Class<?> wrapperOrPrimitive) {
+        final Class aClass = PRIMITIVE_TO_WRAPPER.get(wrapperOrPrimitive);
+        return aClass != null ? aClass : WRAPPER_TO_PRIMITIVE.get(wrapperOrPrimitive);
+    }
+
+    public static Class<?> toWrapper(final Class<?> primitive) {
+        return PRIMITIVE_TO_WRAPPER.get(primitive);
+    }
+
+    public static Class<?> toPrimitive(final Class<?> wrapper) {
+        return WRAPPER_TO_PRIMITIVE.get(wrapper);
+    }
+
+    private Primitives() {
+        // no-op
+    }
+}
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditorConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditorConverter.java
new file mode 100644
index 0000000..0356404
--- /dev/null
+++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditorConverter.java
@@ -0,0 +1,48 @@
+/**
+ * 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.xbean.propertyeditor;
+
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorManager;
+
+public class PropertyEditorConverter extends AbstractConverter {
+    public PropertyEditorConverter(final Class<?> type) {
+        super(type);
+    }
+
+    public String toStringImpl(final Object value) throws PropertyEditorException {
+        final PropertyEditor editor = PropertyEditorManager.findEditor(getType());
+        editor.setValue(value);
+        try {
+            return editor.getAsText();
+        } catch (final Exception e) {
+            throw new PropertyEditorException("Error while converting a \"" + getType().getSimpleName() + "\" to text " +
+                    " using the property editor " + editor.getClass().getSimpleName(), e);
+        }
+    }
+
+    public Object toObjectImpl(final String text) throws PropertyEditorException {
+        final PropertyEditor editor = PropertyEditorManager.findEditor(getType());
+        editor.setAsText(text);
+        try {
+            return editor.getValue();
+        } catch (final Exception e) {
+            throw new PropertyEditorException("Error while converting \"" + text + "\" to a " + getType().getSimpleName() +
+                    " using the property editor " + editor.getClass().getSimpleName(), e);
+        }
+    }
+}
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditorRegistry.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditorRegistry.java
new file mode 100644
index 0000000..e7e17ed
--- /dev/null
+++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditorRegistry.java
@@ -0,0 +1,447 @@
+/**
+ * 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.xbean.propertyeditor;
+
+import static java.util.Collections.unmodifiableMap;
+import static org.apache.xbean.recipe.RecipeHelper.getTypeParameters;
+import static org.apache.xbean.recipe.RecipeHelper.toClass;
+
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorManager;
+import java.io.Closeable;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.SortedSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.xbean.recipe.RecipeHelper;
+
+public class PropertyEditorRegistry implements Closeable {
+    private final ConcurrentMap<Type, Converter> registry = new ConcurrentHashMap<Type, Converter>();
+
+    public PropertyEditorRegistry registerDefaults() {
+        register(new ArrayListEditor());
+        register(new BigDecimalEditor());
+        register(new BigIntegerEditor());
+        register(new BooleanEditor());
+        register(new ByteEditor());
+        register(new CharacterEditor());
+        register(new ClassEditor());
+        register(new DateEditor());
+        register(new DoubleEditor());
+        register(new FileEditor());
+        register(new FloatEditor());
+        register(new HashMapEditor());
+        register(new HashtableEditor());
+        register(new IdentityHashMapEditor());
+        register(new Inet4AddressEditor());
+        register(new Inet6AddressEditor());
+        register(new InetAddressEditor());
+        register(new IntegerEditor());
+        register(new LinkedHashMapEditor());
+        register(new LinkedHashSetEditor());
+        register(new LinkedListEditor());
+        register(new ListEditor());
+        register(new LongEditor());
+        register(new MapEditor());
+        register(new ObjectNameEditor());
+        register(new PropertiesEditor());
+        register(new SetEditor());
+        register(new ShortEditor());
+        register(new SortedMapEditor());
+        register(new SortedSetEditor());
+        register(new StringEditor());
+        register(new TreeMapEditor());
+        register(new TreeSetEditor());
+        register(new URIEditor());
+        register(new URLEditor());
+        register(new LoggerConverter());
+        register(new PatternConverter());
+        register(new JndiConverter());
+        register(new VectorEditor());
+        register(new WeakHashMapEditor());
+
+        try {
+            register(new Log4jConverter());
+        } catch (final Throwable e) {
+            // no-op
+        }
+
+        try {
+            register(new CommonsLoggingConverter());
+        } catch (final Throwable e) {
+            // no-op
+        }
+
+        return this;
+    }
+
+    /**
+     * @return a read-only view of the converters.
+     */
+    public Map<Type, Converter> getRegistry() {
+        return unmodifiableMap(registry);
+    }
+
+    /**
+     * Register a converter in the registry.
+     *
+     * @param converter the converter to register.
+     * @return the previously existing converter for the corresponding type or null.
+     */
+    public Converter register(final Converter converter) {
+        if (converter == null) {
+            throw new NullPointerException("converter is null");
+        }
+        final Class<?> type = converter.getType();
+        final Converter existing = registry.put(type, converter);
+
+        final Class<?> sibling = Primitives.findSibling(type);
+        if (sibling != null) {
+            registry.put(sibling, converter);
+        }
+        return existing;
+    }
+
+    /**
+     * Unregister a converter.
+     *
+     * @param converter the converter to remove from the registry.
+     * @return the converter if found, or null.
+     */
+    public Converter unregister(final Converter converter) {
+        if (converter == null) {
+            throw new NullPointerException("converter is null");
+        }
+        return registry.remove(converter.getType());
+    }
+
+    public Converter findConverter(final Type type){
+        {
+            final Converter converter = findInternalConverter(type);
+            if (converter != null) {
+                if (!registry.containsKey(converter.getType())) {
+                    register(converter);
+                }
+                return converter;
+            }
+        }
+
+        {
+            final Converter converter = createConverterFromEditor(type);
+            if (converter != null) {
+                register(converter);
+                return converter;
+            }
+        }
+
+        {
+            final Converter converter = findStructuralConverter(type);
+            if (converter != null) {
+                register(converter);
+                return converter;
+            }
+        }
+
+        return null;
+    }
+
+    public String toString(final Object value) throws PropertyEditorException {
+        if (value == null) {
+            throw new NullPointerException("value is null");
+        }
+        final Class type = unwrapClass(value);
+        final Converter converter = findConverter(type);
+        if (converter == null) {
+            throw new PropertyEditorException("Unable to find PropertyEditor for " + type.getSimpleName());
+        }
+        return converter.toString(value);
+    }
+
+    public Object getValue(final String type, final String value, final ClassLoader classLoader) throws PropertyEditorException {
+        if (type == null) {
+            throw new NullPointerException("type is null");
+        }
+        if (value == null) {
+            throw new NullPointerException("value is null");
+        }
+        if (classLoader == null) {
+            throw new NullPointerException("classLoader is null");
+        }
+
+        try {
+            return getValue(Class.forName(type, true, classLoader), value);
+        } catch (final ClassNotFoundException e) {
+            throw new PropertyEditorException("Type class could not be found: " + type);
+        }
+    }
+
+    public Object getValue(final Type type, final String value) throws PropertyEditorException {
+        if (type == null) {
+            throw new NullPointerException("type is null");
+        }
+        if (value == null) {
+            throw new NullPointerException("value is null");
+        }
+
+        final Converter converter = findConverter(type);
+        if (converter != null) {
+            return converter.toObject(value);
+        }
+
+        final Class clazz = toClass(type);
+
+        final Converter structuralConverter = findStructuralConverter(clazz);
+        if (structuralConverter != null) {
+            register(structuralConverter);
+            return structuralConverter.toObject(value);
+        }
+
+        throw new PropertyEditorException("Unable to find PropertyEditor for " + clazz.getSimpleName());
+    }
+
+    protected Class<?> unwrapClass(final Object value) {
+        Class<?> aClass = value.getClass();
+        while (aClass.getName().contains("$$")) {
+            aClass = aClass.getSuperclass();
+            if (aClass == null || aClass == Object.class) {
+                return value.getClass();
+            }
+        }
+        return aClass;
+    }
+
+    protected Converter findStructuralConverter(final Type type) {
+        if (type == null) throw new NullPointerException("type is null");
+
+        final Class clazz = toClass(type);
+
+        if (Enum.class.isAssignableFrom(clazz)){
+            return new EnumConverter(clazz);
+        }
+
+        {
+            final ConstructorConverter editor = ConstructorConverter.editor(clazz);
+            if (editor != null) {
+                return editor;
+            }
+        }
+
+        {
+            final StaticFactoryConverter editor = StaticFactoryConverter.editor(clazz);
+            if (editor != null) {
+                return editor;
+            }
+        }
+
+        return null;
+    }
+
+    protected Converter createConverterFromEditor(final Type type) {
+        if (type == null) {
+            throw new NullPointerException("type is null");
+        }
+
+        final Class<?> clazz = toClass(type);
+
+        // try to locate this directly from the editor manager first.
+        final PropertyEditor editor = PropertyEditorManager.findEditor(clazz);
+
+        // we're outta here if we got one.
+        if (editor != null) {
+            return new PropertyEditorConverter(clazz);
+        }
+
+
+        // it's possible this was a request for an array class.  We might not
+        // recognize the array type directly, but the component type might be
+        // resolvable
+        if (clazz.isArray() && !clazz.getComponentType().isArray()) {
+            // do a recursive lookup on the base type
+            final PropertyEditor arrayEditor = findEditor(clazz.getComponentType());
+            // if we found a suitable editor for the base component type,
+            // wrapper this in an array adaptor for real use
+            if (findEditor(clazz.getComponentType()) != null) {
+                return new ArrayConverter(clazz, arrayEditor);
+            }
+        }
+
+        return null;
+    }
+
+    protected Converter findInternalConverter(final Type type) {
+        if (type == null) {
+            throw new NullPointerException("type is null");
+        }
+
+        final Class clazz = toClass(type);
+
+        // it's possible this was a request for an array class.  We might not
+        // recognize the array type directly, but the component type might be
+        // resolvable
+        if (clazz.isArray() && !clazz.getComponentType().isArray()) {
+            // do a recursive lookup on the base type
+            PropertyEditor editor = findConverter(clazz.getComponentType());
+            // if we found a suitable editor for the base component type,
+            // wrapper this in an array adaptor for real use
+            if (editor != null) {
+                return new ArrayConverter(clazz, editor);
+            }
+            return null;
+        }
+
+        if (Collection.class.isAssignableFrom(clazz)){
+            Type[] types = getTypeParameters(Collection.class, type);
+
+            Type componentType = String.class;
+            if (types != null && types.length == 1 && types[0] instanceof Class) {
+                componentType = types[0];
+            }
+
+            PropertyEditor editor = findConverter(componentType);
+
+            if (editor != null){
+                if (RecipeHelper.hasDefaultConstructor(clazz)) {
+                    return new GenericCollectionConverter(clazz, editor);
+                } else if (SortedSet.class.isAssignableFrom(clazz)) {
+                    return new GenericCollectionConverter(TreeSet.class, editor);
+                } else if (Set.class.isAssignableFrom(clazz)) {
+                    return new GenericCollectionConverter(LinkedHashSet.class, editor);
+                }
+                return new GenericCollectionConverter(ArrayList.class, editor);
+            }
+
+            return null;
+        }
+
+        if (Map.class.isAssignableFrom(clazz)){
+            Type[] types = getTypeParameters(Map.class, type);
+
+            Type keyType = String.class;
+            Type valueType = String.class;
+            if (types != null && types.length == 2 && types[0] instanceof Class && types[1] instanceof Class) {
+                keyType = types[0];
+                valueType = types[1];
+            }
+
+            final Converter keyConverter = findConverter(keyType);
+            final Converter valueConverter = findConverter(valueType);
+
+            if (keyConverter != null && valueConverter != null){
+                if (RecipeHelper.hasDefaultConstructor(clazz)) {
+                    return new GenericMapConverter(clazz, keyConverter, valueConverter);
+                } else if (SortedMap.class.isAssignableFrom(clazz)) {
+                    return new GenericMapConverter(TreeMap.class, keyConverter, valueConverter);
+                } else if (ConcurrentMap.class.isAssignableFrom(clazz)) {
+                    return new GenericMapConverter(ConcurrentHashMap.class, keyConverter, valueConverter);
+                }
+                return new GenericMapConverter(LinkedHashMap.class, keyConverter, valueConverter);
+            }
+
+            return null;
+        }
+
+        Converter converter = registry.get(clazz);
+
+        // we're outta here if we got one.
+        if (converter != null) {
+            return converter;
+        }
+
+        final Class[] declaredClasses = clazz.getDeclaredClasses();
+        for (final Class<?> declaredClass : declaredClasses) {
+            if (Converter.class.isAssignableFrom(declaredClass)) {
+                try {
+                    converter = (Converter) declaredClass.newInstance();
+                    register(converter);
+
+                    // try to get the converter from the registry... the converter
+                    // created above may have been for another class
+                    converter = registry.get(clazz);
+                    if (converter != null) {
+                        return converter;
+                    }
+                } catch (Exception e) {
+                    // no-op
+                }
+
+            }
+        }
+
+        // nothing found
+        return null;
+    }
+
+    /**
+     * Locate a property editor for qiven class of object.
+     *
+     * @param type The target object class of the property.
+     * @return The resolved editor, if any.  Returns null if a suitable editor
+     *         could not be located.
+     */
+    protected PropertyEditor findEditor(final Type type) {
+        if (type == null) throw new NullPointerException("type is null");
+
+        Class clazz = toClass(type);
+
+        // try to locate this directly from the editor manager first.
+        PropertyEditor editor = PropertyEditorManager.findEditor(clazz);
+
+        // we're outta here if we got one.
+        if (editor != null) {
+            return editor;
+        }
+
+
+        // it's possible this was a request for an array class.  We might not
+        // recognize the array type directly, but the component type might be
+        // resolvable
+        if (clazz.isArray() && !clazz.getComponentType().isArray()) {
+            // do a recursive lookup on the base type
+            editor = findEditor(clazz.getComponentType());
+            // if we found a suitable editor for the base component type,
+            // wrapper this in an array adaptor for real use
+            if (editor != null) {
+                return new ArrayConverter(clazz, editor);
+            }
+        }
+
+        // nothing found
+        return null;
+    }
+
+    /**
+     * Release closeable converters.
+     */
+    public void close() {
+        for (final Converter converter : registry.values()) {
+            if (Closeable.class.isInstance(converter)) {
+                Closeable.class.cast(converter);
+            }
+        }
+        registry.clear();
+    }
+}
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java
index bd27b11..dda3bf4 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java
@@ -16,29 +16,11 @@
  */
 package org.apache.xbean.propertyeditor;
 
-import static org.apache.xbean.recipe.RecipeHelper.getTypeParameters;
-import static org.apache.xbean.recipe.RecipeHelper.*;
-import org.apache.xbean.recipe.RecipeHelper;
-
-import java.beans.PropertyEditor;
 import java.beans.PropertyEditorManager;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Collection;
-import java.util.SortedSet;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.LinkedHashSet;
-import java.util.ArrayList;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.LinkedHashMap;
-import java.util.Map.Entry;
-import java.util.concurrent.ConcurrentMap;
-import java.util.concurrent.ConcurrentHashMap;
 import java.lang.reflect.Type;
 
+import com.sun.org.apache.regexp.internal.RE;
+
 /**
  * The property editor manager.  This orchestrates Geronimo usage of
  * property editors, allowing additional search paths to be added and
@@ -46,91 +28,27 @@
  *
  * @version $Rev: 6687 $
  */
+@Deprecated // this is all static and leaks, use PropertyEditorRegistry
 public class PropertyEditors {
-    private static final Map<Class, Converter> registry = Collections.synchronizedMap(new ReferenceIdentityMap());
-    private static final Map<Class, Class> PRIMITIVE_TO_WRAPPER;
-    private static final Map<Class, Class> WRAPPER_TO_PRIMITIVE;
     private static boolean registerWithVM;
-
-    /**
-     * Register all of the built in converters
-     */
-    static {
-        Map<Class, Class> map = new HashMap<Class, Class>();
-        map.put(boolean.class, Boolean.class);
-        map.put(char.class, Character.class);
-        map.put(byte.class, Byte.class);
-        map.put(short.class, Short.class);
-        map.put(int.class, Integer.class);
-        map.put(long.class, Long.class);
-        map.put(float.class, Float.class);
-        map.put(double.class, Double.class);
-        PRIMITIVE_TO_WRAPPER = Collections.unmodifiableMap(map);
-
-
-        map = new HashMap<Class, Class>();
-        map.put(Boolean.class, boolean.class);
-        map.put(Character.class, char.class);
-        map.put(Byte.class, byte.class);
-        map.put(Short.class, short.class);
-        map.put(Integer.class, int.class);
-        map.put(Long.class, long.class);
-        map.put(Float.class, float.class);
-        map.put(Double.class, double.class);
-        WRAPPER_TO_PRIMITIVE = Collections.unmodifiableMap(map);
-
-        // Explicitly register the types
-        registerConverter(new ArrayListEditor());
-        registerConverter(new BigDecimalEditor());
-        registerConverter(new BigIntegerEditor());
-        registerConverter(new BooleanEditor());
-        registerConverter(new ByteEditor());
-        registerConverter(new CharacterEditor());
-        registerConverter(new ClassEditor());
-        registerConverter(new DateEditor());
-        registerConverter(new DoubleEditor());
-        registerConverter(new FileEditor());
-        registerConverter(new FloatEditor());
-        registerConverter(new HashMapEditor());
-        registerConverter(new HashtableEditor());
-        registerConverter(new IdentityHashMapEditor());
-        registerConverter(new Inet4AddressEditor());
-        registerConverter(new Inet6AddressEditor());
-        registerConverter(new InetAddressEditor());
-        registerConverter(new IntegerEditor());
-        registerConverter(new LinkedHashMapEditor());
-        registerConverter(new LinkedHashSetEditor());
-        registerConverter(new LinkedListEditor());
-        registerConverter(new ListEditor());
-        registerConverter(new LongEditor());
-        registerConverter(new MapEditor());
-        registerConverter(new ObjectNameEditor());
-        registerConverter(new PropertiesEditor());
-        registerConverter(new SetEditor());
-        registerConverter(new ShortEditor());
-        registerConverter(new SortedMapEditor());
-        registerConverter(new SortedSetEditor());
-        registerConverter(new StringEditor());
-        registerConverter(new TreeMapEditor());
-        registerConverter(new TreeSetEditor());
-        registerConverter(new URIEditor());
-        registerConverter(new URLEditor());
-        registerConverter(new LoggerConverter());
-        registerConverter(new PatternConverter());
-        registerConverter(new JndiConverter());
-        registerConverter(new VectorEditor());
-        registerConverter(new WeakHashMapEditor());
-
-        try {
-            registerConverter(new Log4jConverter());
-        } catch (Throwable e) {
+    private static final PropertyEditorRegistry REGISTRY = new PropertyEditorRegistry() {
+        {
+            registerDefaults();
         }
 
-        try {
-            registerConverter(new CommonsLoggingConverter());
-        } catch (Throwable e) {
+        @Override
+        public Converter register(final Converter converter) {
+            final Converter register = super.register(converter);
+            if (registerWithVM) {
+                PropertyEditorManager.registerEditor(converter.getType(), converter.getClass());
+                final Class<?> sibling = Primitives.findSibling(converter.getType());
+                if (sibling != null) {
+                    PropertyEditorManager.registerEditor(sibling, converter.getClass());
+                }
+            }
+            return register;
         }
-    }
+    };
 
     /**
      * Are converters registered with the VM PropertyEditorManager.  By default
@@ -150,322 +68,51 @@
     public static void setRegisterWithVM(boolean registerWithVM) {
         if (PropertyEditors.registerWithVM != registerWithVM) {
             PropertyEditors.registerWithVM = registerWithVM;
-
-            // register all converters with the VM
             if (registerWithVM) {
-                for (Entry<Class, Converter> entry : registry.entrySet()) {
-                    Class type = entry.getKey();
-                    Converter converter = entry.getValue();
-                    PropertyEditorManager.registerEditor(type, converter.getClass());
+                for (final Converter converter : REGISTRY.getRegistry().values()) {
+                    PropertyEditorManager.registerEditor(converter.getType(), converter.getClass());
                 }
             }
         }
     }
 
     public static void registerConverter(Converter converter) {
-        if (converter == null) throw new NullPointerException("editor is null");
-        Class type = converter.getType();
-        registry.put(type, converter);
-        if (registerWithVM) {
-            PropertyEditorManager.registerEditor(type, converter.getClass());
-        }
-
-        if (PRIMITIVE_TO_WRAPPER.containsKey(type)) {
-            Class wrapperType = PRIMITIVE_TO_WRAPPER.get(type);
-            registry.put(wrapperType, converter);
-            if (registerWithVM) {
-                PropertyEditorManager.registerEditor(wrapperType, converter.getClass());
-            }
-        } else if (WRAPPER_TO_PRIMITIVE.containsKey(type)) {
-            Class primitiveType = WRAPPER_TO_PRIMITIVE.get(type);
-            registry.put(primitiveType, converter);
-            if (registerWithVM) {
-                PropertyEditorManager.registerEditor(primitiveType, converter.getClass());
-            }
-        }
+        REGISTRY.register(converter);
     }
 
-    public static boolean canConvert(String type, ClassLoader classLoader) {
-        if (type == null) throw new NullPointerException("type is null");
-        if (classLoader == null) throw new NullPointerException("classLoader is null");
+    public static boolean canConvert(final String type, final ClassLoader classLoader) {
+        if (type == null) {
+            throw new NullPointerException("type is null");
+        }
+        if (classLoader == null) {
+            throw new NullPointerException("classLoader is null");
+        }
 
-        // load using the ClassLoading utility, which also manages arrays and primitive classes.
-        Class typeClass;
         try {
-            typeClass = Class.forName(type, true, classLoader);
+            return REGISTRY.findConverter(Class.forName(type, true, classLoader)) != null;
         } catch (ClassNotFoundException e) {
             throw new PropertyEditorException("Type class could not be found: " + type);
         }
+    }
 
-        return canConvert(typeClass);
+    public static boolean canConvert(final Class<?> type) {
+        return REGISTRY.findConverter(type) != null;
+    }
+
+    public static String toString(final Object value) throws PropertyEditorException {
+        return REGISTRY.toString(value);
+    }
+
+    public static Object getValue(final String type, final String value, final ClassLoader classLoader) throws PropertyEditorException {
+        return REGISTRY.getValue(type, value, classLoader);
 
     }
 
-    public static boolean canConvert(Class type) {
-        PropertyEditor editor = findConverterOrEditor(type);
-
-        return editor != null;
+    public static Object getValue(final Type type, final String value) throws PropertyEditorException {
+        return REGISTRY.getValue(type, value);
     }
 
-    private static PropertyEditor findConverterOrEditor(Type type){
-        Converter converter = findConverter(type);
-        if (converter != null) {
-            return converter;
-        }
-
-        // fall back to a property editor
-        PropertyEditor editor = findEditor(type);
-        if (editor != null) {
-            return editor;
-        }
-
-        converter = findBuiltinConverter(type);
-        if (converter != null) {
-            return converter;
-        }
-
-        return null;
-    }
-
-    public static String toString(Object value) throws PropertyEditorException {
-        if (value == null) throw new NullPointerException("value is null");
-
-        // get an editor for this type
-        Class type = value.getClass();
-
-        PropertyEditor editor = findConverterOrEditor(type);
-
-        if (editor instanceof Converter) {
-            Converter converter = (Converter) editor;
-            return converter.toString(value);
-        }
-
-        if (editor == null) {
-            throw new PropertyEditorException("Unable to find PropertyEditor for " + type.getSimpleName());
-        }
-
-        // create the string value
-        editor.setValue(value);
-        String textValue;
-        try {
-            textValue = editor.getAsText();
-        } catch (Exception e) {
-            throw new PropertyEditorException("Error while converting a \"" + type.getSimpleName() + "\" to text " +
-                    " using the property editor " + editor.getClass().getSimpleName(), e);
-        }
-        return textValue;
-    }
-
-    public static Object getValue(String type, String value, ClassLoader classLoader) throws PropertyEditorException {
-        if (type == null) throw new NullPointerException("type is null");
-        if (value == null) throw new NullPointerException("value is null");
-        if (classLoader == null) throw new NullPointerException("classLoader is null");
-
-        // load using the ClassLoading utility, which also manages arrays and primitive classes.
-        Class typeClass;
-        try {
-            typeClass = Class.forName(type, true, classLoader);
-        } catch (ClassNotFoundException e) {
-            throw new PropertyEditorException("Type class could not be found: " + type);
-        }
-
-        return getValue(typeClass, value);
-
-    }
-
-    public static Object getValue(Type type, String value) throws PropertyEditorException {
-        if (type == null) throw new NullPointerException("type is null");
-        if (value == null) throw new NullPointerException("value is null");
-
-        PropertyEditor editor = findConverterOrEditor(type);
-
-        if (editor instanceof Converter) {
-            Converter converter = (Converter) editor;
-            return converter.toObject(value);
-        }
-
-        Class clazz = toClass(type);
-
-        if (editor == null) editor = ConstructorConverter.editor(toClass(type));
-        if (editor == null) editor = StaticFactoryConverter.editor(toClass(type));
-
-        if (editor == null) {
-            throw new PropertyEditorException("Unable to find PropertyEditor for " + clazz.getSimpleName());
-        }
-
-        editor.setAsText(value);
-        Object objectValue;
-        try {
-            objectValue = editor.getValue();
-        } catch (Exception e) {
-            throw new PropertyEditorException("Error while converting \"" + value + "\" to a " + clazz.getSimpleName() +
-                    " using the property editor " + editor.getClass().getSimpleName(), e);
-        }
-        return objectValue;
-    }
-
-    private static Converter findBuiltinConverter(Type type) {
-        if (type == null) throw new NullPointerException("type is null");
-
-        final Class clazz = toClass(type);
-
-        if (Enum.class.isAssignableFrom(clazz)){
-            return new EnumConverter(clazz);
-        }
-
-        {
-            final ConstructorConverter editor = ConstructorConverter.editor(clazz);
-            if (editor != null) return editor;
-        }
-
-        {
-            final StaticFactoryConverter editor = StaticFactoryConverter.editor(clazz);
-            if (editor != null) return editor;
-        }
-
-        return null;       
-    }
-
-    private static Converter findConverter(Type type) {
-        if (type == null) throw new NullPointerException("type is null");
-
-        Class clazz = toClass(type);
-
-
-
-        // it's possible this was a request for an array class.  We might not
-        // recognize the array type directly, but the component type might be
-        // resolvable
-        if (clazz.isArray() && !clazz.getComponentType().isArray()) {
-            // do a recursive lookup on the base type
-            PropertyEditor editor = findConverterOrEditor(clazz.getComponentType());
-            // if we found a suitable editor for the base component type,
-            // wrapper this in an array adaptor for real use
-            if (editor != null) {
-                return new ArrayConverter(clazz, editor);
-            } else {
-                return null;
-            }
-        }
-
-        if (Collection.class.isAssignableFrom(clazz)){
-            Type[] types = getTypeParameters(Collection.class, type);
-
-            Type componentType = String.class;
-            if (types != null && types.length == 1 && types[0] instanceof Class) {
-                componentType = types[0];
-            }
-
-            PropertyEditor editor = findConverterOrEditor(componentType);
-
-            if (editor != null){
-                if (RecipeHelper.hasDefaultConstructor(clazz)) {
-                    return new GenericCollectionConverter(clazz, editor);
-                } else if (SortedSet.class.isAssignableFrom(clazz)) {
-                    return new GenericCollectionConverter(TreeSet.class, editor);
-                } else if (Set.class.isAssignableFrom(clazz)) {
-                    return new GenericCollectionConverter(LinkedHashSet.class, editor);
-                } else {
-                    return new GenericCollectionConverter(ArrayList.class, editor);
-                }
-            }
-
-            return null;
-        }
-
-        if (Map.class.isAssignableFrom(clazz)){
-            Type[] types = getTypeParameters(Map.class, type);
-
-            Type keyType = String.class;
-            Type valueType = String.class;
-            if (types != null && types.length == 2 && types[0] instanceof Class && types[1] instanceof Class) {
-                keyType = types[0];
-                valueType = types[1];
-            }
-
-            PropertyEditor keyConverter = findConverterOrEditor(keyType);
-            PropertyEditor valueConverter = findConverterOrEditor(valueType);
-
-            if (keyConverter != null && valueConverter != null){
-                if (RecipeHelper.hasDefaultConstructor(clazz)) {
-                    return new GenericMapConverter(clazz, keyConverter, valueConverter);
-                } else if (SortedMap.class.isAssignableFrom(clazz)) {
-                    return new GenericMapConverter(TreeMap.class, keyConverter, valueConverter);
-                } else if (ConcurrentMap.class.isAssignableFrom(clazz)) {
-                    return new GenericMapConverter(ConcurrentHashMap.class, keyConverter, valueConverter);
-                } else {
-                    return new GenericMapConverter(LinkedHashMap.class, keyConverter, valueConverter);
-                }
-            }
-
-            return null;
-        }
-
-        Converter converter = registry.get(clazz);
-
-        // we're outta here if we got one.
-        if (converter != null) {
-            return converter;
-        }
-
-        Class[] declaredClasses = clazz.getDeclaredClasses();
-        for (Class declaredClass : declaredClasses) {
-            if (Converter.class.isAssignableFrom(declaredClass)) {
-                try {
-                    converter = (Converter) declaredClass.newInstance();
-                    registerConverter(converter);
-
-                    // try to get the converter from the registry... the converter
-                    // created above may have been for another class
-                    converter = registry.get(clazz);
-                    if (converter != null) {
-                        return converter;
-                    }
-                } catch (Exception e) {
-                }
-
-            }
-        }
-
-        // nothing found
-        return null;
-    }
-
-    /**
-     * Locate a property editor for qiven class of object.
-     *
-     * @param type The target object class of the property.
-     * @return The resolved editor, if any.  Returns null if a suitable editor
-     *         could not be located.
-     */
-    private static PropertyEditor findEditor(Type type) {
-        if (type == null) throw new NullPointerException("type is null");
-
-        Class clazz = toClass(type);
-
-        // try to locate this directly from the editor manager first.
-        PropertyEditor editor = PropertyEditorManager.findEditor(clazz);
-
-        // we're outta here if we got one.
-        if (editor != null) {
-            return editor;
-        }
-
-
-        // it's possible this was a request for an array class.  We might not
-        // recognize the array type directly, but the component type might be
-        // resolvable
-        if (clazz.isArray() && !clazz.getComponentType().isArray()) {
-            // do a recursive lookup on the base type
-            editor = findEditor(clazz.getComponentType());
-            // if we found a suitable editor for the base component type,
-            // wrapper this in an array adaptor for real use
-            if (editor != null) {
-                return new ArrayConverter(clazz, editor);
-            }
-        }
-
-        // nothing found
-        return null;
+    public static PropertyEditorRegistry registry() {
+        return REGISTRY;
     }
 }
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PrototypeArrayConverter.java b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PrototypeArrayConverter.java
new file mode 100644
index 0000000..b84988d
--- /dev/null
+++ b/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PrototypeArrayConverter.java
@@ -0,0 +1,57 @@
+/**
+ * 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.xbean.propertyeditor;
+
+import java.beans.PropertyEditor;
+import java.beans.PropertyEditorManager;
+import java.lang.reflect.Array;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * Adapter for editing array types.
+ *
+ * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $
+ */
+public final class PrototypeArrayConverter extends AbstractCollectionConverter {
+    private static final PropertyEditor MOCK_EDITOR = new StringEditor();
+
+    public PrototypeArrayConverter(Class type) {
+        super(type, MOCK_EDITOR);
+        if (!type.isArray()) {
+            throw new IllegalArgumentException("type is not an array " + type.getSimpleName());
+        }
+        if (type.getComponentType().isArray()) {
+            throw new IllegalArgumentException("type is a multi-dimensional array " + type.getSimpleName());
+        }
+    }
+
+    @Override
+    protected PropertyEditor getEditor() {
+        return PropertyEditorManager.findEditor(getType());
+    }
+
+    protected Object createCollection(final List list) {
+        final Object array = Array.newInstance(getType().getComponentType(), list.size());
+        for (final ListIterator iterator = list.listIterator(); iterator.hasNext();) {
+            final Object item = iterator.next();
+            int index = iterator.previousIndex();
+            Array.set(array, index, item);
+        }
+        return array;
+    }
+}
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/recipe/ArrayRecipe.java b/xbean-reflect/src/main/java/org/apache/xbean/recipe/ArrayRecipe.java
index 8fb216a..f824f38 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/recipe/ArrayRecipe.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/recipe/ArrayRecipe.java
@@ -24,6 +24,8 @@
 import java.util.EnumSet;
 import java.util.List;
 
+import org.apache.xbean.propertyeditor.PropertyEditorRegistry;
+
 /**
  * @version $Rev$ $Date$
  */
@@ -31,6 +33,7 @@
     private final List<Object> list;
     private String typeName;
     private Class typeClass;
+    private PropertyEditorRegistry registry;
     private final EnumSet<Option> options = EnumSet.noneOf(Option.class);
 
     public ArrayRecipe() {
@@ -56,6 +59,10 @@
         list = new ArrayList<Object>(collectionRecipe.list);
     }
 
+    public void setRegistry(final PropertyEditorRegistry registry) {
+        this.registry = registry;
+    }
+
     public void allow(Option option) {
         options.add(option);
     }
@@ -108,7 +115,7 @@
 
         int index = 0;
         for (Object value : list) {
-            value = RecipeHelper.convert(type, value, refAllowed);
+            value = RecipeHelper.convert(type, value, refAllowed, registry);
             
             if (value instanceof Reference) {
                 Reference reference = (Reference) value;
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/recipe/CollectionRecipe.java b/xbean-reflect/src/main/java/org/apache/xbean/recipe/CollectionRecipe.java
index 5ce24ec..d9f124d 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/recipe/CollectionRecipe.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/recipe/CollectionRecipe.java
@@ -29,6 +29,8 @@
 import java.util.SortedSet;
 import java.util.TreeSet;
 
+import org.apache.xbean.propertyeditor.PropertyEditorRegistry;
+
 /**
  * @version $Rev: 6685 $ $Date: 2005-12-28T00:29:37.967210Z $
  */
@@ -36,6 +38,7 @@
     private final List<Object> list;
     private String typeName;
     private Class typeClass;
+    private PropertyEditorRegistry registry;
     private final EnumSet<Option> options = EnumSet.noneOf(Option.class);
 
     public CollectionRecipe() {
@@ -79,6 +82,10 @@
         list = new ArrayList<Object>(collectionRecipe.list);
     }
 
+    public void setRegistry(final PropertyEditorRegistry registry) {
+        this.registry = registry;
+    }
+
     public void allow(Option option) {
         options.add(option);
     }
@@ -145,7 +152,7 @@
 
         int index = 0;
         for (Object value : list) {
-            value = RecipeHelper.convert(componentType, value, refAllowed);
+            value = RecipeHelper.convert(componentType, value, refAllowed, registry);
 
             if (value instanceof Reference) {
                 Reference reference = (Reference) value;
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/recipe/MapRecipe.java b/xbean-reflect/src/main/java/org/apache/xbean/recipe/MapRecipe.java
index 9c88c46..874ecc3 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/recipe/MapRecipe.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/recipe/MapRecipe.java
@@ -32,6 +32,8 @@
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 
+import org.apache.xbean.propertyeditor.PropertyEditorRegistry;
+
 /**
  * @version $Rev: 6687 $ $Date: 2005-12-28T21:08:56.733437Z $
  */
@@ -39,6 +41,7 @@
     private final List<Object[]> entries;
     private String typeName;
     private Class typeClass;
+    private PropertyEditorRegistry registry;
     private final EnumSet<Option> options = EnumSet.noneOf(Option.class);
 
     public MapRecipe() {
@@ -81,6 +84,10 @@
         entries = new ArrayList<Object[]>(mapRecipe.entries);
     }
 
+    public void setRegistry(final PropertyEditorRegistry registry) {
+        this.registry = registry;
+    }
+
     public void allow(Option option){
         options.add(option);
     }
@@ -163,8 +170,8 @@
         // add map entries
         boolean refAllowed = options.contains(Option.LAZY_ASSIGNMENT);
         for (Object[] entry : entries) {
-            Object key = RecipeHelper.convert(keyType, entry[0], refAllowed);
-            Object value = RecipeHelper.convert(valueType, entry[1], refAllowed);
+            Object key = RecipeHelper.convert(keyType, entry[0], refAllowed, registry);
+            Object value = RecipeHelper.convert(valueType, entry[1], refAllowed, registry);
 
             if (key instanceof Reference) {
                 // when the key reference and optional value reference are both resolved
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/recipe/ObjectRecipe.java b/xbean-reflect/src/main/java/org/apache/xbean/recipe/ObjectRecipe.java
index aed13db..499bd19 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/recipe/ObjectRecipe.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/recipe/ObjectRecipe.java
@@ -29,6 +29,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+
+import org.apache.xbean.propertyeditor.PropertyEditorRegistry;
 import org.apache.xbean.recipe.ReflectionUtil.*;
 
 /**
@@ -40,6 +42,7 @@
     private String factoryMethod;
     private List<String> constructorArgNames;
     private List<Class<?>> constructorArgTypes;
+    private PropertyEditorRegistry registry;
     private final LinkedHashMap<Property,Object> properties = new LinkedHashMap<Property,Object>();
     private final EnumSet<Option> options = EnumSet.of(Option.FIELD_INJECTION);
     private final Map<String,Object> unsetProperties = new LinkedHashMap<String,Object>();
@@ -362,6 +365,10 @@
         return null;
     }
 
+    public void setRegistry(final PropertyEditorRegistry registry) {
+        this.registry = registry;
+    }
+
     private void setProperties(Map<Property, Object> propertyValues, Object instance, Class clazz) {
         // set remaining properties
         for (Map.Entry<Property, Object> entry : RecipeHelper.prioritizeProperties(propertyValues)) {
@@ -378,20 +385,20 @@
         List<Member> members = new ArrayList<Member>();
         try {
             if (propertyName instanceof SetterProperty){
-                List<Method> setters = ReflectionUtil.findAllSetters(clazz, propertyName.name, propertyValue, options);
+                List<Method> setters = ReflectionUtil.findAllSetters(clazz, propertyName.name, propertyValue, options, registry);
                 for (Method setter : setters) {
                     MethodMember member = new MethodMember(setter);
                     members.add(member);
                 }
             } else if (propertyName instanceof FieldProperty){
-                FieldMember member = new FieldMember(ReflectionUtil.findField(clazz, propertyName.name, propertyValue, options));
+                FieldMember member = new FieldMember(ReflectionUtil.findField(clazz, propertyName.name, propertyValue, options, registry));
                 members.add(member);
             } else if (propertyName instanceof AutoMatchProperty){
                 MissingAccessorException noField = null;
                 if (options.contains(Option.FIELD_INJECTION)) {
                     List<Field> fieldsByType = null;
                     try {
-                        fieldsByType = ReflectionUtil.findAllFieldsByType(clazz, propertyValue, options);
+                        fieldsByType = ReflectionUtil.findAllFieldsByType(clazz, propertyValue, options, registry);
                         FieldMember member = new FieldMember(fieldsByType.iterator().next());
                         members.add(member);
                     } catch (MissingAccessorException e) {
@@ -412,7 +419,7 @@
                 if (members.isEmpty()) {
                     List<Method> settersByType;
                     try {
-                        settersByType = ReflectionUtil.findAllSettersByType(clazz, propertyValue, options);
+                        settersByType = ReflectionUtil.findAllSettersByType(clazz, propertyValue, options, registry);
                         MethodMember member = new MethodMember(settersByType.iterator().next());
                         members.add(member);
                     } catch (MissingAccessorException noSetter) {
@@ -450,7 +457,7 @@
                         throw new ConstructionException("No getter for " + names[i] + " property");
                     }
                 }
-                List<Method> setters = ReflectionUtil.findAllSetters(clazz, names[names.length - 1], propertyValue, options);
+                List<Method> setters = ReflectionUtil.findAllSetters(clazz, names[names.length - 1], propertyValue, options, registry);
                 for (Method setter : setters) {
                     MethodMember member = new MethodMember(setter);
                     members.add(member);
@@ -459,7 +466,7 @@
                 // add setter members
                 MissingAccessorException noSetter = null;
                 try {
-                    List<Method> setters = ReflectionUtil.findAllSetters(clazz, propertyName.name, propertyValue, options);
+                    List<Method> setters = ReflectionUtil.findAllSetters(clazz, propertyName.name, propertyValue, options, registry);
                     for (Method setter : setters) {
                         MethodMember member = new MethodMember(setter);
                         members.add(member);
@@ -473,7 +480,7 @@
 
                 if (options.contains(Option.FIELD_INJECTION)) {
                     try {
-                        FieldMember member = new FieldMember(ReflectionUtil.findField(clazz, propertyName.name, propertyValue, options));
+                        FieldMember member = new FieldMember(ReflectionUtil.findField(clazz, propertyName.name, propertyValue, options, registry));
                         members.add(member);
                     } catch (MissingAccessorException noField) {
                         if (members.isEmpty()) {
@@ -494,7 +501,7 @@
         for (Member member : members) {
             // convert the value to type of setter/field
             try {
-                propertyValue = RecipeHelper.convert(member.getType(), propertyValue, false);
+                propertyValue = RecipeHelper.convert(member.getType(), propertyValue, false, registry);
             } catch (Exception e) {
                 // save off first conversion exception, in case setting failed
                 if (conversionException == null) {
@@ -582,14 +589,14 @@
             Object value;
             if (propertyValues.containsKey(name)) {
                 value = propertyValues.remove(name);
-                if (!RecipeHelper.isInstance(type, value) && !RecipeHelper.isConvertable(type, value)) {
+                if (!RecipeHelper.isInstance(type, value) && !RecipeHelper.isConvertable(type, value, registry)) {
                     throw new ConstructionException("Invalid and non-convertable constructor parameter type: " +
                             "name=" + name + ", " +
                             "index=" + i + ", " +
                             "expected=" + RecipeHelper.toClass(type).getName() + ", " +
                             "actual=" + (value == null ? "null" : value.getClass().getName()));
                 }
-                value = RecipeHelper.convert(type, value, false);
+                value = RecipeHelper.convert(type, value, false, registry);
             } else {
                 value = getDefaultValue(RecipeHelper.toClass(type));
             }
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java b/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java
index c1fded0..77224b9 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java
@@ -30,6 +30,8 @@
 import java.util.List;
 import java.util.Map;
 
+import org.apache.xbean.propertyeditor.Primitives;
+import org.apache.xbean.propertyeditor.PropertyEditorRegistry;
 import org.apache.xbean.propertyeditor.PropertyEditors;
 
 /**
@@ -124,12 +126,12 @@
         return instance == null || type.isInstance(instance);
     }
 
-    public static boolean isConvertable(Type type, Object propertyValue) {
+    public static boolean isConvertable(Type type, Object propertyValue, PropertyEditorRegistry registry) {
         if (propertyValue instanceof Recipe) {
             Recipe recipe = (Recipe) propertyValue;
             return recipe.canCreate(type);
         }
-        return (propertyValue instanceof String && PropertyEditors.canConvert(toClass(type)))
+        return (propertyValue instanceof String && (registry == null ? PropertyEditors.registry() : registry).findConverter(toClass(type)) != null)
             || (type == String.class && char[].class.isInstance(propertyValue));
     }
 
@@ -138,31 +140,13 @@
 
         if (expected.isPrimitive()) {
             // verify actual is the correct wrapper type
-            if (expected.equals(boolean.class)) {
-                return actual.equals(Boolean.class);
-            } else if (expected.equals(char.class)) {
-                return actual.equals(Character.class);
-            } else if (expected.equals(byte.class)) {
-                return actual.equals(Byte.class);
-            } else if (expected.equals(short.class)) {
-                return actual.equals(Short.class);
-            } else if (expected.equals(int.class)) {
-                return actual.equals(Integer.class);
-            } else if (expected.equals(long.class)) {
-                return actual.equals(Long.class);
-            } else if (expected.equals(float.class)) {
-                return actual.equals(Float.class);
-            } else if (expected.equals(double.class)) {
-                return actual.equals(Double.class);
-            } else {
-                throw new AssertionError("Invalid primitve type: " + expected);
-            }
+            return actual.equals(Primitives.toWrapper(expected));
         }
 
         return expected.isAssignableFrom(actual);
     }
 
-    public static Object convert(Type expectedType, Object value, boolean lazyRefAllowed) {
+    public static Object convert(Type expectedType, Object value, boolean lazyRefAllowed, PropertyEditorRegistry registry) {
         if (value instanceof Recipe) {
             Recipe recipe = (Recipe) value;
             value = recipe.create(expectedType, lazyRefAllowed);
@@ -178,7 +162,7 @@
 
         if (value instanceof String && (expectedType != Object.class)) {
             String stringValue = (String) value;
-            value = PropertyEditors.getValue(expectedType, stringValue);
+            value = (registry == null ? PropertyEditors.registry() : registry).getValue(expectedType, stringValue);
         }
         return value;
     }
@@ -215,9 +199,8 @@
         } else if (type instanceof ParameterizedType) {
             ParameterizedType parameterizedType = (ParameterizedType) type;
             return toClass(parameterizedType.getRawType());
-        } else {
-            return Object.class;
         }
+        return Object.class;
     }
 
     public static class RecipeComparator implements Comparator<Object> {
diff --git a/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java b/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java
index d205985..57c465c 100644
--- a/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java
+++ b/xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java
@@ -37,6 +37,8 @@
 
 import static org.apache.xbean.recipe.RecipeHelper.isAssignableFrom;
 
+import org.apache.xbean.propertyeditor.PropertyEditorRegistry;
+
 public final class ReflectionUtil {
     private static final class ParameterLoader {
         private static ParameterNameLoader PARAMETER_NAME_LOADER;
@@ -65,7 +67,8 @@
         }
     }
     
-    public static Field findField(Class typeClass, String propertyName, Object propertyValue, Set<Option> options) {
+    public static Field findField(Class typeClass, String propertyName, Object propertyValue, Set<Option> options,
+                                  PropertyEditorRegistry registry) {
         if (typeClass == null) throw new NullPointerException("typeClass is null");
         if (propertyName == null) throw new NullPointerException("name is null");
         if (propertyName.length() == 0) throw new IllegalArgumentException("name is an empty string");
@@ -135,7 +138,7 @@
                 }
 
 
-                if (!RecipeHelper.isInstance(fieldType, propertyValue) && !RecipeHelper.isConvertable(fieldType, propertyValue)) {
+                if (!RecipeHelper.isInstance(fieldType, propertyValue) && !RecipeHelper.isConvertable(fieldType, propertyValue, registry)) {
                     if (matchLevel < 5) {
                         matchLevel = 5;
                         missException = new MissingAccessorException((propertyValue == null ? "null" : propertyValue.getClass().getName()) + " can not be assigned or converted to " +
@@ -229,8 +232,9 @@
         return null;
     }
     
-    public static Method findSetter(Class typeClass, String propertyName, Object propertyValue, Set<Option> options) {
-        List<Method> setters = findAllSetters(typeClass, propertyName, propertyValue, options);
+    public static Method findSetter(Class typeClass, String propertyName, Object propertyValue, Set<Option> options,
+                                    PropertyEditorRegistry registry) {
+        List<Method> setters = findAllSetters(typeClass, propertyName, propertyValue, options, registry);
         return setters.get(0);
     }
 
@@ -244,7 +248,8 @@
      * @param options controls which setters are considered valid
      * @return the valid setters; never null or empty
      */
-    public static List<Method> findAllSetters(Class typeClass, String propertyName, Object propertyValue, Set<Option> options) {
+    public static List<Method> findAllSetters(Class typeClass, String propertyName, Object propertyValue, Set<Option> options,
+                                              PropertyEditorRegistry registry) {
         if (typeClass == null) throw new NullPointerException("typeClass is null");
         if (propertyName == null) throw new NullPointerException("name is null");
         if (propertyName.length() == 0) throw new IllegalArgumentException("name is an empty string");
@@ -349,7 +354,7 @@
                 }
 
 
-                if (!RecipeHelper.isInstance(methodParameterType, propertyValue) && !RecipeHelper.isConvertable(methodParameterType, propertyValue)) {
+                if (!RecipeHelper.isInstance(methodParameterType, propertyValue) && !RecipeHelper.isConvertable(methodParameterType, propertyValue, registry)) {
                     if (matchLevel < 5) {
                         matchLevel = 5;
                         missException = new MissingAccessorException((propertyValue == null ? "null" : propertyValue.getClass().getName()) + " can not be assigned or converted to " +
@@ -396,7 +401,8 @@
         }
     }
 
-    public static List<Field> findAllFieldsByType(Class typeClass, Object propertyValue, Set<Option> options) {
+    public static List<Field> findAllFieldsByType(Class typeClass, Object propertyValue, Set<Option> options,
+                                                  PropertyEditorRegistry registry) {
         if (typeClass == null) throw new NullPointerException("typeClass is null");
         if (options == null) options = EnumSet.noneOf(Option.class);
 
@@ -416,7 +422,7 @@
         LinkedList<Field> validFields = new LinkedList<Field>();
         for (Field field : fields) {
             Class fieldType = field.getType();
-            if (RecipeHelper.isInstance(fieldType, propertyValue) || RecipeHelper.isConvertable(fieldType, propertyValue)) {
+            if (RecipeHelper.isInstance(fieldType, propertyValue) || RecipeHelper.isConvertable(fieldType, propertyValue, registry)) {
                 if (!allowPrivate && !Modifier.isPublic(field.getModifiers())) {
                     if (matchLevel < 4) {
                         matchLevel = 4;
@@ -475,7 +481,8 @@
             throw new MissingAccessorException(buffer.toString(), -1);
         }
     }
-    public static List<Method> findAllSettersByType(Class typeClass, Object propertyValue, Set<Option> options) {
+    public static List<Method> findAllSettersByType(Class typeClass, Object propertyValue, Set<Option> options,
+                                                    PropertyEditorRegistry registry) {
         if (typeClass == null) throw new NullPointerException("typeClass is null");
         if (options == null) options = EnumSet.noneOf(Option.class);
 
@@ -489,7 +496,7 @@
         List<Method> methods = new ArrayList<Method>(Arrays.asList(typeClass.getMethods()));
         methods.addAll(Arrays.asList(typeClass.getDeclaredMethods()));
         for (Method method : methods) {
-            if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && (RecipeHelper.isInstance(method.getParameterTypes()[0], propertyValue) || RecipeHelper.isConvertable(method.getParameterTypes()[0], propertyValue))) {
+            if (method.getName().startsWith("set") && method.getParameterTypes().length == 1 && (RecipeHelper.isInstance(method.getParameterTypes()[0], propertyValue) || RecipeHelper.isConvertable(method.getParameterTypes()[0], propertyValue, registry))) {
                 if (method.getReturnType() != Void.TYPE) {
                     if (matchLevel < 2) {
                         matchLevel = 2;
diff --git a/xbean-reflect/src/test/java/org/apache/xbean/recipe/RecipeHelperTest.java b/xbean-reflect/src/test/java/org/apache/xbean/recipe/RecipeHelperTest.java
index 21efbe6..712acc5 100644
--- a/xbean-reflect/src/test/java/org/apache/xbean/recipe/RecipeHelperTest.java
+++ b/xbean-reflect/src/test/java/org/apache/xbean/recipe/RecipeHelperTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.xbean.recipe;
 
+import org.apache.xbean.propertyeditor.PropertyEditors;
 import org.junit.Test;
 
 import static org.junit.Assert.assertArrayEquals;
@@ -25,13 +26,13 @@
 public class RecipeHelperTest {
     @Test
     public void stringCharArrayIsConvertable() {
-        assertTrue(RecipeHelper.isConvertable(char[].class, "foo"));
-        assertTrue(RecipeHelper.isConvertable(String.class, "foo".toCharArray()));
+        assertTrue(RecipeHelper.isConvertable(char[].class, "foo", PropertyEditors.registry()));
+        assertTrue(RecipeHelper.isConvertable(String.class, "foo".toCharArray(), PropertyEditors.registry()));
     }
 
     @Test
     public void stringCharArrayConvert() {
-        assertArrayEquals("foo".toCharArray(), char[].class.cast(RecipeHelper.convert(char[].class, "foo", false)));
-        assertEquals("foo", RecipeHelper.convert(String.class, "foo".toCharArray(), false));
+        assertArrayEquals("foo".toCharArray(), char[].class.cast(RecipeHelper.convert(char[].class, "foo", false, PropertyEditors.registry())));
+        assertEquals("foo", RecipeHelper.convert(String.class, "foo".toCharArray(), false, PropertyEditors.registry()));
     }
 }