You are viewing a plain text version of this content. The canonical link for it is here.
Posted to xbean-scm@geronimo.apache.org by db...@apache.org on 2008/03/08 23:21:14 UTC
svn commit: r635096 - in /geronimo/xbean/trunk/xbean-reflect/src:
main/java/org/apache/xbean/propertyeditor/
main/java/org/apache/xbean/recipe/ test/java/org/apache/xbean/recipe/
Author: dblevins
Date: Sat Mar 8 14:20:49 2008
New Revision: 635096
URL: http://svn.apache.org/viewvc?rev=635096&view=rev
Log:
XBEAN-103: Support for Generic Collections and Maps
Added:
geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericCollectionConverter.java
geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericMapConverter.java
geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/GenericCollectionsTest.java
Modified:
geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java
geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/CollectionRecipe.java
geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java
Added: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericCollectionConverter.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericCollectionConverter.java?rev=635096&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericCollectionConverter.java (added)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericCollectionConverter.java Sat Mar 8 14:20:49 2008
@@ -0,0 +1,41 @@
+/**
+ * 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.util.List;
+import java.util.Collection;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class GenericCollectionConverter extends AbstractCollectionConverter {
+
+ public GenericCollectionConverter(Class type, PropertyEditor editor) {
+ super(type, editor);
+ }
+
+ protected Object createCollection(List list) {
+ try {
+ Collection collection = (Collection) getType().newInstance();
+ collection.addAll(list);
+ return collection;
+ } catch (Exception e) {
+ throw new PropertyEditorException(e);
+ }
+ }
+}
Added: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericMapConverter.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericMapConverter.java?rev=635096&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericMapConverter.java (added)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/GenericMapConverter.java Sat Mar 8 14:20:49 2008
@@ -0,0 +1,40 @@
+/**
+ * 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.util.Map;
+import java.util.Collection;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class GenericMapConverter extends AbstractMapConverter {
+ public GenericMapConverter(Class type, PropertyEditor keyEditor, PropertyEditor valueEditor) {
+ super(type, keyEditor, valueEditor);
+ }
+
+ protected Map createMap(Map data) {
+ try {
+ Map map = (Map) getType().newInstance();
+ map.putAll(data);
+ return map;
+ } catch (Exception e) {
+ throw new PropertyEditorException(e);
+ }
+ }
+}
Modified: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java?rev=635096&r1=635095&r2=635096&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/propertyeditor/PropertyEditors.java Sat Mar 8 14:20:49 2008
@@ -16,11 +16,27 @@
*/
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.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.lang.reflect.Type;
/**
* The property editor manager. This orchestrates Geronimo usage of
@@ -196,7 +212,7 @@
}
- public static Object getValue(Class type, String value) throws PropertyEditorException {
+ 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");
@@ -206,10 +222,12 @@
return converter.toObject(value);
}
+ Class clazz = toClass(type);
+
// fall back to a property editor
PropertyEditor editor = findEditor(type);
if (editor == null) {
- throw new PropertyEditorException("Unable to find PropertyEditor for " + type.getSimpleName());
+ throw new PropertyEditorException("Unable to find PropertyEditor for " + clazz.getSimpleName());
}
// create the object value
@@ -218,15 +236,86 @@
try {
objectValue = editor.getValue();
} catch (Exception e) {
- throw new PropertyEditorException("Error while converting \"" + value + "\" to a " + type.getSimpleName() +
+ throw new PropertyEditorException("Error while converting \"" + value + "\" to a " + clazz.getSimpleName() +
" using the property editor " + editor.getClass().getSimpleName(), e);
}
return objectValue;
}
- private static Converter findConverter(Class type) {
+ 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
+ Converter converter = findConverter(clazz.getComponentType());
+ // if we found a suitable editor for the base component type,
+ // wrapper this in an array adaptor for real use
+ if (converter != null) {
+ return new ArrayConverter(clazz, converter);
+ } 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];
+ }
+
+ Converter converter = findConverter(componentType);
+
+ if (converter != null){
+ if (RecipeHelper.hasDefaultConstructor(clazz)) {
+ return new GenericCollectionConverter(clazz, converter);
+ } else if (SortedSet.class.isAssignableFrom(clazz)) {
+ return new GenericCollectionConverter(TreeSet.class, converter);
+ } else if (Set.class.isAssignableFrom(clazz)) {
+ return new GenericCollectionConverter(LinkedHashSet.class, converter);
+ } else {
+ return new GenericCollectionConverter(ArrayList.class, converter);
+ }
+ }
+
+ 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];
+ }
+
+ Converter keyConverter = findConverter(keyType);
+ 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);
+ } else {
+ return new GenericMapConverter(LinkedHashMap.class, keyConverter, valueConverter);
+ }
+ }
+
+ return null;
+ }
Converter converter = (Converter) registry.get(type);
// we're outta here if we got one.
@@ -234,7 +323,7 @@
return converter;
}
- Class[] declaredClasses = type.getDeclaredClasses();
+ Class[] declaredClasses = clazz.getDeclaredClasses();
for (int i = 0; i < declaredClasses.length; i++) {
Class declaredClass = declaredClasses[i];
if (Converter.class.isAssignableFrom(declaredClass)) {
@@ -244,7 +333,7 @@
// try to get the converter from the registry... the converter
// created above may have been for another class
- converter = (Converter) registry.get(type);
+ converter = (Converter) registry.get(clazz);
if (converter != null) {
return converter;
}
@@ -254,19 +343,6 @@
}
}
- // 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 (type.isArray() && !type.getComponentType().isArray()) {
- // do a recursive lookup on the base type
- converter = findConverter(type.getComponentType());
- // if we found a suitable editor for the base component type,
- // wrapper this in an array adaptor for real use
- if (converter != null) {
- return new ArrayConverter(type, converter);
- }
- }
-
// nothing found
return null;
}
@@ -278,27 +354,30 @@
* @return The resolved editor, if any. Returns null if a suitable editor
* could not be located.
*/
- private static PropertyEditor findEditor(Class type) {
+ 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(type);
+ 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 (type.isArray() && !type.getComponentType().isArray()) {
+ if (clazz.isArray() && !clazz.getComponentType().isArray()) {
// do a recursive lookup on the base type
- editor = findEditor(type.getComponentType());
+ 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(type, editor);
+ return new ArrayConverter(clazz, editor);
}
}
Modified: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/CollectionRecipe.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/CollectionRecipe.java?rev=635096&r1=635095&r2=635096&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/CollectionRecipe.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/CollectionRecipe.java Sat Mar 8 14:20:49 2008
@@ -135,8 +135,8 @@
}
// get component type
- Type componentType = Object.class;
Type[] typeParameters = RecipeHelper.getTypeParameters(Collection.class, expectedType);
+ Type componentType = Object.class;
if (typeParameters != null && typeParameters.length == 1 && typeParameters[0] instanceof Class) {
componentType = typeParameters[0];
}
Modified: geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java?rev=635096&r1=635095&r2=635096&view=diff
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java (original)
+++ geronimo/xbean/trunk/xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java Sat Mar 8 14:20:49 2008
@@ -57,6 +57,9 @@
if (!Modifier.isPublic(type.getModifiers())) {
return false;
}
+ if (Modifier.isAbstract(type.getModifiers())) {
+ return false;
+ }
Constructor[] constructors = type.getConstructors();
for (Constructor constructor : constructors) {
if (Modifier.isPublic(constructor.getModifiers()) &&
@@ -166,7 +169,7 @@
if (value instanceof String && (expectedType != Object.class)) {
String stringValue = (String) value;
- value = PropertyEditors.getValue(toClass(expectedType), stringValue);
+ value = PropertyEditors.getValue(expectedType, stringValue);
}
return value;
}
Added: geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/GenericCollectionsTest.java
URL: http://svn.apache.org/viewvc/geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/GenericCollectionsTest.java?rev=635096&view=auto
==============================================================================
--- geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/GenericCollectionsTest.java (added)
+++ geronimo/xbean/trunk/xbean-reflect/src/test/java/org/apache/xbean/recipe/GenericCollectionsTest.java Sat Mar 8 14:20:49 2008
@@ -0,0 +1,188 @@
+/**
+ * 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.recipe;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Collection;
+import java.net.URI;
+
+/**
+ * @version $Rev$ $Date$
+ */
+public class GenericCollectionsTest extends TestCase {
+
+ public void test() throws Exception {
+ Something expected = new Something();
+
+ expected.plainList = new ArrayList();
+ expected.plainList.add("red");
+ expected.plainList.add("green");
+ expected.plainList.add("blue");
+
+ expected.plainMap = new LinkedHashMap();
+ expected.plainMap.put("red","rojo");
+ expected.plainMap.put("green","verde");
+ expected.plainMap.put("blue","azul");
+
+ expected.listOfClass = new ArrayList<Class>();
+ expected.listOfClass.add(Red.class);
+ expected.listOfClass.add(Green.class);
+ expected.listOfClass.add(Blue.class);
+
+ expected.listOfURI = new ArrayList<URI>();
+ expected.listOfURI.add(new URI("red://rojo"));
+ expected.listOfURI.add(new URI("green://verde"));
+ expected.listOfURI.add(new URI("blue://azul"));
+
+ expected.mapOfClass = new LinkedHashMap<String, Class>();
+ expected.mapOfClass.put("Rojo", Red.class);
+ expected.mapOfClass.put("Verde", Green.class);
+ expected.mapOfClass.put("Azul", Blue.class);
+
+ expected.mapOfURI = new LinkedHashMap<URI, String>();
+ expected.mapOfURI.put(new URI("red://rojo"), "Rojo");
+ expected.mapOfURI.put(new URI("green://verde"), "Verde");
+ expected.mapOfURI.put(new URI("blue://azul"), "Azul");
+
+ expected.setOfClass = new LinkedHashSet<Class>();
+ expected.setOfClass.add(Red.class);
+ expected.setOfClass.add(Green.class);
+ expected.setOfClass.add(Blue.class);
+
+ expected.setOfURI = new LinkedHashSet();
+ expected.setOfURI.add(new URI("red://rojo"));
+ expected.setOfURI.add(new URI("green://verde"));
+ expected.setOfURI.add(new URI("blue://azul"));
+
+
+ ObjectRecipe recipe = new ObjectRecipe(Something.class);
+ recipe.setProperty("plainList", toString(expected.plainList));
+ recipe.setProperty("plainMap", toString(expected.plainMap));
+ recipe.setProperty("listOfClass", toString(expected.listOfClass));
+ recipe.setProperty("listOfURI", toString(expected.listOfURI));
+ recipe.setProperty("mapOfClass", toString(expected.mapOfClass));
+ recipe.setProperty("mapOfURI", toString(expected.mapOfURI));
+ recipe.setProperty("setOfClass", toString(expected.setOfClass));
+ recipe.setProperty("setOfURI", toString(expected.setOfURI));
+
+ Something actual = (Something) recipe.create();
+
+ assertEquals("PlainList", expected.getPlainList(), actual.getPlainList());
+ assertEquals("PlainMap", expected.getPlainMap(), actual.getPlainMap());
+ assertEquals("ListOfClass", expected.getListOfClass(), actual.getListOfClass());
+ assertEquals("ListOfURI", expected.getListOfURI(), actual.getListOfURI());
+ assertEquals("MapOfClass", expected.getMapOfClass(), actual.getMapOfClass());
+ assertEquals("MapOfURI", expected.getMapOfURI(), actual.getMapOfURI());
+ assertEquals("SetOfClass", expected.getSetOfClass(), actual.getSetOfClass());
+ assertEquals("SetOfURI", expected.getSetOfURI(), actual.getSetOfURI());
+ }
+
+ private String toString(Map m) {
+ Map<Object,Object> map = m;
+ List<String> pairs = new ArrayList<String>();
+ for (Map.Entry<Object, Object> entry : map.entrySet()) {
+ String key = toString(entry.getKey());
+ key = key.replaceAll(":","\\\\:");
+ String value = toString(entry.getValue());
+ pairs.add(key +"="+value);
+ }
+ return toString(pairs).replace(',', '\n');
+ }
+
+ private String toString(Collection collection) {
+ StringBuilder s = new StringBuilder();
+ for (Object obj : collection) {
+ s.append(toString(obj));
+ s.append(",");
+ }
+
+ s.deleteCharAt(s.length()-1);
+
+ return s.toString();
+ }
+
+ private String toString(Object obj) {
+ if (obj instanceof Class) {
+ return ((Class) obj).getName();
+ } else {
+ return obj.toString();
+ }
+ }
+
+ public static class Something {
+
+ public List plainList;
+
+ public Map plainMap;
+
+ public List<Class> listOfClass;
+
+ public List<URI> listOfURI;
+
+ public Map<String,Class> mapOfClass;
+
+ public Map<URI, String> mapOfURI;
+
+ public Set<Class> setOfClass;
+
+ public Set<URI> setOfURI;
+
+ public List getPlainList() {
+ return plainList;
+ }
+
+ public Map getPlainMap() {
+ return plainMap;
+ }
+
+ public List<Class> getListOfClass() {
+ return listOfClass;
+ }
+
+ public List<URI> getListOfURI() {
+ return listOfURI;
+ }
+
+ public Map<String, Class> getMapOfClass() {
+ return mapOfClass;
+ }
+
+ public Map<URI, String> getMapOfURI() {
+ return mapOfURI;
+ }
+
+ public Set<Class> getSetOfClass() {
+ return setOfClass;
+ }
+
+ public Set<URI> getSetOfURI() {
+ return setOfURI;
+ }
+
+ }
+
+ public static class Red {}
+ public static class Green {}
+ public static class Blue {}
+}