diff --git a/spring-binding/.settings/org.eclipse.jdt.core.prefs b/spring-binding/.settings/org.eclipse.jdt.core.prefs index c416b20c..127f96ce 100644 --- a/spring-binding/.settings/org.eclipse.jdt.core.prefs +++ b/spring-binding/.settings/org.eclipse.jdt.core.prefs @@ -1,4 +1,4 @@ -#Wed Aug 15 08:35:04 EDT 2007 +#Mon Jun 30 01:12:48 EDT 2008 eclipse.preferences.version=1 org.eclipse.jdt.core.codeComplete.argumentPrefixes= org.eclipse.jdt.core.codeComplete.argumentSuffixes= @@ -9,22 +9,22 @@ org.eclipse.jdt.core.codeComplete.localSuffixes= org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.3 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.2 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.3 +org.eclipse.jdt.core.compiler.compliance=1.4 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate org.eclipse.jdt.core.compiler.doc.comment.support=enabled org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.assertIdentifier=warning org.eclipse.jdt.core.compiler.problem.autoboxing=ignore org.eclipse.jdt.core.compiler.problem.deprecation=warning org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled org.eclipse.jdt.core.compiler.problem.discouragedReference=warning org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=warning org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning diff --git a/spring-binding/.settings/org.eclipse.jdt.ui.prefs b/spring-binding/.settings/org.eclipse.jdt.ui.prefs index 02fbb7e9..193deb8d 100644 --- a/spring-binding/.settings/org.eclipse.jdt.ui.prefs +++ b/spring-binding/.settings/org.eclipse.jdt.ui.prefs @@ -1,9 +1,8 @@ -#Wed Aug 15 08:38:35 EDT 2007 +#Wed Jun 25 10:24:03 EDT 2008 eclipse.preferences.version=1 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true formatter_profile=_Spring Java Conventions formatter_settings_version=11 -internal.default.compliance=user org.eclipse.jdt.ui.exception.name=e org.eclipse.jdt.ui.gettersetter.use.is=false org.eclipse.jdt.ui.javadoc=false diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/ConversionExecutor.java b/spring-binding/src/main/java/org/springframework/binding/convert/ConversionExecutor.java index 06ffa756..ba171f53 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/ConversionExecutor.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/ConversionExecutor.java @@ -42,11 +42,4 @@ public interface ConversionExecutor { */ public Object execute(Object source) throws ConversionExecutionException; - /** - * Execute the conversion for the provided source object. - * @param source the source object to convert - * @param context the conversion context, useful for influencing the behavior of the converter - */ - public Object execute(Object source, Object context) throws ConversionExecutionException; - } \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java index a450e2c2..2ed8fad3 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/ConversionService.java @@ -27,7 +27,7 @@ package org.springframework.binding.convert; public interface ConversionService { /** - * Return a conversion executor command object capable of converting source objects of the specified + * Return the default conversion executor capable of converting source objects of the specified * sourceClass to instances of the targetClass. *

