Index: xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java =================================================================== --- xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java (revision 704373) +++ xbean-reflect/src/main/java/org/apache/xbean/recipe/RecipeHelper.java (working copy) @@ -16,6 +16,7 @@ */ package org.apache.xbean.recipe; +import java.io.File; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.GenericArrayType; @@ -23,6 +24,8 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; +import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -125,13 +128,44 @@ } public static boolean isConvertable(Type type, Object propertyValue) { - if (propertyValue instanceof Recipe) { - Recipe recipe = (Recipe) propertyValue; + return isConvertable(type, propertyValue, true); + } + + /** + * Checks whether the specified property value can be converted to the requested data type. + * + * @param type The desired destination type, may be {@code null}. + * @param value The property value to check for a supported conversion, may be {@code null}. + * @param lenient A flag whether conversions from string-like values like {@link java.io.File} to {@link String} + * will be considered, too. + * @return {@code true} if a conversion is possible, {@code false} otherwise. + */ + public static boolean isConvertable(Type type, Object value, boolean lenient) { + if (value instanceof Recipe) { + Recipe recipe = (Recipe) value; return recipe.canCreate(type); } - return (propertyValue instanceof String && PropertyEditors.canConvert(toClass(type))); + if (lenient && !isStringLike(value)) { + return false; + } + if (!lenient && !(value instanceof String)) { + return false; + } + return PropertyEditors.canConvert(toClass(type)); } + /** + * Checks whether the specified property value has a well-known string representation. + * + * @param value The property value to check, may be {@code null}. + * @return {@code true} if invoking {@link Object#toString()} on the value delivers a well-known string + * representation, {@code false} otherwise. + */ + private static boolean isStringLike(Object value) { + return value instanceof String || value instanceof File || value instanceof Number + || value instanceof Boolean || value instanceof URL || value instanceof URI || value instanceof Character; + } + public static boolean isAssignableFrom(Class expected, Class actual) { if (expected == null) return true; @@ -167,8 +201,9 @@ value = recipe.create(expectedType, lazyRefAllowed); } - if (value instanceof String && (expectedType != Object.class)) { - String stringValue = (String) value; + if ((expectedType != Object.class && value instanceof String) + || (expectedType == String.class && isStringLike(value))) { + String stringValue = value.toString(); value = PropertyEditors.getValue(expectedType, stringValue); } return value; Index: xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java =================================================================== --- xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java (revision 704373) +++ xbean-reflect/src/main/java/org/apache/xbean/recipe/ReflectionUtil.java (working copy) @@ -337,7 +337,7 @@ LinkedList validFields = new LinkedList(); 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, false)) { if (!allowPrivate && !Modifier.isPublic(field.getModifiers())) { if (matchLevel < 4) { matchLevel = 4; @@ -410,7 +410,7 @@ List methods = new ArrayList(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, false))) { if (method.getReturnType() != Void.TYPE) { if (matchLevel < 2) { matchLevel = 2; Index: xbean-reflect/src/test/java/org/apache/xbean/recipe/Car.java =================================================================== --- xbean-reflect/src/test/java/org/apache/xbean/recipe/Car.java (revision 704373) +++ xbean-reflect/src/test/java/org/apache/xbean/recipe/Car.java (working copy) @@ -22,14 +22,17 @@ public class Car { public String make; public String model; + public String reference; public int year; - public Car(String make, String model, int year) { + public Car(String make, String model, String reference, int year) { if (make == null) throw new NullPointerException("make is null"); if (model == null) throw new NullPointerException("model is null"); + if (reference == null) throw new NullPointerException("reference is null"); this.make = make; this.model = model; + this.reference = reference; this.year = year; } @@ -41,6 +44,10 @@ return model; } + public String getReference() { + return reference; + } + public int getYear() { return year; } @@ -56,6 +63,7 @@ Car car = (Car) o; return year == car.year && + reference.equals(car.reference) && make.equals(car.make) && model.equals(car.model); } @@ -64,6 +72,7 @@ int result; result = make.hashCode(); result = 31 * result + model.hashCode(); + result = 31 * result + reference.hashCode(); result = 31 * result + year; return result; } Index: xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectRecipeTest.java =================================================================== --- xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectRecipeTest.java (revision 704373) +++ xbean-reflect/src/test/java/org/apache/xbean/recipe/ObjectRecipeTest.java (working copy) @@ -25,6 +25,7 @@ import static org.apache.xbean.recipe.Person.ConstructionCalled.PERSON_FACTORY; import static org.apache.xbean.recipe.Person.ConstructionCalled.PERSON_FACTORY_4_ARG; +import java.io.File; import java.net.URL; public class ObjectRecipeTest extends TestCase { @@ -109,15 +110,16 @@ } private void doTest(ObjectRecipe objectRecipe, Person.ConstructionCalled expectedConstruction) throws Exception { - Person expected = new Person("Joe", 21, new URL("http://www.acme.org"), new Car("Mini", "Cooper", 2008)); + Person expected = new Person("Joe", 21, new URL("http://www.acme.org"), new Car("Mini", "Cooper", "ref.pdf", 2008)); objectRecipe.setProperty("name", "Joe"); objectRecipe.setProperty("age", "21"); objectRecipe.setProperty("homePage", "http://www.acme.org"); - ObjectRecipe carRecipe = new ObjectRecipe(Car.class, new String[]{"make", "model", "year"}); + ObjectRecipe carRecipe = new ObjectRecipe(Car.class, new String[]{"make", "model", "reference", "year"}); carRecipe.setProperty("make", "Mini"); carRecipe.setProperty("model", "Cooper"); + carRecipe.setProperty("reference", new File("ref.pdf")); carRecipe.setProperty("year", "2008"); objectRecipe.setProperty("car", carRecipe); Index: xbean-reflect/src/test/java/org/apache/xbean/recipe/RecipeHelperTest.java =================================================================== --- xbean-reflect/src/test/java/org/apache/xbean/recipe/RecipeHelperTest.java (revision 0) +++ xbean-reflect/src/test/java/org/apache/xbean/recipe/RecipeHelperTest.java (revision 0) @@ -0,0 +1,68 @@ +/** + * 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 java.io.File; +import java.net.URI; +import java.net.URL; + +import junit.framework.TestCase; + +/** + * @version $Rev$ $Date$ + */ +public class RecipeHelperTest + extends TestCase +{ + + public void testIsConvertable() + throws Exception + { + assertTrue( RecipeHelper.isConvertable( String.class, "" ) ); + assertTrue( RecipeHelper.isConvertable( String.class, Character.valueOf( 'X' ) ) ); + assertTrue( RecipeHelper.isConvertable( String.class, new File( "test.txt" ) ) ); + assertTrue( RecipeHelper.isConvertable( String.class, new URI( "http://www.apache.org/" ) ) ); + assertTrue( RecipeHelper.isConvertable( String.class, new URL( "http://www.apache.org/" ) ) ); + assertTrue( RecipeHelper.isConvertable( String.class, Boolean.TRUE ) ); + assertTrue( RecipeHelper.isConvertable( String.class, Byte.valueOf( (byte) -128 ) ) ); + assertTrue( RecipeHelper.isConvertable( String.class, Short.valueOf( (short) -256 ) ) ); + assertTrue( RecipeHelper.isConvertable( String.class, Integer.valueOf( 1024 ) ) ); + assertTrue( RecipeHelper.isConvertable( String.class, Long.valueOf( 2048 ) ) ); + assertTrue( RecipeHelper.isConvertable( String.class, Float.valueOf( 1.5f ) ) ); + assertTrue( RecipeHelper.isConvertable( String.class, Double.valueOf( 2.5f ) ) ); + } + + public void testConvert() + throws Exception + { + assertEquals( "test", RecipeHelper.convert( String.class, "test", false ) ); + assertEquals( "X", RecipeHelper.convert( String.class, Character.valueOf( 'X' ), false ) ); + assertEquals( "test.txt", RecipeHelper.convert( String.class, new File( "test.txt" ), false ) ); + assertEquals( "http://www.apache.org/", RecipeHelper.convert( String.class, + new URI( "http://www.apache.org/" ), false ) ); + assertEquals( "http://www.apache.org/", RecipeHelper.convert( String.class, + new URL( "http://www.apache.org/" ), false ) ); + assertEquals( "true", RecipeHelper.convert( String.class, Boolean.TRUE, false ) ); + assertEquals( "-128", RecipeHelper.convert( String.class, Byte.valueOf( (byte) -128 ), false ) ); + assertEquals( "-256", RecipeHelper.convert( String.class, Short.valueOf( (short) -256 ), false ) ); + assertEquals( "1024", RecipeHelper.convert( String.class, Integer.valueOf( 1024 ), false ) ); + assertEquals( "2048", RecipeHelper.convert( String.class, Long.valueOf( 2048 ), false ) ); + assertEquals( "1.5", RecipeHelper.convert( String.class, Float.valueOf( 1.5f ), false ) ); + assertEquals( "2.5", RecipeHelper.convert( String.class, Double.valueOf( 2.5f ), false ) ); + } + +} Property changes on: xbean-reflect\src\test\java\org\apache\xbean\recipe\RecipeHelperTest.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native