diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/converter/NumberToCharacter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/converter/NumberToCharacter.java new file mode 100644 index 0000000000..636f450b68 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/converter/NumberToCharacter.java @@ -0,0 +1,49 @@ +/* + * Copyright 2004-2009 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.core.convert.converter; + +import org.springframework.util.NumberUtils; + +/** + * Converts from any JDK-standard Number implementation to a Character and back. + * + * Support Number classes including Byte, Short, Integer, Float, Double, Long, BigInteger, BigDecimal. This class + * delegates to {@link NumberUtils#convertNumberToTargetClass(Number, Class)} to perform the conversion. + * + * @see java.lang.Byte + * @see java.lang.Short + * @see java.lang.Integer + * @see java.lang.Long + * @see java.math.BigInteger + * @see java.lang.Float + * @see java.lang.Double + * @see java.math.BigDecimal + * @see NumberUtils + * + * @author Keith Donald + */ +public class NumberToCharacter implements SuperTwoWayConverter { + + @SuppressWarnings("unchecked") + public RT convert(Number source, Class targetClass) { + return (RT) Character.valueOf((char) source.shortValue()); + } + + public RS convertBack(Character target, Class sourceClass) { + return NumberUtils.convertNumberToTargetClass((short) target.charValue(), sourceClass); + } + +} \ No newline at end of file diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/converter/ObjectToString.java b/org.springframework.core/src/main/java/org/springframework/core/convert/converter/ObjectToString.java new file mode 100644 index 0000000000..5365bfbaa7 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/converter/ObjectToString.java @@ -0,0 +1,17 @@ +package org.springframework.core.convert.converter; + +import org.springframework.core.convert.service.DefaultConversionService; + +/** + * Simply calls {@link Object#toString()} to convert any object to a string. + * Used by the {@link DefaultConversionService} as a fallback if there are no other explicit to string converters registered. + * @author Keith Donald + */ +public class ObjectToString implements SuperConverter { + + @SuppressWarnings("unchecked") + public RT convert(Object source, Class targetClass) { + return (RT) source.toString(); + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/service/DefaultConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/service/DefaultConversionService.java index 4533f41ab5..a149217bf5 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/service/DefaultConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/service/DefaultConversionService.java @@ -20,7 +20,9 @@ import java.math.BigInteger; import java.util.Date; import java.util.Locale; +import org.springframework.core.convert.converter.NumberToCharacter; import org.springframework.core.convert.converter.NumberToNumber; +import org.springframework.core.convert.converter.ObjectToString; import org.springframework.core.convert.converter.StringToBigDecimal; import org.springframework.core.convert.converter.StringToBigInteger; import org.springframework.core.convert.converter.StringToBoolean; @@ -67,6 +69,8 @@ public class DefaultConversionService extends GenericConversionService { addConverter(new StringToLocale()); addConverter(new StringToEnum()); addConverter(new NumberToNumber()); + addConverter(new NumberToCharacter()); + addConverter(new ObjectToString()); } protected void addDefaultAliases() { diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/service/GenericConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/service/GenericConversionService.java index 982059f83f..74d19fdcc0 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/service/GenericConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/service/GenericConversionService.java @@ -193,21 +193,21 @@ public class GenericConversionService implements ConversionService { if (sourceType.isCollection()) { return new CollectionToArray(sourceType, targetType, this); } else { - throw new UnsupportedOperationException("Object to Array conversion not yet supported"); + throw new ConversionExecutorNotFoundException(sourceType, targetType, "Object to Array conversion not yet supported"); } } if (sourceType.isCollection()) { if (targetType.isCollection()) { return new CollectionToCollection(sourceType, targetType, this); } else { - throw new UnsupportedOperationException("Object to Collection conversion not yet supported"); + throw new ConversionExecutorNotFoundException(sourceType, targetType, "Object to Collection conversion not yet supported"); } } if (sourceType.isMap()) { if (targetType.isMap()) { return new MapToMap(sourceType, targetType, this); } else { - throw new UnsupportedOperationException("Object to Map conversion not yet supported"); + throw new ConversionExecutorNotFoundException(sourceType, targetType, "Object to Map conversion not yet supported"); } } Converter converter = findRegisteredConverter(sourceClass, targetType.getType()); diff --git a/org.springframework.core/src/test/java/org/springframework/core/convert/converters/DefaultConverterTests.java b/org.springframework.core/src/test/java/org/springframework/core/convert/converters/DefaultConverterTests.java index 284cbb57c9..95e4045f91 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/convert/converters/DefaultConverterTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/convert/converters/DefaultConverterTests.java @@ -8,7 +8,9 @@ import java.math.BigInteger; import java.util.Locale; import org.junit.Test; +import org.springframework.core.convert.converter.NumberToCharacter; import org.springframework.core.convert.converter.NumberToNumber; +import org.springframework.core.convert.converter.ObjectToString; import org.springframework.core.convert.converter.StringToBigDecimal; import org.springframework.core.convert.converter.StringToBigInteger; import org.springframework.core.convert.converter.StringToBoolean; @@ -154,6 +156,19 @@ public class DefaultConverterTests { } } + @Test + public void testNumberToCharacter() { + NumberToCharacter n = new NumberToCharacter(); + assertEquals(Character.valueOf('A'), n.convert(Integer.valueOf(65), Character.class)); + assertEquals(Integer.valueOf(65), n.convertBack(Character.valueOf('A'), Integer.class)); + } + + @Test + public void testObjectToString() { + ObjectToString o = new ObjectToString(); + assertEquals("3", o.convert(3, String.class)); + } + public static class CustomNumber extends Number { @Override diff --git a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java index ec42b5c85d..e7ac28f31f 100644 --- a/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java +++ b/org.springframework.expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java @@ -16,14 +16,14 @@ package org.springframework.expression.spel.support; +import org.springframework.core.convert.ConversionException; +import org.springframework.core.convert.ConversionExecutorNotFoundException; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.service.DefaultConversionService; import org.springframework.expression.EvaluationException; import org.springframework.expression.TypeConverter; import org.springframework.expression.spel.SpelException; import org.springframework.expression.spel.SpelMessages; -import org.springframework.util.ClassUtils; -import org.springframework.util.NumberUtils; /** * @author Juergen Hoeller @@ -40,84 +40,25 @@ public class StandardTypeConverter implements TypeConverter { @SuppressWarnings("unchecked") public T convertValue(Object value, Class targetType) throws EvaluationException { -// For activation when conversion service available - this replaces the rest of the method (probably...) -// return (T)convertValue(value,TypeDescriptor.valueOf(targetType)); - if (ClassUtils.isAssignableValue(targetType, value)) { - return (T) value; - } - if (String.class.equals(targetType)) { - return (T) (value != null ? value.toString() : null); - } - Class actualTargetType = ClassUtils.resolvePrimitiveIfNecessary(targetType); - if (Number.class.isAssignableFrom(actualTargetType)) { - try { - if (value instanceof String) { - return (T) NumberUtils.parseNumber(value.toString(), (Class) actualTargetType); - } - else if (value instanceof Number) { - return (T) NumberUtils.convertNumberToTargetClass((Number) value, (Class) actualTargetType); - } - } - catch (IllegalArgumentException ex) { - throw new SpelException(SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, ex.getMessage()); - } - } - if (Character.class.equals(actualTargetType)) { - if (value instanceof String) { - String str = (String) value; - if (str.length() == 1) { - return (T) new Character(str.charAt(0)); - } - } - else if (value instanceof Number) { - return (T) new Character((char) ((Number) value).shortValue()); - } - } - if (Boolean.class.equals(actualTargetType) && value instanceof String) { - String str = (String) value; - if ("true".equalsIgnoreCase(str)) { - return (T) Boolean.TRUE; - } - else if ("false".equalsIgnoreCase(str)) { - return (T) Boolean.FALSE; - } - } - throw new SpelException(SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), targetType); + return (T) convertValue(value,TypeDescriptor.valueOf(targetType)); } - @SuppressWarnings("unchecked") public Object convertValue(Object value, TypeDescriptor typeDescriptor) throws EvaluationException { -// For activation when conversion service available - this replaces the rest of the method (probably...) -// try { -// return conversionService.executeConversion(value, typeDescriptor); -// } catch (ConversionExecutorNotFoundException cenfe) { -// throw new SpelException(cenfe, SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), typeDescriptor.asString()); -// } catch (ConversionException ce) { -// throw new SpelException(ce, SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), typeDescriptor.asString()); -// } - return convertValue(value,typeDescriptor.getType()); + try { + return conversionService.executeConversion(value, typeDescriptor); + } catch (ConversionExecutorNotFoundException cenfe) { + throw new SpelException(cenfe, SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), typeDescriptor.asString()); + } catch (ConversionException ce) { + throw new SpelException(ce, SpelMessages.TYPE_CONVERSION_ERROR, value.getClass(), typeDescriptor.asString()); + } } public boolean canConvert(Class sourceType, Class targetType) { - // For activation when conversion service available - this replaces the rest of the method (probably...) -// return canConvert(sourceType,TypeDescriptor.valueOf(targetType)); - if (ClassUtils.isAssignable(targetType, sourceType) || String.class.equals(targetType)) { - return true; - } - Class actualTargetType = ClassUtils.resolvePrimitiveIfNecessary(targetType); - return (((Number.class.isAssignableFrom(actualTargetType) || Character.class.equals(actualTargetType)) && - (String.class.equals(sourceType) || Number.class.isAssignableFrom(sourceType))) || - (Boolean.class.equals(actualTargetType) && String.class.equals(sourceType))); + return canConvert(sourceType, TypeDescriptor.valueOf(targetType)); } - public boolean canConvert(Class sourceType, TypeDescriptor typeDescriptor) { - // For activation when conversion service available - this replaces the rest of the method (probably...) -// try { -// return conversionService.getConversionExecutor(sourceType, typeDescriptor)!=null; -// } catch (ConversionExecutorNotFoundException cenfe) { -// return false; -// } - return canConvert(sourceType,typeDescriptor.getType()); + public boolean canConvert(Class sourceType, TypeDescriptor targetType) { + return conversionService.canConvert(sourceType, targetType); } } diff --git a/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java b/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java index cbc3009a06..fa6799aa49 100644 --- a/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java +++ b/org.springframework.expression/src/test/java/org/springframework/expression/spel/LiteralTests.java @@ -82,7 +82,7 @@ public class LiteralTests extends ExpressionTestCase { // ask for the result to be made into an Integer evaluateAndAskForReturnType("0x20 * 2L", 64, Integer.class); // ask for the result to be made into an Integer knowing that it will not fit - evaluateAndCheckError("0x1220 * 0xffffffffL", Integer.class, SpelMessages.PROBLEM_DURING_TYPE_CONVERSION, -1); + evaluateAndCheckError("0x1220 * 0xffffffffL", Integer.class, SpelMessages.TYPE_CONVERSION_ERROR, -1); } public void testSignedIntLiterals() {