* The returned ConversionExecutor is thread-safe and may safely be cached for use in client code. @@ -39,4 +39,18 @@ public interface ConversionService { public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass) throws ConversionExecutorNotFoundException; + /** + * Return a custom conversion executor capable of converting source objects of the specified + * sourceClass to instances of the targetClass. + *

+ * The returned ConversionExecutor is thread-safe and may safely be cached for use in client code. + * @param id the id of the custom conversion executor (should be unique among custom converters) + * @param sourceClass the source class to convert from + * @param targetClass the target class to convert to + * @return the executor that can execute instance type conversion, never null + * @throws ConversionExecutorNotFoundException when no suitable conversion executor could be found + */ + public ConversionExecutor getConversionExecutor(String id, Class sourceClass, Class targetClass) + throws ConversionExecutorNotFoundException; + } \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/Converter.java b/spring-binding/src/main/java/org/springframework/binding/convert/Converter.java deleted file mode 100644 index 0c2b2894..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/convert/Converter.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2004-2008 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.binding.convert; - -import org.springframework.binding.format.Formatter; - -/** - * Converts objects from one type to another. May support conversion of multiple source types to multiple target types. - *

- * Implementations of this interface are thread-safe and can be shared. - *

- *

- * A converter is more generic than a formatter. A formatter only handles converting from String and back, while - * converters convert from an arbitrary Object type to another. - *

- * @see Formatter - * @author Keith Donald - */ -public interface Converter { - - /** - * The source classes this converter can convert from. - * @return the supported source classes - */ - public Class[] getSourceClasses(); - - /** - * The target classes this converter can convert to. - * @return the supported target classes - */ - public Class[] getTargetClasses(); - - /** - * Convert the provided source object argument to an instance of the specified target class. - * @param source the source object to convert, its class must be one of the supported sourceClasses - * @param targetClass the target class to convert the source to, it must be one of the supported - * targetClasses - * @param context an optional conversion context that may be used to influence the conversion process - * @return the converted object, an instance of the target type - * @throws Exception an exception occurred performing the conversion - */ - public Object convert(Object source, Class targetClass, Object context) throws Exception; - -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToArray.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToArray.java new file mode 100644 index 00000000..67fb0d41 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToArray.java @@ -0,0 +1,48 @@ +package org.springframework.binding.convert.converters; + +import java.lang.reflect.Array; + +import org.springframework.binding.convert.ConversionExecutor; +import org.springframework.binding.convert.ConversionService; + +public class ArrayToArray implements Converter { + + private ConversionService conversionService; + + public ArrayToArray(ConversionService conversionService) { + this.conversionService = conversionService; + } + + public Class getSourceClass() { + return Object[].class; + } + + public Class getTargetClass() { + return Object[].class; + } + + public Object convertSourceToTargetClass(Object source, Class targetClass) throws Exception { + if (source == null) { + return null; + } + Class sourceComponentType = source.getClass().getComponentType(); + if (!sourceComponentType.isPrimitive()) { + Object[] sourceArray = (Object[]) source; + Class targetComponentType = targetClass.getComponentType(); + ConversionExecutor executor = conversionService.getConversionExecutor(sourceComponentType, + targetComponentType); + Object target = Array.newInstance(targetComponentType, sourceArray.length); + if (!targetComponentType.isPrimitive()) { + Object[] targetArray = (Object[]) target; + for (int i = 0; i < sourceArray.length; i++) { + targetArray[i] = executor.execute(sourceArray[i]); + } + return targetArray; + } else { + throw new UnsupportedOperationException("Primitive arrays not yet supported"); + } + } else { + throw new UnsupportedOperationException("Primitive arrays not yet supported"); + } + } +} diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToCollection.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToCollection.java new file mode 100644 index 00000000..ec8a7303 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ArrayToCollection.java @@ -0,0 +1,100 @@ +package org.springframework.binding.convert.converters; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.springframework.binding.convert.ConversionExecutor; +import org.springframework.binding.convert.ConversionService; +import org.springframework.core.GenericCollectionTypeResolver; +import org.springframework.core.JdkVersion; + +public class ArrayToCollection implements TwoWayConverter { + + private ConversionService conversionService; + + public ArrayToCollection(ConversionService conversionService) { + this.conversionService = conversionService; + } + + public Class getSourceClass() { + return Object[].class; + } + + public Class getTargetClass() { + return Collection.class; + } + + public Object convertSourceToTargetClass(Object source, Class targetClass) throws Exception { + if (source == null) { + return null; + } + Class collectionImplClass; + if (targetClass.isInterface()) { + if (List.class.equals(targetClass)) { + collectionImplClass = ArrayList.class; + } else if (Set.class.equals(targetClass)) { + collectionImplClass = LinkedHashSet.class; + } else if (SortedSet.class.equals(targetClass)) { + collectionImplClass = TreeSet.class; + } else { + throw new IllegalArgumentException("Unsupported collection interface [" + targetClass.getName() + "]"); + } + } else { + collectionImplClass = targetClass; + } + Constructor constructor = collectionImplClass.getConstructor(null); + ConversionExecutor converter; + if (JdkVersion.isAtLeastJava15()) { + Class elementType = GenericCollectionTypeResolver.getCollectionType(targetClass); + if (elementType != null) { + Class componentType = source.getClass().getComponentType(); + converter = conversionService.getConversionExecutor(componentType, elementType); + } else { + converter = null; + } + } else { + converter = null; + } + Collection collection = (Collection) constructor.newInstance(null); + Method add = collectionImplClass.getMethod("add", new Class[] { Object.class }); + int length = Array.getLength(source); + if (converter != null) { + for (int i = 0; i < length; i++) { + Object value = Array.get(source, i); + value = converter.execute(value); + add.invoke(collection, new Object[] { value }); + } + } else { + for (int i = 0; i < length; i++) { + Object value = Array.get(source, i); + add.invoke(collection, new Object[] { value }); + } + } + return collection; + } + + public Object convertTargetToSourceClass(Object target, Class sourceClass) throws Exception { + if (target == null) { + return null; + } + Collection collection = (Collection) target; + Object array = Array.newInstance(sourceClass.getComponentType(), collection.size()); + int i = 0; + for (Iterator it = collection.iterator(); it.hasNext(); i++) { + Object value = it.next(); + ConversionExecutor converter = conversionService.getConversionExecutor(value.getClass(), sourceClass + .getComponentType()); + Array.set(array, i, converter.execute(value)); + } + return array; + } +} diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/Converter.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/Converter.java new file mode 100644 index 00000000..4f1cae41 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/Converter.java @@ -0,0 +1,53 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +/** + * A converter is capable of converting a source object of type {@link #getSourceClass()} to a target type of type + * {@link #getTargetClass()}. If the converter is a {@link TwoWayConverter}, it can also convert from the target back + * to the source. + *

+ * Implementations of this interface are thread-safe and can be shared. + *

+ * @author Keith Donald + */ +public interface Converter { + + /** + * The source class this converter can convert from. May be an interface or abstract type to allow this converter to + * convert specific subclasses as well. + * @return the source type + */ + public Class getSourceClass(); + + /** + * The target class this converter can convert to. May be an interface or abstract type to allow this converter to + * convert specific subclasses as well. + * @return the target type + */ + public Class getTargetClass(); + + /** + * Convert the provided source object argument to an instance of the specified target class. + * @param source the source object to convert, which must be an instance of {@link #getSourceClass()} + * @param targetClass the target class to convert the source to, which must be equal to or a specialization of + * {@link #getTargetClass()} + * @return the converted object, which must be an instance of the targetClass + * @throws Exception an exception occurred performing the conversion + */ + public Object convertSourceToTargetClass(Object source, Class targetClass) throws Exception; + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/FormattedStringToNumber.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/FormattedStringToNumber.java new file mode 100644 index 00000000..379aff32 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/FormattedStringToNumber.java @@ -0,0 +1,125 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Locale; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.binding.format.DefaultNumberFormatFactory; +import org.springframework.binding.format.NumberFormatFactory; +import org.springframework.util.NumberUtils; + +/** + * A converter for common number types such as integers and big decimals. Allows the configuration of an explicit number + * pattern and locale. + * + * Works with a general purpose {@link DecimalFormat} instance returned by calling + * {@link NumberFormat#getInstance(Locale)} by default. This instance supports parsing any number type generally and + * will not perform special type-specific logic such as rounding or truncation. Subclasses may override. + * + * Will coerse parsed Numbers to the desired numberClass as necessary. If type-coersion results in an overflow + * condition; for example, what can occur with a Long being coersed to a Short, an exception will be thrown. + * + * @see NumberFormat + * @see DecimalFormat + * + * @author Keith Donald + */ +public class FormattedStringToNumber extends StringToObject { + + private static Log logger = LogFactory.getLog(FormattedStringToNumber.class); + + private NumberFormatFactory numberFormatFactory = new DefaultNumberFormatFactory(); + + private boolean lenient; + + public FormattedStringToNumber(Class numberClass) { + super(numberClass); + } + + /** + * Sets the factory that returns the {@link NumberFormat} instance that will format numbers handled by this + * converter. + * @param numberFormatFactory the number format factory + */ + public void setNumberFormatFactory(NumberFormatFactory numberFormatFactory) { + this.numberFormatFactory = numberFormatFactory; + } + + /** + * If this Converter is "lenient" in parsing number strings. A lenient converter does not require that all + * characters in the String be parsed successfully. Default is false. + * @return the lenient flag + */ + public boolean getLenient() { + return lenient; + } + + /** + * Sets if this Converter should parse leniently. + * @param lenient the lenient flag + */ + public void setLenient(boolean lenient) { + this.lenient = lenient; + } + + protected Object toObject(String string, Class targetClass) throws Exception { + ParsePosition parsePosition = new ParsePosition(0); + NumberFormat format = numberFormatFactory.getNumberFormat(); + Number number = format.parse(string, parsePosition); + if (number == null) { + // no object could be parsed + throw new InvalidFormatException(string, getPattern(format)); + } + if (!lenient) { + if (string.length() != parsePosition.getIndex()) { + // indicates a part of the string that was not parsed; e.g. ".5" in 1234.5 when parsing an Integer + throw new InvalidFormatException(string, getPattern(format)); + } + } + return convertToNumberClass(number, targetClass); + } + + protected String toString(Object object) throws Exception { + Number number = (Number) object; + return numberFormatFactory.getNumberFormat().format(number); + } + + /** + * Coerces the Number object returned by NumberFormat to the desired numberClass. Subclasses may override. + * @param number the parsed number + * @return the coersed number + * @throws IllegalArgumentException when an overflow condition occurs during coersion + */ + protected Number convertToNumberClass(Number number, Class numberClass) throws IllegalArgumentException { + return NumberUtils.convertNumberToTargetClass(number, numberClass); + } + + // internal helpers + + private String getPattern(NumberFormat format) { + if (format instanceof DecimalFormat) { + return ((DecimalFormat) format).toPattern(); + } else { + logger.warn("Pattern string cannot be determined because NumberFormat is not a DecimalFormat"); + return "defaultNumberFormatInstance"; + } + } +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/FormatterConverter.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/FormatterConverter.java deleted file mode 100644 index bd03212b..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/convert/converters/FormatterConverter.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2004-2008 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.binding.convert.converters; - -import org.springframework.binding.convert.Converter; -import org.springframework.binding.format.Formatter; - -/** - * Adapts a Formatter to the Converter interface. Allows a Formatter to be used as a Converter. Allows for both the - * to-string and from-string logic to be used. - * - * @see Formatter - * @see Formatter#format(Object) - * @see Formatter#parse(String) - * - * @author Keith Donald - */ -public class FormatterConverter implements Converter { - - private Formatter formatter; - - /** - * Creates a new formatter converter. - * @param formatter the formatter instance to adapt to the Converter API - */ - public FormatterConverter(Formatter formatter) { - this.formatter = formatter; - } - - public Class[] getSourceClasses() { - return new Class[] { formatter.getObjectType(), String.class }; - } - - public Class[] getTargetClasses() { - return new Class[] { String.class, formatter.getObjectType() }; - } - - public Object convert(Object source, Class targetClass, Object context) throws Exception { - if (targetClass.equals(String.class)) { - return formatter.format(source); - } else { - return formatter.parse((String) source); - } - } - -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/InvalidFormatException.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/InvalidFormatException.java similarity index 97% rename from spring-binding/src/main/java/org/springframework/binding/format/InvalidFormatException.java rename to spring-binding/src/main/java/org/springframework/binding/convert/converters/InvalidFormatException.java index 7d43537b..96c8acde 100644 --- a/spring-binding/src/main/java/org/springframework/binding/format/InvalidFormatException.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/InvalidFormatException.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.binding.format; +package org.springframework.binding.convert.converters; /** * Thrown when a formatted value is of the wrong form. diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ObjectToArray.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ObjectToArray.java new file mode 100644 index 00000000..1a9f230f --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ObjectToArray.java @@ -0,0 +1,72 @@ +package org.springframework.binding.convert.converters; + +import java.lang.reflect.Array; + +import org.springframework.binding.convert.ConversionExecutor; +import org.springframework.binding.convert.ConversionService; + +public class ObjectToArray implements TwoWayConverter { + + private ConversionService conversionService; + + public ObjectToArray(ConversionService conversionService) { + this.conversionService = conversionService; + } + + public Class getSourceClass() { + return Object.class; + } + + public Class getTargetClass() { + return Object[].class; + } + + public Object convertSourceToTargetClass(Object source, Class targetClass) throws Exception { + if (source == null) { + return null; + } + if (source instanceof String) { + String string = (String) source; + String[] elements = string.split(","); + Class componentType = targetClass.getComponentType(); + Object array = Array.newInstance(componentType, elements.length); + ConversionExecutor converter = conversionService.getConversionExecutor(String.class, componentType); + for (int i = 0; i < elements.length; i++) { + String element = elements[i].trim(); + Array.set(array, i, converter.execute(element)); + } + return array; + } else { + Class componentType = targetClass.getComponentType(); + Object array = Array.newInstance(componentType, 1); + ConversionExecutor converter = conversionService.getConversionExecutor(source.getClass(), componentType); + Array.set(array, 0, converter.execute(source)); + return array; + } + } + + public Object convertTargetToSourceClass(Object target, Class sourceClass) throws Exception { + if (target == null) { + return null; + } + if (String.class.equals(sourceClass)) { + int length = Array.getLength(target); + Class componentType = target.getClass().getComponentType(); + ConversionExecutor converter = conversionService.getConversionExecutor(componentType, String.class); + StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < length; i++) { + Object value = Array.get(target, i); + buffer.append(converter.execute(value)); + if (i < length) { + buffer.append(","); + } + } + } else { + Object value = Array.get(target, 0); + Class componentType = target.getClass().getComponentType(); + ConversionExecutor converter = conversionService.getConversionExecutor(componentType, sourceClass); + return converter.execute(value); + } + return null; + } +} diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/ReverseConverter.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ReverseConverter.java new file mode 100644 index 00000000..fd27d24b --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/ReverseConverter.java @@ -0,0 +1,23 @@ +package org.springframework.binding.convert.converters; + +public class ReverseConverter implements Converter { + + private TwoWayConverter converter; + + public ReverseConverter(TwoWayConverter converter) { + this.converter = converter; + } + + public Class getSourceClass() { + return converter.getTargetClass(); + } + + public Class getTargetClass() { + return converter.getSourceClass(); + } + + public Object convertSourceToTargetClass(Object source, Class targetClass) throws Exception { + return converter.convertTargetToSourceClass(source, targetClass); + } + +} diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToNumber.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToBigDecimal.java similarity index 50% rename from spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToNumber.java rename to spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToBigDecimal.java index e453a56f..4dd9da09 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToNumber.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToBigDecimal.java @@ -16,30 +16,25 @@ package org.springframework.binding.convert.converters; import java.math.BigDecimal; -import java.math.BigInteger; - -import org.springframework.binding.convert.Converter; -import org.springframework.util.NumberUtils; /** - * Converts textual representations of numbers to a Number specialization. Delegates to a synchronized - * formatter to parse text strings. + * Converts a String to a BigDecimal. * * @author Keith Donald */ -public class TextToNumber implements Converter { +public class StringToBigDecimal extends StringToObject { - public Class[] getSourceClasses() { - return new Class[] { String.class }; + public StringToBigDecimal() { + super(BigDecimal.class); } - public Class[] getTargetClasses() { - return new Class[] { Integer.class, Short.class, Long.class, Float.class, Double.class, Byte.class, - BigInteger.class, BigDecimal.class }; + public Object toObject(String string, Class objectClass) throws Exception { + return new BigDecimal(string); } - public Object convert(Object source, Class targetClass, Object context) throws Exception { - return NumberUtils.parseNumber((String) source, targetClass); + public String toString(Object object) throws Exception { + BigDecimal number = (BigDecimal) object; + return number.toString(); } } \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToBigInteger.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToBigInteger.java new file mode 100644 index 00000000..a0887fb1 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToBigInteger.java @@ -0,0 +1,39 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +import java.math.BigInteger; + +/** + * Converts a String to a BigInteger. + * + * @author Keith Donald + */ +public class StringToBigInteger extends StringToObject { + + public StringToBigInteger() { + super(BigInteger.class); + } + + public Object toObject(String string, Class objectClass) throws Exception { + return new BigInteger(string); + } + + public String toString(Object object) throws Exception { + BigInteger number = (BigInteger) object; + return number.toString(); + } +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToBoolean.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToBoolean.java new file mode 100644 index 00000000..caff4c92 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToBoolean.java @@ -0,0 +1,84 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +/** + * Converts a textual representation of a boolean object to a Boolean instance. + * + * @author Keith Donald + */ +public class StringToBoolean extends StringToObject { + + private static final String VALUE_TRUE = "true"; + + private static final String VALUE_FALSE = "false"; + + private String trueString; + + private String falseString; + + /** + * Create a text boolean converter that parses standard true and false strings. + */ + public StringToBoolean() { + super(Boolean.class); + } + + /** + * Create a text to boolean converter that takes specific string representations of true and false into account. + * @param trueString special true string to use + * @param falseString special false string to use + */ + public StringToBoolean(String trueString, String falseString) { + super(Boolean.class); + this.trueString = trueString; + this.falseString = falseString; + } + + protected Object toObject(String string, Class targetClass) throws Exception { + if (trueString != null && string.equals(trueString)) { + return Boolean.TRUE; + } else if (falseString != null && string.equals(falseString)) { + return Boolean.FALSE; + } else if (trueString == null && string.equals(VALUE_TRUE)) { + return Boolean.TRUE; + } else if (falseString == null && string.equals(VALUE_FALSE)) { + return Boolean.FALSE; + } else { + throw new IllegalArgumentException("Invalid boolean value [" + string + "]"); + } + } + + protected String toString(Object object) throws Exception { + Boolean value = (Boolean) object; + if (Boolean.TRUE.equals(value)) { + if (trueString != null) { + return trueString; + } else { + return VALUE_TRUE; + } + } else if (Boolean.FALSE.equals(value)) { + if (falseString != null) { + return falseString; + } else { + return VALUE_FALSE; + } + } else { + throw new IllegalArgumentException("Invalid boolean value [" + value + "]"); + } + } + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToByte.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToByte.java new file mode 100644 index 00000000..4a505317 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToByte.java @@ -0,0 +1,38 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +/** + * Converts a String to a BigInteger. + * + * @author Keith Donald + */ +public class StringToByte extends StringToObject { + + public StringToByte() { + super(Byte.class); + } + + public Object toObject(String string, Class objectClass) throws Exception { + return Byte.valueOf(string); + } + + public String toString(Object object) throws Exception { + Byte number = (Byte) object; + return number.toString(); + } + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToCharacter.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToCharacter.java new file mode 100644 index 00000000..c38b5de4 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToCharacter.java @@ -0,0 +1,18 @@ +package org.springframework.binding.convert.converters; + +public class StringToCharacter extends StringToObject { + + public StringToCharacter() { + super(Character.class); + } + + protected Object toObject(String string, Class targetClass) throws Exception { + return new Character(string.charAt(0)); + } + + protected String toString(Object object) throws Exception { + Character character = (Character) object; + return character.toString(); + } + +} diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToClass.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToClass.java new file mode 100644 index 00000000..c8d89c74 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToClass.java @@ -0,0 +1,39 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +import org.springframework.util.ClassUtils; + +/** + * Converts a textual representation of a class object to a Class instance. + * + * @author Keith Donald + */ +public class StringToClass extends StringToObject { + + public StringToClass() { + super(Class.class); + } + + public Object toObject(String string, Class objectClass) throws Exception { + return ClassUtils.forName(string); + } + + public String toString(Object object) throws Exception { + Class clazz = (Class) object; + return clazz.getName(); + } +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/formatters/DateFormatter.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToDate.java similarity index 79% rename from spring-binding/src/main/java/org/springframework/binding/format/formatters/DateFormatter.java rename to spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToDate.java index dba05850..c3a1f903 100644 --- a/spring-binding/src/main/java/org/springframework/binding/format/formatters/DateFormatter.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToDate.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.binding.format.formatters; +package org.springframework.binding.convert.converters; import java.text.DateFormat; import java.text.ParseException; @@ -23,10 +23,7 @@ import java.util.Locale; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.binding.format.Formatter; -import org.springframework.binding.format.InvalidFormatException; import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** @@ -34,9 +31,9 @@ import org.springframework.util.StringUtils; * @see SimpleDateFormat * @author Keith Donald */ -public class DateFormatter implements Formatter { +public class StringToDate extends StringToObject { - private static Log logger = LogFactory.getLog(DateFormatter.class); + private static Log logger = LogFactory.getLog(StringToDate.class); /** * The default date pattern. @@ -47,6 +44,10 @@ public class DateFormatter implements Formatter { private Locale locale; + public StringToDate() { + super(Date.class); + } + /** * The pattern to use to format date values. If not specified, the default pattern 'yyyy-MM-dd' is used. * @return the date formatting pattern @@ -80,32 +81,26 @@ public class DateFormatter implements Formatter { this.locale = locale; } - // implementing Formatter - - public Class getObjectType() { - return Date.class; - } - - public String format(Object date) { - if (date == null) { - return ""; - } - Assert.isInstanceOf(Date.class, date, "Object is not a [java.util.Date]"); - return getDateFormat().format((Date) date); - } - - public Object parse(String formattedString) throws InvalidFormatException { - if (!StringUtils.hasText(formattedString)) { + public Object toObject(String string, Class targetClass) throws Exception { + if (!StringUtils.hasText(string)) { return null; } DateFormat dateFormat = getDateFormat(); try { - return dateFormat.parse(formattedString); + return dateFormat.parse(string); } catch (ParseException e) { - throw new InvalidFormatException(formattedString, getPattern(dateFormat), e); + throw new InvalidFormatException(string, getPattern(dateFormat), e); } } + public String toString(Object target) throws Exception { + Date date = (Date) target; + if (date == null) { + return ""; + } + return getDateFormat().format(date); + } + // subclassing hookings protected DateFormat getDateFormat() { diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToDouble.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToDouble.java new file mode 100644 index 00000000..8533ebc4 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToDouble.java @@ -0,0 +1,38 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +/** + * Converts a String to an Short using {@link Short#valueOf(String)}. + * + * @author Keith Donald + */ +public class StringToDouble extends StringToObject { + + public StringToDouble() { + super(Double.class); + } + + public Object toObject(String string, Class objectClass) throws Exception { + return Double.valueOf(string); + } + + public String toString(Object object) throws Exception { + Double number = (Double) object; + return number.toString(); + } + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToFloat.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToFloat.java new file mode 100644 index 00000000..190b2965 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToFloat.java @@ -0,0 +1,38 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +/** + * Converts a String to an Short using {@link Short#valueOf(String)}. + * + * @author Keith Donald + */ +public class StringToFloat extends StringToObject { + + public StringToFloat() { + super(Float.class); + } + + public Object toObject(String string, Class objectClass) throws Exception { + return Float.valueOf(string); + } + + public String toString(Object object) throws Exception { + Float number = (Float) object; + return number.toString(); + } + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToInteger.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToInteger.java new file mode 100644 index 00000000..3135e341 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToInteger.java @@ -0,0 +1,37 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +/** + * Converts a String to an Integer using {@link Integer#valueOf(String)}. + * + * @author Keith Donald + */ +public class StringToInteger extends StringToObject { + + public StringToInteger() { + super(Integer.class); + } + + public Object toObject(String string, Class objectClass) throws Exception { + return Integer.valueOf(string); + } + + public String toString(Object object) throws Exception { + Integer number = (Integer) object; + return number.toString(); + } +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToLabeledEnum.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToLabeledEnum.java similarity index 74% rename from spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToLabeledEnum.java rename to spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToLabeledEnum.java index 35595243..3baab27a 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToLabeledEnum.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToLabeledEnum.java @@ -15,7 +15,6 @@ */ package org.springframework.binding.convert.converters; -import org.springframework.binding.convert.Converter; import org.springframework.core.enums.LabeledEnum; import org.springframework.core.enums.LabeledEnumResolver; import org.springframework.core.enums.StaticLabeledEnumResolver; @@ -25,20 +24,21 @@ import org.springframework.core.enums.StaticLabeledEnumResolver; * * @author Keith Donald */ -public class TextToLabeledEnum implements Converter { +public class StringToLabeledEnum extends StringToObject { private LabeledEnumResolver labeledEnumResolver = StaticLabeledEnumResolver.instance(); - public Class[] getSourceClasses() { - return new Class[] { String.class }; + public StringToLabeledEnum() { + super(LabeledEnum.class); } - public Class[] getTargetClasses() { - return new Class[] { LabeledEnum.class }; + protected Object toObject(String string, Class targetClass) throws Exception { + return labeledEnumResolver.getLabeledEnumByLabel(targetClass, string); } - public Object convert(Object source, Class targetClass, Object context) throws Exception { - String label = (String) source; - return labeledEnumResolver.getLabeledEnumByLabel(targetClass, label); + protected String toString(Object object) throws Exception { + LabeledEnum labeledEnum = (LabeledEnum) object; + return labeledEnum.getLabel(); } + } \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToLong.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToLong.java new file mode 100644 index 00000000..0b141f9e --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToLong.java @@ -0,0 +1,38 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +/** + * Converts a String to an Long using {@link Long#valueOf(String)}. + * + * @author Keith Donald + */ +public class StringToLong extends StringToObject { + + public StringToLong() { + super(Long.class); + } + + public Object toObject(String string, Class objectClass) throws Exception { + return Long.valueOf(string); + } + + public String toString(Object object) throws Exception { + Long number = (Long) object; + return number.toString(); + } + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToObject.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToObject.java new file mode 100644 index 00000000..f5e0114f --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToObject.java @@ -0,0 +1,40 @@ +package org.springframework.binding.convert.converters; + +public abstract class StringToObject implements TwoWayConverter { + + private Class objectClass; + + public StringToObject(Class objectClass) { + this.objectClass = objectClass; + } + + public final Class getSourceClass() { + return String.class; + } + + public final Class getTargetClass() { + return objectClass; + } + + public final Object convertSourceToTargetClass(Object source, Class targetClass) throws Exception { + String string = (String) source; + if (string != null && string.length() > 0) { + return toObject(string, targetClass); + } else { + return null; + } + } + + public final Object convertTargetToSourceClass(Object target, Class sourceClass) throws Exception { + if (target != null) { + return toString(target); + } else { + return ""; + } + } + + protected abstract Object toObject(String string, Class targetClass) throws Exception; + + protected abstract String toString(Object object) throws Exception; + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToShort.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToShort.java new file mode 100644 index 00000000..e0ad50f9 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/StringToShort.java @@ -0,0 +1,38 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +/** + * Converts a String to an Short using {@link Short#valueOf(String)}. + * + * @author Keith Donald + */ +public class StringToShort extends StringToObject { + + public StringToShort() { + super(Short.class); + } + + public Object toObject(String string, Class objectClass) throws Exception { + return Short.valueOf(string); + } + + public String toString(Object object) throws Exception { + Short number = (Short) object; + return number.toString(); + } + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToBoolean.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToBoolean.java deleted file mode 100644 index 17730175..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToBoolean.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2004-2008 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.binding.convert.converters; - -import org.springframework.binding.convert.Converter; -import org.springframework.util.StringUtils; - -/** - * Converts a textual representation of a boolean object to a Boolean instance. - * - * @author Keith Donald - */ -public class TextToBoolean implements Converter { - - private static final String VALUE_TRUE = "true"; - - private static final String VALUE_FALSE = "false"; - - private static final String VALUE_ON = "on"; - - private static final String VALUE_OFF = "off"; - - private static final String VALUE_YES = "yes"; - - private static final String VALUE_NO = "no"; - - private static final String VALUE_1 = "1"; - - private static final String VALUE_0 = "0"; - - private String trueString; - - private String falseString; - - /** - * Default constructor. No special true or false strings are considered. - */ - public TextToBoolean() { - this(null, null); - } - - /** - * Create a text to boolean converter. Take given special string representations of true and false into - * account. - * @param trueString special true string to consider - * @param falseString special false string to consider - */ - public TextToBoolean(String trueString, String falseString) { - this.trueString = trueString; - this.falseString = falseString; - } - - public Class[] getSourceClasses() { - return new Class[] { String.class }; - } - - public Class[] getTargetClasses() { - return new Class[] { Boolean.class }; - } - - public Object convert(Object source, Class targetClass, Object context) throws Exception { - String text = (String) source; - if (!StringUtils.hasText(text)) { - return null; - } else if (this.trueString != null && text.equalsIgnoreCase(this.trueString)) { - return Boolean.TRUE; - } else if (this.falseString != null && text.equalsIgnoreCase(this.falseString)) { - return Boolean.FALSE; - } else if (this.trueString == null - && (text.equalsIgnoreCase(VALUE_TRUE) || text.equalsIgnoreCase(VALUE_ON) - || text.equalsIgnoreCase(VALUE_YES) || text.equals(VALUE_1))) { - return Boolean.TRUE; - } else if (this.falseString == null - && (text.equalsIgnoreCase(VALUE_FALSE) || text.equalsIgnoreCase(VALUE_OFF) - || text.equalsIgnoreCase(VALUE_NO) || text.equals(VALUE_0))) { - return Boolean.FALSE; - } else { - throw new IllegalArgumentException("Invalid boolean value [" + text + "]"); - } - } -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToClass.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToClass.java deleted file mode 100644 index d2179029..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/convert/converters/TextToClass.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2004-2008 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.binding.convert.converters; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.HashMap; -import java.util.Map; - -import org.springframework.binding.convert.Converter; -import org.springframework.core.enums.LabeledEnum; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; - -/** - * Converts a textual representation of a class object to a Class instance. - * - * @author Keith Donald - */ -public class TextToClass implements Converter { - - private Map aliasMap = new HashMap(); - - public TextToClass() { - addDefaultAliases(); - } - - /** - * Add an alias for given target type. - */ - public void addAlias(String alias, Class targetType) { - aliasMap.put(alias, targetType); - } - - public Class[] getSourceClasses() { - return new Class[] { String.class }; - } - - public Class[] getTargetClasses() { - return new Class[] { Class.class }; - } - - public Object convert(Object source, Class targetClass, Object context) throws Exception { - String text = (String) source; - if (StringUtils.hasText(text)) { - text = text.trim(); - if (aliasMap.containsKey(text)) { - return aliasMap.get(text); - } else { - return ClassUtils.forName(text); - } - } else { - return null; - } - } - - protected void addDefaultAliases() { - addAlias("string", String.class); - addAlias("short", Short.class); - addAlias("integer", Integer.class); - addAlias("int", Integer.class); - addAlias("byte", Byte.class); - addAlias("long", Long.class); - addAlias("float", Float.class); - addAlias("double", Double.class); - addAlias("bigInteger", BigInteger.class); - addAlias("bigDecimal", BigDecimal.class); - addAlias("boolean", Boolean.class); - addAlias("class", Class.class); - addAlias("labeledEnum", LabeledEnum.class); - } -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/converters/TwoWayConverter.java b/spring-binding/src/main/java/org/springframework/binding/convert/converters/TwoWayConverter.java new file mode 100644 index 00000000..57ec4c3d --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/convert/converters/TwoWayConverter.java @@ -0,0 +1,35 @@ +/* + * Copyright 2004-2008 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.binding.convert.converters; + +/** + * A converter that can also convert from the target back to the source. + * + * @author Keith Donald + */ +public interface TwoWayConverter extends Converter { + + /** + * Convert the provided target object argument to an instance of the specified source class. + * @param target the target object to convert, which must be an instance of {@link #getTargetClass()} + * @param sourceClass the source class to convert the target to, which must be equal to or a specialization of + * {@link #getSourceClass()} + * @return the converted object, which must be an instance of the sourceClass + * @throws Exception an exception occurred performing the conversion + */ + public Object convertTargetToSourceClass(Object target, Class sourceClass) throws Exception; + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java index d55f1a8b..177b4859 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/service/DefaultConversionService.java @@ -16,10 +16,19 @@ package org.springframework.binding.convert.service; import org.springframework.binding.convert.ConversionService; -import org.springframework.binding.convert.converters.TextToBoolean; -import org.springframework.binding.convert.converters.TextToClass; -import org.springframework.binding.convert.converters.TextToLabeledEnum; -import org.springframework.binding.convert.converters.TextToNumber; +import org.springframework.binding.convert.converters.StringToBigDecimal; +import org.springframework.binding.convert.converters.StringToBigInteger; +import org.springframework.binding.convert.converters.StringToBoolean; +import org.springframework.binding.convert.converters.StringToByte; +import org.springframework.binding.convert.converters.StringToCharacter; +import org.springframework.binding.convert.converters.StringToClass; +import org.springframework.binding.convert.converters.StringToDate; +import org.springframework.binding.convert.converters.StringToDouble; +import org.springframework.binding.convert.converters.StringToFloat; +import org.springframework.binding.convert.converters.StringToInteger; +import org.springframework.binding.convert.converters.StringToLabeledEnum; +import org.springframework.binding.convert.converters.StringToLong; +import org.springframework.binding.convert.converters.StringToShort; /** * Default, local implementation of a conversion service. Will automatically register from string converters for @@ -45,10 +54,19 @@ public class DefaultConversionService extends GenericConversionService { * Add all default converters to the conversion service. */ protected void addDefaultConverters() { - addConverter(new TextToClass()); - addConverter(new TextToBoolean()); - addConverter(new TextToLabeledEnum()); - addConverter(new TextToNumber()); + addConverter(new StringToByte()); + addConverter(new StringToBoolean()); + addConverter(new StringToCharacter()); + addConverter(new StringToShort()); + addConverter(new StringToInteger()); + addConverter(new StringToLong()); + addConverter(new StringToFloat()); + addConverter(new StringToDouble()); + addConverter(new StringToBigInteger()); + addConverter(new StringToBigDecimal()); + addConverter(new StringToClass()); + addConverter(new StringToLabeledEnum()); + addConverter(new StringToDate()); } /** diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java index f535d95b..e34a77a7 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/service/GenericConversionService.java @@ -15,6 +15,8 @@ */ package org.springframework.binding.convert.service; +import java.lang.reflect.Modifier; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; @@ -23,7 +25,12 @@ import java.util.Map; import org.springframework.binding.convert.ConversionExecutor; import org.springframework.binding.convert.ConversionExecutorNotFoundException; import org.springframework.binding.convert.ConversionService; -import org.springframework.binding.convert.Converter; +import org.springframework.binding.convert.converters.ArrayToArray; +import org.springframework.binding.convert.converters.ArrayToCollection; +import org.springframework.binding.convert.converters.Converter; +import org.springframework.binding.convert.converters.ObjectToArray; +import org.springframework.binding.convert.converters.ReverseConverter; +import org.springframework.binding.convert.converters.TwoWayConverter; import org.springframework.util.Assert; /** @@ -35,8 +42,8 @@ public class GenericConversionService implements ConversionService { /** * An indexed map of converters. Each entry key is a source class that can be converted from, and each entry value - * is a map of target classes that can be convertered to, ultimately mapping to a specific converter that can - * perform the source->target conversion. + * is a map of target classes that can be converted to, ultimately mapping to a specific converter that can perform + * the source->target conversion. */ private Map sourceClassConverters = new HashMap(); @@ -64,36 +71,65 @@ public class GenericConversionService implements ConversionService { * @param converter the converter */ public void addConverter(Converter converter) { - Class[] sourceClasses = converter.getSourceClasses(); - Class[] targetClasses = converter.getTargetClasses(); - for (int i = 0; i < sourceClasses.length; i++) { - Class sourceClass = sourceClasses[i]; - Map sourceMap = (Map) sourceClassConverters.get(sourceClass); - if (sourceMap == null) { - sourceMap = new HashMap(); - sourceClassConverters.put(sourceClass, sourceMap); - } - for (int j = 0; j < targetClasses.length; j++) { - Class targetClass = targetClasses[j]; - if (!targetClass.equals(sourceClass)) { - sourceMap.put(targetClass, converter); - } - } + Class sourceClass = converter.getSourceClass(); + Class targetClass = converter.getTargetClass(); + if (sourceClass.isPrimitive()) { + throw new IllegalArgumentException("Invalid Converter " + converter + + "; A primitive type is not allowed for the [sourceClass] argument"); } + if (targetClass.isPrimitive()) { + throw new IllegalArgumentException("Invalid Converter " + converter + + "; A primitive type is not allowed for the [targetClass] argument"); + } + Map sourceMap = getSourceMap(sourceClass); + sourceMap.put(targetClass, converter); + if (converter instanceof TwoWayConverter) { + sourceMap = getSourceMap(targetClass); + sourceMap.put(sourceClass, new ReverseConverter((TwoWayConverter) converter)); + } + } + + private Map getSourceMap(Class sourceClass) { + Map sourceMap = (Map) sourceClassConverters.get(sourceClass); + if (sourceMap == null) { + sourceMap = new HashMap(); + sourceClassConverters.put(sourceClass, sourceMap); + } + return sourceMap; } public ConversionExecutor getConversionExecutor(Class sourceClass, Class targetClass) throws ConversionExecutorNotFoundException { Assert.notNull(sourceClass, "The source class to convert from is required"); Assert.notNull(targetClass, "The target class to convert to is required"); - if (sourceClassConverters == null || sourceClassConverters.isEmpty()) { - throw new IllegalStateException("No converters have been added to this service's registry"); - } sourceClass = convertToWrapperClassIfNecessary(sourceClass); targetClass = convertToWrapperClassIfNecessary(targetClass); if (targetClass.isAssignableFrom(sourceClass)) { return new StaticConversionExecutor(sourceClass, targetClass, new NoOpConverter(sourceClass, targetClass)); } + if (sourceClass.isArray()) { + if (targetClass.isArray()) { + return new StaticConversionExecutor(sourceClass, targetClass, new ArrayToArray(this)); + } else if (Collection.class.isAssignableFrom(targetClass)) { + if (!targetClass.isInterface() && Modifier.isAbstract(targetClass.getModifiers())) { + throw new IllegalArgumentException("Conversion target class [" + targetClass.getName() + + "] is invalid; cannot convert to abstract collection types--" + + "request an interface or concrete implementation instead"); + } + return new StaticConversionExecutor(sourceClass, targetClass, new ArrayToCollection(this)); + } else { + Converter arrayToObject = new ReverseConverter(new ObjectToArray(this)); + return new StaticConversionExecutor(sourceClass, targetClass, arrayToObject); + } + } + if (targetClass.isArray()) { + if (Collection.class.isAssignableFrom(sourceClass)) { + Converter collectionToArray = new ReverseConverter(new ArrayToCollection(this)); + return new StaticConversionExecutor(sourceClass, targetClass, collectionToArray); + } else { + return new StaticConversionExecutor(sourceClass, targetClass, new ObjectToArray(this)); + } + } Map sourceTargetConverters = findConvertersForSource(sourceClass); Converter converter = findTargetConverter(sourceTargetConverters, targetClass); if (converter != null) { @@ -111,6 +147,11 @@ public class GenericConversionService implements ConversionService { } } + public ConversionExecutor getConversionExecutor(String id, Class sourceClass, Class targetClass) + throws ConversionExecutorNotFoundException { + throw new UnsupportedOperationException("Not yet implemented"); + } + // subclassing support /** @@ -143,7 +184,7 @@ public class GenericConversionService implements ConversionService { if (sourceTargetConverters != null && !sourceTargetConverters.isEmpty()) { return sourceTargetConverters; } - if (!sourceClass.isInterface() && (sourceClass.getSuperclass() != null)) { + if (!sourceClass.isInterface() && sourceClass.getSuperclass() != null) { classQueue.addFirst(sourceClass.getSuperclass()); } // queue up source class's implemented interfaces. @@ -164,7 +205,7 @@ public class GenericConversionService implements ConversionService { if (converter != null) { return converter; } - if (!targetClass.isInterface() && (targetClass.getSuperclass() != null)) { + if (!targetClass.isInterface() && targetClass.getSuperclass() != null) { classQueue.addFirst(targetClass.getSuperclass()); } // queue up target class's implemented interfaces. @@ -201,4 +242,5 @@ public class GenericConversionService implements ConversionService { return targetType; } } + } \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/service/NoOpConverter.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/NoOpConverter.java index 582747d6..3c331480 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/service/NoOpConverter.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/service/NoOpConverter.java @@ -15,7 +15,7 @@ */ package org.springframework.binding.convert.service; -import org.springframework.binding.convert.Converter; +import org.springframework.binding.convert.converters.Converter; /** * Package private converter that is a "no op". @@ -36,15 +36,24 @@ class NoOpConverter implements Converter { this.targetClass = targetClass; } - public Object convert(Object source, Class targetClass, Object context) throws Exception { + public Class getSourceClass() { + return sourceClass; + } + + public Class getTargetClass() { + return targetClass; + } + + public Object convertSourceToTargetClass(Object source, Class targetClass) throws Exception { return source; } - public Class[] getSourceClasses() { - return new Class[] { sourceClass }; + public boolean isTwoWay() { + return true; } - public Class[] getTargetClasses() { - return new Class[] { targetClass }; + public Object convertTargetToSourceClass(Object target, Class sourceClass) throws Exception, + UnsupportedOperationException { + return target; } } \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/convert/service/StaticConversionExecutor.java b/spring-binding/src/main/java/org/springframework/binding/convert/service/StaticConversionExecutor.java index 9e7e2334..52754390 100644 --- a/spring-binding/src/main/java/org/springframework/binding/convert/service/StaticConversionExecutor.java +++ b/spring-binding/src/main/java/org/springframework/binding/convert/service/StaticConversionExecutor.java @@ -17,7 +17,7 @@ package org.springframework.binding.convert.service; import org.springframework.binding.convert.ConversionExecutionException; import org.springframework.binding.convert.ConversionExecutor; -import org.springframework.binding.convert.Converter; +import org.springframework.binding.convert.converters.Converter; import org.springframework.core.style.ToStringCreator; import org.springframework.util.Assert; @@ -87,10 +87,6 @@ public class StaticConversionExecutor implements ConversionExecutor { } public Object execute(Object source) throws ConversionExecutionException { - return execute(source, null); - } - - public Object execute(Object source, Object context) throws ConversionExecutionException { if (targetClass.isInstance(source)) { // source is already assignment compatible with target class return source; @@ -100,7 +96,7 @@ public class StaticConversionExecutor implements ConversionExecutor { + source + " is expected to be an instance of " + getSourceClass()); } try { - return converter.convert(source, targetClass, context); + return converter.convertSourceToTargetClass(source, targetClass); } catch (Exception e) { throw new ConversionExecutionException(source, getSourceClass(), getTargetClass(), e); } diff --git a/spring-binding/src/main/java/org/springframework/binding/format/AbstractNumberFormatFactory.java b/spring-binding/src/main/java/org/springframework/binding/format/AbstractNumberFormatFactory.java new file mode 100644 index 00000000..4e2185f4 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/format/AbstractNumberFormatFactory.java @@ -0,0 +1,66 @@ +/* + * Copyright 2004-2008 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.binding.format; + +import java.text.NumberFormat; +import java.util.Locale; + +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * Base class suitable for subclassing by most {@link NumberFormatFactory} implementations. + * + * @author Keith Donald + */ +public abstract class AbstractNumberFormatFactory implements NumberFormatFactory { + + private Locale locale; + + /** + * The locale to use in formatting number values. If null, the locale associated with the current thread is used. + * @see LocaleContextHolder#getLocale() + * @return the locale + */ + public Locale getLocale() { + return locale; + } + + /** + * Sets the locale to use in formatting number values. + * @param locale the locale + */ + public void setLocale(Locale locale) { + this.locale = locale; + } + + public final NumberFormat getNumberFormat() { + Locale locale = determineLocale(this.locale); + return getNumberFormat(locale); + } + + /** + * Subclasses should override to create the new NumberFormat instance. + * @param locale the locale to use + * @return the number format + */ + protected abstract NumberFormat getNumberFormat(Locale locale); + + // internal helpers + + private Locale determineLocale(Locale locale) { + return locale != null ? locale : LocaleContextHolder.getLocale(); + } +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/CurrencyNumberFormatFactory.java b/spring-binding/src/main/java/org/springframework/binding/format/CurrencyNumberFormatFactory.java new file mode 100644 index 00000000..0e59651e --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/format/CurrencyNumberFormatFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2004-2008 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.binding.format; + +import java.text.NumberFormat; +import java.util.Locale; + +/** + * Produces NumberFormat instances that format currency values. + * + * @see NumberFormat + * @author Keith Donald + */ +public class CurrencyNumberFormatFactory extends AbstractNumberFormatFactory { + protected NumberFormat getNumberFormat(Locale locale) { + return NumberFormat.getCurrencyInstance(locale); + } +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/DefaultNumberFormatFactory.java b/spring-binding/src/main/java/org/springframework/binding/format/DefaultNumberFormatFactory.java new file mode 100644 index 00000000..9b391e2c --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/format/DefaultNumberFormatFactory.java @@ -0,0 +1,71 @@ +/* + * Copyright 2004-2008 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.binding.format; + +import java.text.DecimalFormat; +import java.text.NumberFormat; +import java.util.Locale; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Works with a general purpose {@link DecimalFormat} instance returned by calling + * {@link NumberFormat#getInstance(Locale)} by default. This instance supports parsing any number type generally and + * will not perform special type-specific logic such as rounding or truncation. + * + * @see NumberFormat + * @see DecimalFormat + * + * @author Keith Donald + */ +public class DefaultNumberFormatFactory extends AbstractNumberFormatFactory { + + private static Log logger = LogFactory.getLog(DefaultNumberFormatFactory.class); + + private String pattern; + + /** + * The pattern to use to format number values. If not specified, the default DecimalFormat pattern is used. + * @return the date formatting pattern + */ + public String getPattern() { + return pattern; + } + + /** + * Sets the pattern for formatting numbers. + * @param pattern the format pattern + * @see DecimalFormat + */ + public void setPattern(String pattern) { + this.pattern = pattern; + } + + protected NumberFormat getNumberFormat(Locale locale) { + NumberFormat format = NumberFormat.getInstance(locale); + if (pattern != null) { + if (format instanceof DecimalFormat) { + ((DecimalFormat) format).applyPattern(pattern); + } else { + logger.warn("Unable to apply format pattern '" + pattern + + "'; Returned NumberFormat is not a DecimalFormat"); + } + } + return format; + } + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/Formatter.java b/spring-binding/src/main/java/org/springframework/binding/format/Formatter.java deleted file mode 100644 index 3dbbb9b4..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/format/Formatter.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2004-2008 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.binding.format; - -/** - * Formats objects of a certain class for display. - * - * @author Keith Donald - */ -public interface Formatter { - - /** - * Returns the type of object this Formatter formats. Successful {@link #parse(String) parse calls} will return - * instances of this type. {@link #format(Object)} callers must provide an instance of this type. - * @return the type of object being formatted - */ - public Class getObjectType(); - - /** - * Format the object for display. - * @param object the object to format - * @return the formatted string, fit for display in a UI - * @throws IllegalArgumentException if the object is of the wrong type - */ - public String format(Object object) throws IllegalArgumentException; - - /** - * Parse the formatted string representation of an object and return the object. - * @param formattedString the formatted string representation - * @return the parsed object - * @throws InvalidFormatException the formatted string was in an invalid form, often due to user input error - */ - public Object parse(String formattedString) throws InvalidFormatException; - -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/FormatterRegistry.java b/spring-binding/src/main/java/org/springframework/binding/format/FormatterRegistry.java deleted file mode 100644 index a7bfa72e..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/format/FormatterRegistry.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2004-2008 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.binding.format; - -/** - * A Source for shared and commonly used Formatters. - * - * @author Keith Donald - */ -public interface FormatterRegistry { - - /** - * Returns the default formatter installed for the given class of object. - * @param clazz the type of object that will be formatted - * @return the formatter - */ - public Formatter getFormatter(Class clazz); - - /** - * Returns the formatter for the given class of object with the given id. Use this method to query a custom - * formatter instance for a given class of object. - * @param clazz the type of object that will be formatted - * @param id the id of the custom formatter instance; typically descriptive like "localDate" - * @return the formatter - */ - public Formatter getFormatter(Class clazz, String id); - -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/formatters/IntegerFormatter.java b/spring-binding/src/main/java/org/springframework/binding/format/IntegerNumberFormatFactory.java similarity index 61% rename from spring-binding/src/main/java/org/springframework/binding/format/formatters/IntegerFormatter.java rename to spring-binding/src/main/java/org/springframework/binding/format/IntegerNumberFormatFactory.java index c50ff952..22c8877b 100644 --- a/spring-binding/src/main/java/org/springframework/binding/format/formatters/IntegerFormatter.java +++ b/spring-binding/src/main/java/org/springframework/binding/format/IntegerNumberFormatFactory.java @@ -1,36 +1,29 @@ /* * Copyright 2004-2008 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.binding.format.formatters; +package org.springframework.binding.format; import java.text.NumberFormat; import java.util.Locale; /** - * The default formatter for integer instances that are whole numbers and do not have fractions, such as Integers, - * Longs, and Shorts. Simply delegates to {@link NumberFormat#getIntegerInstance(Locale)}. - * + * @see NumberFormat * @author Keith Donald */ -public class IntegerFormatter extends NumberFormatter { - - public IntegerFormatter(Class numberClass) { - super(numberClass); - } - - protected NumberFormat createNumberFormat(Locale locale) { +public class IntegerNumberFormatFactory extends AbstractNumberFormatFactory { + protected NumberFormat getNumberFormat(Locale locale) { return NumberFormat.getIntegerInstance(locale); } } \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/NumberFormatFactory.java b/spring-binding/src/main/java/org/springframework/binding/format/NumberFormatFactory.java new file mode 100644 index 00000000..2212556a --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/format/NumberFormatFactory.java @@ -0,0 +1,20 @@ +package org.springframework.binding.format; + +import java.text.NumberFormat; + +/** + * A factory for {@link NumberFormat} objects. Conceals the complexity associated with configuring, constructing, and/or + * caching number format instances. + * + * @author Keith Donald + */ +public interface NumberFormatFactory { + + /** + * Factory method that returns a fully-configured {@link NumberFormat} instance to use to format an object for + * display. + * @return returns the number for a + */ + public NumberFormat getNumberFormat(); + +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/PercentNumberFormatFactory.java b/spring-binding/src/main/java/org/springframework/binding/format/PercentNumberFormatFactory.java new file mode 100644 index 00000000..52305028 --- /dev/null +++ b/spring-binding/src/main/java/org/springframework/binding/format/PercentNumberFormatFactory.java @@ -0,0 +1,31 @@ +/* + * Copyright 2004-2008 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.binding.format; + +import java.text.NumberFormat; +import java.util.Locale; + +/** + * Produces NumberFormat instances that format percent values. + * + * @see NumberFormat + * @author Keith Donald + */ +public class PercentNumberFormatFactory extends AbstractNumberFormatFactory { + protected NumberFormat getNumberFormat(Locale locale) { + return NumberFormat.getPercentInstance(locale); + } +} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/formatters/BooleanFormatter.java b/spring-binding/src/main/java/org/springframework/binding/format/formatters/BooleanFormatter.java deleted file mode 100644 index bc97f087..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/format/formatters/BooleanFormatter.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2004-2008 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.binding.format.formatters; - -import org.springframework.binding.format.Formatter; -import org.springframework.binding.format.InvalidFormatException; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - -/** - * A formatter for boolean values. Formats {@link Boolean#TRUE} as "true" and {@link Boolean#FALSE} as "false". - * - * @author Keith Donald - */ -public class BooleanFormatter implements Formatter { - - public Class getObjectType() { - return Boolean.class; - } - - public String format(Object value) throws IllegalArgumentException { - if (value == null) { - return ""; - } - Assert.isInstanceOf(Boolean.class, value, "Object is not a [java.lang.Boolean]"); - if (Boolean.TRUE.equals(value)) { - return "true"; - } else { - return "false"; - } - } - - public Object parse(String formattedString) throws InvalidFormatException { - if (!StringUtils.hasText(formattedString)) { - return null; - } - if (formattedString.equals("true")) { - return Boolean.TRUE; - } else if (formattedString.equals("false")) { - return Boolean.FALSE; - } else { - throw new InvalidFormatException(formattedString, "true | false"); - } - } -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/formatters/NumberFormatter.java b/spring-binding/src/main/java/org/springframework/binding/format/formatters/NumberFormatter.java deleted file mode 100644 index 039b2b9d..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/format/formatters/NumberFormatter.java +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2004-2008 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.binding.format.formatters; - -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.text.ParsePosition; -import java.util.Locale; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.springframework.binding.format.Formatter; -import org.springframework.binding.format.InvalidFormatException; -import org.springframework.context.i18n.LocaleContextHolder; -import org.springframework.util.Assert; -import org.springframework.util.NumberUtils; -import org.springframework.util.StringUtils; - -/** - * A general formatter for common number types such as integers and big decimals. Allows the configuration of an - * explicit number pattern and locale. - * - * Works with a general purpose {@link DecimalFormat} instance returned by calling - * {@link NumberFormat#getInstance(Locale)} by default. This instance supports parsing any number type generally and - * will not perform special type-specific logic such as rounding or truncation. Subclasses may override. - * - * Will coerse parsed Numbers to the desired numberClass as necessary. If type-coersion results in an overflow - * condition; for example, what can occur with a Long being coersed to a Short, an exception will be thrown. - * - * @see NumberFormat - * @see DecimalFormat - * - * @author Keith Donald - */ -public class NumberFormatter implements Formatter { - - private static Log logger = LogFactory.getLog(NumberFormatter.class); - - private String pattern; - - private Class numberClass; - - private Locale locale; - - private boolean lenient; - - /** - * Creates a number formatter for the specified number type. - * @param numberClass the number type, a class extending from {@link Number}. - */ - public NumberFormatter(Class numberClass) { - Assert.notNull(numberClass, "The number class is required"); - Assert.isTrue(Number.class.isAssignableFrom(numberClass), "The class must extend from Number"); - this.numberClass = numberClass; - } - - /** - * The pattern to use to format number values. If not specified, the default DecimalFormat pattern is used. - * @return the date formatting pattern - */ - public String getPattern() { - return pattern; - } - - /** - * Sets the pattern for formatting numbers. - * @param pattern the format pattern - * @see DecimalFormat - */ - public void setPattern(String pattern) { - this.pattern = pattern; - } - - /** - * The locale to use in formatting number values. If null, the locale associated with the current thread is used. - * @see LocaleContextHolder#getLocale() - * @return the locale - */ - public Locale getLocale() { - return locale; - } - - /** - * Sets the locale to use in formatting number values. - * @param locale the locale - */ - public void setLocale(Locale locale) { - this.locale = locale; - } - - /** - * If this Formatter is "lenient" in parsing number strings. A lenient formatter does not require that all - * characters in the String be parsed successfully. Default is false. - * @return the lenient flag - */ - public boolean getLenient() { - return lenient; - } - - /** - * Sets if this Formatter should parse leniently. - * @param lenient the lenient flag - */ - public void setLenient(boolean lenient) { - this.lenient = lenient; - } - - // implementing Formatter - - public Class getObjectType() { - return numberClass; - } - - public String format(Object number) { - if (number == null) { - return ""; - } - if (!numberClass.isInstance(number)) { - throw new IllegalArgumentException("Object is not a [" + numberClass.getName() + "]; it is a [" - + number.getClass().getName() + "]"); - } - return getNumberFormat().format(number); - } - - public Object parse(String formattedString) throws InvalidFormatException { - if (!StringUtils.hasText(formattedString)) { - return null; - } - ParsePosition parsePosition = new ParsePosition(0); - NumberFormat format = getNumberFormat(); - Number number = format.parse(formattedString, parsePosition); - if (number == null) { - // no object could be parsed - throw new InvalidFormatException(formattedString, getPattern(format)); - } - if (!lenient) { - if (formattedString.length() != parsePosition.getIndex()) { - // indicates a part of the string that was not parsed; e.g. ".5" in 1234.5 when parsing an Integer - throw new InvalidFormatException(formattedString, getPattern(format)); - } - } - return convertToNumberClass(number); - } - - // subclassing hookings - - /** - * Factory method that returns a fully-configured {@link NumberFormat} instance to use to format an object for - * display. Applies the locale and pattern properties if configured. Subclasses may override. - */ - protected NumberFormat getNumberFormat() { - Locale locale = determineLocale(this.locale); - NumberFormat format = createNumberFormat(locale); - if (pattern != null) { - if (format instanceof DecimalFormat) { - ((DecimalFormat) format).applyPattern(pattern); - } else { - logger.warn("Unable to apply format pattern '" + pattern - + "'; Returned NumberFormat is not a DecimalFormat"); - } - } - return format; - } - - /** - * Delegates to the {@link NumberFormat java.text.NumberFormat API} to construct the new NumberFormat instance. - * Called by {@link #getNumberFormat()} after calculating the Locale. Subclasses may override to control how the - * Format instance is constructed. - * @param locale the calculated Locale - * @return the new NumberFormat instance - */ - protected NumberFormat createNumberFormat(Locale locale) { - return NumberFormat.getInstance(locale); - } - - /** - * Coerces the Number object returned by NumberFormat to the desired numberClass. Subclasses may override. - * @param number the parsed number - * @return the coersed number - * @throws IllegalArgumentException when an overflow condition occurs during coersion - */ - protected Number convertToNumberClass(Number number) throws IllegalArgumentException { - return NumberUtils.convertNumberToTargetClass(number, numberClass); - } - - // internal helpers - - private Locale determineLocale(Locale locale) { - return locale != null ? locale : LocaleContextHolder.getLocale(); - } - - private String getPattern(NumberFormat format) { - if (format instanceof DecimalFormat) { - return ((DecimalFormat) format).toPattern(); - } else { - logger.warn("Pattern string cannot be determined because NumberFormat is not a DecimalFormat"); - return "defaultNumberFormatInstance"; - } - } -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/formatters/PropertyEditorFormatter.java b/spring-binding/src/main/java/org/springframework/binding/format/formatters/PropertyEditorFormatter.java deleted file mode 100644 index ea4bb9f7..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/format/formatters/PropertyEditorFormatter.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2004-2008 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.binding.format.formatters; - -import java.beans.PropertyEditor; - -import org.springframework.binding.format.Formatter; -import org.springframework.util.Assert; - -/** - * Adapts a {@link PropertyEditor} to the formatter interface. Useful for re-using pre-existing PropertyEditors as - * Formatters. - * - * Note: this class synchronizes calls into the configured PropertyEditor instance to ensure thread safety. - * - * @author Keith Donald - */ -public class PropertyEditorFormatter implements Formatter { - - private Class objectType; - - private PropertyEditor propertyEditor; - - /** - * Wrap the given property editor in a formatter. - */ - public PropertyEditorFormatter(PropertyEditor propertyEditor, Class objectType) { - Assert.notNull(propertyEditor, "The PropertyEditor is required"); - Assert.notNull(objectType, "The object type is required"); - this.propertyEditor = propertyEditor; - this.objectType = objectType; - } - - /** - * Returns the wrapped property editor. - */ - public PropertyEditor getPropertyEditor() { - return propertyEditor; - } - - // implementing Formatter - - public Class getObjectType() { - return objectType; - } - - public String format(Object value) { - synchronized (propertyEditor) { - propertyEditor.setValue(value); - String text = propertyEditor.getAsText(); - propertyEditor.setValue(null); - return text; - } - } - - public Object parse(String formattedValue) { - synchronized (propertyEditor) { - propertyEditor.setAsText(formattedValue); - Object value = propertyEditor.getValue(); - propertyEditor.setValue(null); - return value; - } - } -} \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/format/formatters/package.html b/spring-binding/src/main/java/org/springframework/binding/format/formatters/package.html deleted file mode 100644 index 11cf8142..00000000 --- a/spring-binding/src/main/java/org/springframework/binding/format/formatters/package.html +++ /dev/null @@ -1,7 +0,0 @@ - - -

-Common Formatter implementations. -

- - \ No newline at end of file diff --git a/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapping.java b/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapping.java index 573f5e83..96613c03 100644 --- a/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapping.java +++ b/spring-binding/src/main/java/org/springframework/binding/mapping/impl/DefaultMapping.java @@ -123,7 +123,7 @@ public class DefaultMapping implements Mapping { if (sourceValue != null) { if (typeConverter != null) { try { - targetValue = typeConverter.execute(sourceValue, context); + targetValue = typeConverter.execute(sourceValue); } catch (ConversionExecutionException e) { context.setTypeConversionErrorResult(e); return; @@ -142,7 +142,7 @@ public class DefaultMapping implements Mapping { ConversionExecutor typeConverter = conversionService.getConversionExecutor(sourceValue .getClass(), targetType); try { - targetValue = typeConverter.execute(sourceValue, context); + targetValue = typeConverter.execute(sourceValue); } catch (ConversionExecutionException e) { context.setTypeConversionErrorResult(e); return; diff --git a/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java b/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java index 56c200d2..0ef0b2c1 100644 --- a/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/convert/service/DefaultConversionServiceTests.java @@ -15,8 +15,11 @@ */ package org.springframework.binding.convert.service; +import java.util.AbstractList; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -25,10 +28,10 @@ import junit.framework.TestCase; import org.springframework.binding.convert.ConversionExecutionException; import org.springframework.binding.convert.ConversionExecutor; import org.springframework.binding.convert.ConversionExecutorNotFoundException; -import org.springframework.binding.convert.Converter; -import org.springframework.binding.convert.converters.FormatterConverter; -import org.springframework.binding.convert.converters.TextToBoolean; -import org.springframework.binding.format.formatters.IntegerFormatter; +import org.springframework.binding.convert.converters.Converter; +import org.springframework.binding.convert.converters.FormattedStringToNumber; +import org.springframework.binding.convert.converters.StringToBoolean; +import org.springframework.binding.format.DefaultNumberFormatFactory; /** * Test case for the default conversion service. @@ -39,10 +42,8 @@ public class DefaultConversionServiceTests extends TestCase { public void testConvertCompatibleTypes() { DefaultConversionService service = new DefaultConversionService(); - List lst = new ArrayList(); assertSame(lst, service.getConversionExecutor(ArrayList.class, List.class).execute(lst)); - try { service.getConversionExecutor(List.class, ArrayList.class); fail(); @@ -52,10 +53,8 @@ public class DefaultConversionServiceTests extends TestCase { } public void testOverrideConverter() { - Converter customConverter = new TextToBoolean("ja", "nee"); - + Converter customConverter = new StringToBoolean("ja", "nee"); DefaultConversionService service = new DefaultConversionService(); - StaticConversionExecutor executor = (StaticConversionExecutor) service.getConversionExecutor(String.class, Boolean.class); assertNotSame(customConverter, executor.getConverter()); @@ -65,9 +64,7 @@ public class DefaultConversionServiceTests extends TestCase { } catch (ConversionExecutionException e) { // expected } - service.addConverter(customConverter); - executor = (StaticConversionExecutor) service.getConversionExecutor(String.class, Boolean.class); assertSame(customConverter, executor.getConverter()); assertTrue(((Boolean) executor.execute("ja")).booleanValue()); @@ -87,21 +84,204 @@ public class DefaultConversionServiceTests extends TestCase { ConversionExecutor executor = service.getConversionExecutor(String.class, Integer.class); Integer three = (Integer) executor.execute("3"); assertEquals(3, three.intValue()); + + ConversionExecutor executor2 = service.getConversionExecutor(Integer.class, String.class); + String threeString = (String) executor2.execute(new Integer(3)); + assertEquals("3", threeString); } public void testRegisterConverter() { GenericConversionService service = new GenericConversionService(); - IntegerFormatter formatter = new IntegerFormatter(Integer.class); - formatter.setLocale(Locale.US); - FormatterConverter converter = new FormatterConverter(formatter); + FormattedStringToNumber converter = new FormattedStringToNumber(Integer.class); + DefaultNumberFormatFactory numberFormatFactory = new DefaultNumberFormatFactory(); + numberFormatFactory.setLocale(Locale.US); + converter.setNumberFormatFactory(numberFormatFactory); service.addConverter(converter); ConversionExecutor executor = service.getConversionExecutor(String.class, Integer.class); Integer three = (Integer) executor.execute("3,000"); assertEquals(3000, three.intValue()); - ConversionExecutor executor2 = service.getConversionExecutor(Integer.class, String.class); String string = (String) executor2.execute(new Integer(3000)); assertEquals("3,000", string); - } + + public void testConversionPrimitive() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(String.class, int.class); + Integer three = (Integer) executor.execute("3"); + assertEquals(3, three.intValue()); + } + + public void testArrayConversion() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(String[].class, Integer[].class); + Integer[] result = (Integer[]) executor.execute(new String[] { "1", "2", "3" }); + assertEquals(new Integer(1), result[0]); + assertEquals(new Integer(2), result[1]); + assertEquals(new Integer(3), result[2]); + } + + public void testPrimitiveArrayConversion() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(String[].class, int[].class); + int[] result = (int[]) executor.execute(new String[] { "1", "2", "3" }); + assertEquals(1, result[0]); + assertEquals(2, result[1]); + assertEquals(3, result[2]); + } + + public void testArrayListConversion() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(String[].class, List.class); + List result = (List) executor.execute(new String[] { "1", "2", "3" }); + assertEquals("1", result.get(0)); + assertEquals("2", result.get(1)); + assertEquals("3", result.get(2)); + } + + public void testListArrayConversion() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(Collection.class, String[].class); + List list = new ArrayList(); + list.add("1"); + list.add("2"); + list.add("3"); + String[] result = (String[]) executor.execute(list); + assertEquals("1", result[0]); + assertEquals("2", result[1]); + assertEquals("3", result[2]); + } + + public void testListArrayConversionWithComponentConversion() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(Collection.class, Integer[].class); + List list = new ArrayList(); + list.add("1"); + list.add("2"); + list.add("3"); + Integer[] result = (Integer[]) executor.execute(list); + assertEquals(new Integer(1), result[0]); + assertEquals(new Integer(2), result[1]); + assertEquals(new Integer(3), result[2]); + } + + public void testArrayLinkedListConversion() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(String[].class, LinkedList.class); + LinkedList result = (LinkedList) executor.execute(new String[] { "1", "2", "3" }); + assertEquals("1", result.get(0)); + assertEquals("2", result.get(1)); + assertEquals("3", result.get(2)); + } + + public void testArrayAbstractListConversion() { + DefaultConversionService service = new DefaultConversionService(); + try { + service.getConversionExecutor(String[].class, AbstractList.class); + } catch (IllegalArgumentException e) { + + } + } + + public void testToArrayConversion() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(String.class, String[].class); + String[] result = (String[]) executor.execute("1,2,3"); + assertEquals("1", result[0]); + assertEquals("2", result[1]); + assertEquals("3", result[2]); + } + + public void testToArrayConversionWithElementConversion() { + DefaultConversionService service = new DefaultConversionService(); + ConversionExecutor executor = service.getConversionExecutor(String.class, Integer[].class); + Integer[] result = (Integer[]) executor.execute("1,2,3"); + assertEquals(new Integer(1), result[0]); + assertEquals(new Integer(2), result[1]); + assertEquals(new Integer(3), result[2]); + } + + /* + * + * public void testArrayListConversionWithElementConversion() throws Exception { DefaultConversionService service = + * new DefaultConversionService(); ConversionExecutor executor = service.getConversionExecutor(String[].class, + * IntegerArrayList.class); List result = (List) executor.execute(new String[] { "1", "2", "3" }); assertEquals(new + * Integer(1), result.get(0)); assertEquals(new Integer(2), result.get(1)); assertEquals(new Integer(3), + * result.get(2)); } + * + * + * public static class IntegerArrayList implements List { + * + * private ArrayList realList = new ArrayList(); + * + * public IntegerArrayList() { } + * + * public void add(int index, Integer element) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public boolean add(Integer o) { return realList.add(o); } + * + * public boolean addAll(Collection c) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public boolean addAll(int index, Collection c) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public void clear() { // TODO Auto-generated method stub throw new UnsupportedOperationException("Auto-generated + * method stub"); } + * + * public boolean contains(Object o) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public boolean containsAll(Collection c) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public Integer get(int index) { return (Integer) realList.get(index); } + * + * public int indexOf(Object o) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public boolean isEmpty() { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public Iterator iterator() { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public int lastIndexOf(Object o) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public ListIterator listIterator() { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public ListIterator listIterator(int index) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public Integer remove(int index) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public boolean remove(Object o) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public boolean removeAll(Collection c) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public boolean retainAll(Collection c) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public Integer set(int index, Integer element) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public int size() { // TODO Auto-generated method stub throw new UnsupportedOperationException("Auto-generated + * method stub"); } + * + * public List subList(int fromIndex, int toIndex) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public Object[] toArray() { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } + * + * public T[] toArray(T[] a) { // TODO Auto-generated method stub throw new + * UnsupportedOperationException("Auto-generated method stub"); } } + */ + } \ No newline at end of file diff --git a/spring-binding/src/test/java/org/springframework/binding/convert/service/StaticConversionExecutorImplTests.java b/spring-binding/src/test/java/org/springframework/binding/convert/service/StaticConversionExecutorImplTests.java index 5508204c..8c6fa4ca 100644 --- a/spring-binding/src/test/java/org/springframework/binding/convert/service/StaticConversionExecutorImplTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/convert/service/StaticConversionExecutorImplTests.java @@ -20,14 +20,14 @@ import java.util.Date; import junit.framework.TestCase; import org.springframework.binding.convert.ConversionExecutionException; -import org.springframework.binding.convert.Converter; +import org.springframework.binding.convert.converters.StringToDate; public class StaticConversionExecutorImplTests extends TestCase { private StaticConversionExecutor conversionExecutor; protected void setUp() throws Exception { - conversionExecutor = new StaticConversionExecutor(String.class, Date.class, new TestTextToDate()); + conversionExecutor = new StaticConversionExecutor(String.class, Date.class, new StringToDate()); } public void testTypeConversion() { @@ -51,20 +51,4 @@ public class StaticConversionExecutorImplTests extends TestCase { // expected } } - - private class TestTextToDate implements Converter { - - public Class[] getSourceClasses() { - return new Class[] { String.class }; - } - - public Class[] getTargetClasses() { - return new Class[] { Date.class }; - } - - public Object convert(Object source, Class targetClass, Object context) throws Exception { - return source == null ? null : new Date(); - } - } - } diff --git a/spring-binding/src/test/java/org/springframework/binding/format/formatters/BooleanFormatterTests.java b/spring-binding/src/test/java/org/springframework/binding/format/formatters/BooleanFormatterTests.java deleted file mode 100644 index 680d4f56..00000000 --- a/spring-binding/src/test/java/org/springframework/binding/format/formatters/BooleanFormatterTests.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.springframework.binding.format.formatters; - -import junit.framework.TestCase; - -import org.springframework.binding.format.InvalidFormatException; - -public class BooleanFormatterTests extends TestCase { - - private BooleanFormatter formatter = new BooleanFormatter(); - - public void testFormatTrue() { - assertEquals("true", formatter.format(Boolean.TRUE)); - } - - public void testFormatFalse() { - assertEquals("false", formatter.format(Boolean.FALSE)); - } - - public void testFormatNull() { - assertEquals("", formatter.format(null)); - } - - public void testParseTrue() { - assertEquals(Boolean.TRUE, formatter.parse("true")); - } - - public void testParseFalse() { - assertEquals(Boolean.FALSE, formatter.parse("false")); - } - - public void testParseInvalid() { - try { - formatter.parse("bogus"); - fail("Should have failed"); - } catch (InvalidFormatException e) { - } - } - - public void testParseNull() { - assertNull(formatter.parse(null)); - } - - public void testParseEmptyString() { - assertNull(formatter.parse("")); - } - -} diff --git a/spring-binding/src/test/java/org/springframework/binding/format/formatters/DateFormatterTests.java b/spring-binding/src/test/java/org/springframework/binding/format/formatters/DateFormatterTests.java deleted file mode 100644 index 79f8565d..00000000 --- a/spring-binding/src/test/java/org/springframework/binding/format/formatters/DateFormatterTests.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.springframework.binding.format.formatters; - -import java.util.Calendar; -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.Locale; - -import junit.framework.TestCase; - -import org.springframework.binding.format.InvalidFormatException; - -public class DateFormatterTests extends TestCase { - - public void testFormatDefaultPattern() { - DateFormatter dateFormatter = new DateFormatter(); - dateFormatter.setLocale(new Locale("nl")); - Calendar calendar = new GregorianCalendar(2008, 3, 1); - assertEquals("2008-04-01", dateFormatter.format(calendar.getTime())); - } - - public void testFormatCustomPattern() { - DateFormatter dateFormatter = new DateFormatter(); - dateFormatter.setPattern("MM-dd-yyyy"); - Calendar calendar = new GregorianCalendar(2008, 3, 1); - assertEquals("04-01-2008", dateFormatter.format(calendar.getTime())); - } - - public void testFormatNull() { - DateFormatter dateFormatter = new DateFormatter(); - assertEquals("", dateFormatter.format(null)); - } - - public void testParseDefaultPattern() { - DateFormatter dateFormatter = new DateFormatter(); - Calendar calendar = new GregorianCalendar(); - calendar.setTime((Date) dateFormatter.parse("2008-04-01")); - assertEquals(2008, calendar.get(Calendar.YEAR)); - assertEquals(3, calendar.get(Calendar.MONTH)); - assertEquals(1, calendar.get(Calendar.DAY_OF_MONTH)); - } - - public void testParseInvalidFormat() { - DateFormatter dateFormatter = new DateFormatter(); - try { - dateFormatter.parse("01/04/08"); - fail("Should have failed"); - } catch (InvalidFormatException e) { - - } - } - - public void testParseNull() { - DateFormatter dateFormatter = new DateFormatter(); - assertNull(dateFormatter.parse(null)); - } - - public void testParseEmptyString() { - DateFormatter dateFormatter = new DateFormatter(); - assertNull(dateFormatter.parse("")); - } - -} diff --git a/spring-binding/src/test/java/org/springframework/binding/format/formatters/NumberFormatterTests.java b/spring-binding/src/test/java/org/springframework/binding/format/formatters/NumberFormatterTests.java deleted file mode 100644 index c4038b45..00000000 --- a/spring-binding/src/test/java/org/springframework/binding/format/formatters/NumberFormatterTests.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.springframework.binding.format.formatters; - -import java.math.BigDecimal; -import java.util.Locale; - -import junit.framework.TestCase; - -import org.springframework.binding.format.InvalidFormatException; - -public class NumberFormatterTests extends TestCase { - - private NumberFormatter formatter; - - public void testFormatIntegerDefaultPattern() { - formatter = new NumberFormatter(Integer.class); - formatter.setLocale(Locale.US); - String value = formatter.format(new Integer(12345)); - assertEquals("12,345", value); - } - - public void testFormatBigDecimalCustomPattern() { - formatter = new NumberFormatter(BigDecimal.class); - formatter.setPattern("000.00"); - formatter.setLocale(Locale.US); - BigDecimal dec = new BigDecimal("123.45"); - String value = formatter.format(dec); - assertEquals("123.45", value); - } - - public void testFormatNull() { - formatter = new NumberFormatter(Integer.class); - assertEquals("", formatter.format(null)); - } - - public void testParseIntegerDefaultPattern() { - formatter = new NumberFormatter(Integer.class); - formatter.setLocale(Locale.US); - Integer integer = (Integer) formatter.parse("123,450"); - assertEquals(Integer.valueOf(123450), integer); - } - - public void testParseBigDecimalCustomPattern() { - formatter = new NumberFormatter(BigDecimal.class); - formatter.setPattern("000.00"); - formatter.setLocale(Locale.US); - BigDecimal dec = (BigDecimal) formatter.parse("123.45"); - assertEquals(new BigDecimal("123.45"), dec); - } - - public void testParseInvalidFormatPatternTruncation() { - try { - formatter = new NumberFormatter(Integer.class); - formatter.setLocale(Locale.US); - formatter.parse("123,450b"); - fail("Should have failed"); - } catch (InvalidFormatException e) { - } - } - - public void testParseInvalidFormatPatternTruncationInteger() { - try { - formatter = new IntegerFormatter(Integer.class); - formatter.setLocale(Locale.US); - formatter.parse("123,450.00"); - fail("Should have failed"); - } catch (InvalidFormatException e) { - - } - } - - public void testParseInvalidFormatPatternLenient() { - formatter = new NumberFormatter(Integer.class); - formatter.setLocale(Locale.US); - formatter.setLenient(true); - Integer integer = (Integer) formatter.parse("123,450b"); - assertEquals(Integer.valueOf(123450), integer); - } - - public void testParseInvalidFormatPattern() { - try { - formatter = new NumberFormatter(BigDecimal.class); - formatter.setPattern("000.00"); - formatter.parse("bogus"); - fail("Should have failed"); - } catch (InvalidFormatException e) { - } - } - - public void testParseNull() { - formatter = new NumberFormatter(Integer.class); - assertNull(formatter.parse(null)); - } - - public void testParseEmptyString() { - formatter = new NumberFormatter(Integer.class); - assertNull(formatter.parse("")); - } - -} diff --git a/spring-binding/src/test/java/org/springframework/binding/format/impl/GenericFormatterRegistryTests.java b/spring-binding/src/test/java/org/springframework/binding/format/impl/GenericFormatterRegistryTests.java deleted file mode 100644 index 0606a632..00000000 --- a/spring-binding/src/test/java/org/springframework/binding/format/impl/GenericFormatterRegistryTests.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.springframework.binding.format.impl; - -import java.math.BigDecimal; - -import junit.framework.TestCase; - -import org.springframework.binding.format.Formatter; -import org.springframework.binding.format.InvalidFormatException; -import org.springframework.binding.format.formatters.NumberFormatter; -import org.springframework.binding.format.registry.GenericFormatterRegistry; - -public class GenericFormatterRegistryTests extends TestCase { - - GenericFormatterRegistry registry = new GenericFormatterRegistry(); - - public void testRegisterAndGetFormatter() { - registry.registerFormatter(new NumberFormatter(Integer.class)); - Formatter formatter = registry.getFormatter(Integer.class); - Integer value = (Integer) formatter.parse("3"); - assertEquals(new Integer(3), value); - } - - public void testRegisterAndGetFormatterAbstractClass() { - registry.registerFormatter(new NumberFormatter(Number.class)); - Formatter formatter = registry.getFormatter(Long.class); - assertNull(formatter); - } - - public void testRegisterAndGetFormatterPrimitive() { - registry.registerFormatter(new NumberFormatter(Integer.class)); - Formatter formatter = registry.getFormatter(int.class); - Integer value = (Integer) formatter.parse("3"); - assertEquals(new Integer(3), value); - } - - public void testRegisterAndGetFormatterInterface() { - registry.registerFormatter(new CustomTypeFormatter()); - Formatter formatter = registry.getFormatter(CustomType.class); - assertEquals("12345", formatter.format(new DefaultCustomType("12345"))); - assertEquals(new DefaultCustomType("12345"), formatter.parse("12345")); - } - - public void testRegisterCustomFormatter() { - registry.registerFormatter(new NumberFormatter(Integer.class)); - NumberFormatter percentFormatter = new NumberFormatter(BigDecimal.class); - percentFormatter.setPattern("00%"); - registry.registerFormatter("percent", percentFormatter); - Formatter formatter = registry.getFormatter(BigDecimal.class, "percent"); - assertEquals("35%", formatter.format(new BigDecimal(".35"))); - BigDecimal value = (BigDecimal) formatter.parse("35%"); - assertEquals(new BigDecimal(".35"), value); - } - - public void testRegisterCustomFormatterBogusLookupId() { - registry.registerFormatter(new NumberFormatter(Integer.class)); - registry.registerFormatter("double", new NumberFormatter(Double.class)); - Formatter formatter = registry.getFormatter(Integer.class, "bogusFormat"); - assertNull(formatter); - formatter = registry.getFormatter(Double.class, "double"); - assertNotNull(formatter); - } - - public void testRegisterCustomFormatterBogusClass() { - registry.registerFormatter("double", new NumberFormatter(Double.class)); - try { - registry.getFormatter(Integer.class, "double"); - fail("Should have failed"); - } catch (IllegalArgumentException e) { - - } - } - - public interface CustomType { - public String getText(); - } - - public class DefaultCustomType implements CustomType { - - private String text; - - public DefaultCustomType(String text) { - this.text = text; - } - - public boolean equals(Object o) { - DefaultCustomType other = (DefaultCustomType) o; - return text.equals(other.text); - } - - public String getText() { - return text; - } - } - - private class CustomTypeFormatter implements Formatter { - - public Class getObjectType() { - return CustomType.class; - } - - public String format(Object value) throws IllegalArgumentException { - CustomType type = (CustomType) value; - return type.getText(); - } - - public Object parse(String formattedString) throws InvalidFormatException { - return new DefaultCustomType(formattedString); - } - - } - -} diff --git a/spring-binding/src/test/java/org/springframework/binding/mapping/DefaultMapperTests.java b/spring-binding/src/test/java/org/springframework/binding/mapping/DefaultMapperTests.java index 731e678e..5b2cf91c 100644 --- a/spring-binding/src/test/java/org/springframework/binding/mapping/DefaultMapperTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/mapping/DefaultMapperTests.java @@ -4,7 +4,6 @@ import java.util.Locale; import junit.framework.TestCase; -import org.springframework.binding.convert.Converter; import org.springframework.binding.convert.service.DefaultConversionService; import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.expression.el.DefaultExpressionFactoryUtils; @@ -12,7 +11,6 @@ import org.springframework.binding.expression.el.ELExpressionParser; import org.springframework.binding.expression.support.FluentParserContext; import org.springframework.binding.mapping.impl.DefaultMapper; import org.springframework.binding.mapping.impl.DefaultMapping; -import org.springframework.util.StringUtils; public class DefaultMapperTests extends TestCase { private DefaultMapper mapper = new DefaultMapper(); @@ -64,19 +62,6 @@ public class DefaultMapperTests extends TestCase { public void testMappingWithCustomConversionService() { DefaultConversionService conversionService = new DefaultConversionService(); - conversionService.addConverter(new Converter() { - public Class[] getSourceClasses() { - return new Class[] { String.class }; - } - - public Class[] getTargetClasses() { - return new Class[] { Locale.class }; - } - - public Object convert(Object source, Class targetClass, Object context) throws Exception { - return StringUtils.parseLocaleString((String) source); - } - }); mapper.setConversionService(conversionService); DefaultMapping mapping1 = new DefaultMapping(parser.parseExpression("foo", null), parser.parseExpression( "beep", null));