formatter simplifications from code review

This commit is contained in:
Keith Donald
2008-03-26 03:47:54 +00:00
parent 0965f93c97
commit b1b7de9a30
28 changed files with 325 additions and 715 deletions

View File

@@ -1,50 +0,0 @@
/*
* Copyright 2004-2007 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.format.FormatterRegistry;
import org.springframework.util.Assert;
/**
* A converter that delegates to a formatter to perform the conversion. Formatters are typically not thread safe, so we
* use a FormatterRegistry that is expected to provide us with instances local to the current thread.
*
* @author Keith Donald
*/
public abstract class AbstractFormattingConverter extends AbstractConverter {
/**
* The formatter registry.
*/
private FormatterRegistry formatterRegistry;
/**
* Creates a new converter that delegates to a formatter.
* @param formatterRegistry the formatterRegistry to use
*/
protected AbstractFormattingConverter(FormatterRegistry formatterRegistry) {
Assert.notNull(formatterRegistry, "The formatter registry is required");
this.formatterRegistry = formatterRegistry;
}
public FormatterRegistry getFormatterRegistry() {
return formatterRegistry;
}
protected Object doConvert(Object source, Class targetClass, Object context) throws Exception {
return getFormatterRegistry().getFormatter(targetClass).parseValue((String) source);
}
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2004-2007 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.util.Date;
import org.springframework.binding.format.FormatterRegistry;
/**
* Converts textual representations of numbers to a <code>Number</code> specialization. Delegates to a synchronized
* formatter to parse text strings.
*
* @author Keith Donald
*/
public class TextToDate extends AbstractFormattingConverter {
/**
* Create a string to number converter using given formatter factory.
* @param formatterRegistry the formatter registry to use
*/
public TextToDate(FormatterRegistry formatterRegistry) {
super(formatterRegistry);
}
public Class[] getSourceClasses() {
return new Class[] { String.class };
}
public Class[] getTargetClasses() {
return new Class[] { Date.class };
}
}

View File

@@ -18,7 +18,7 @@ package org.springframework.binding.convert.converters;
import java.math.BigDecimal;
import java.math.BigInteger;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.util.NumberUtils;
/**
* Converts textual representations of numbers to a <code>Number</code> specialization. Delegates to a synchronized
@@ -26,15 +26,7 @@ import org.springframework.binding.format.FormatterRegistry;
*
* @author Keith Donald
*/
public class TextToNumber extends AbstractFormattingConverter {
/**
* Create a string to number converter using given formatter factory.
* @param formatterRegistry the formatter registry to use
*/
public TextToNumber(FormatterRegistry formatterRegistry) {
super(formatterRegistry);
}
public class TextToNumber extends AbstractConverter {
public Class[] getSourceClasses() {
return new Class[] { String.class };
@@ -44,4 +36,9 @@ public class TextToNumber extends AbstractFormattingConverter {
return new Class[] { Integer.class, Short.class, Long.class, Float.class, Double.class, Byte.class,
BigInteger.class, BigDecimal.class };
}
protected Object doConvert(Object source, Class targetClass, Object context) throws Exception {
return NumberUtils.parseNumber((String) source, targetClass);
}
}

View File

@@ -18,13 +18,8 @@ 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.TextToDate;
import org.springframework.binding.convert.converters.TextToLabeledEnum;
import org.springframework.binding.convert.converters.TextToNumber;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.binding.format.factories.DateFormatterFactory;
import org.springframework.binding.format.factories.NumberFormatterFactory;
import org.springframework.binding.format.impl.FormatterRegistryImpl;
/**
* Default, local implementation of a conversion service. Will automatically register <i>from string</i> converters for
@@ -37,18 +32,12 @@ public class DefaultConversionService extends GenericConversionService {
/**
* A singleton shared instance. Should never be modified.
*/
private static final DefaultConversionService SHARED_INSTANCE = new DefaultConversionService();
/**
* Returns the formatter registry used by this conversion service
*/
private FormatterRegistry formatterRegistry;
private static DefaultConversionService SHARED_INSTANCE;
/**
* Creates a new default conversion service, installing the default converters.
*/
public DefaultConversionService() {
formatterRegistry = createDefaultFormatterRegistry();
addDefaultConverters();
}
@@ -59,21 +48,16 @@ public class DefaultConversionService extends GenericConversionService {
addConverter(new TextToClass());
addConverter(new TextToBoolean());
addConverter(new TextToLabeledEnum());
addConverter(new TextToNumber(formatterRegistry));
addConverter(new TextToDate(formatterRegistry));
}
protected FormatterRegistry createDefaultFormatterRegistry() {
FormatterRegistryImpl registry = new FormatterRegistryImpl();
registry.registerFormatter(new NumberFormatterFactory());
registry.registerFormatter(new DateFormatterFactory());
return registry;
addConverter(new TextToNumber());
}
/**
* Returns the shared {@link DefaultConversionService} instance.
*/
public static ConversionService getSharedInstance() {
public synchronized static ConversionService getSharedInstance() {
if (SHARED_INSTANCE == null) {
SHARED_INSTANCE = new DefaultConversionService();
}
return SHARED_INSTANCE;
}
}

View File

@@ -16,29 +16,26 @@
package org.springframework.binding.format;
/**
* A lightweight interface for formatting a value and parsing a value from its formatted form.
* <p>
* Note: formatters are typically not thread safe as <code>Format</code> objects aren't thread safe. In general, you
* should not attempt to share formatters between threads.
* </p>
* Formats objects for display.
*
* @author Keith Donald
*/
public interface Formatter {
/**
* Format the value.
* @param value the value to format
* Format the object for display.
* @param object the object to format
* @return the formatted string, fit for display in a UI
* @throws IllegalArgumentException the value could not be formatted
* @throws IllegalArgumentException if the object could not be formatted
*/
public String formatValue(Object value) throws IllegalArgumentException;
public String format(Object object) throws IllegalArgumentException;
/**
* Parse the formatted string representation of a value, restoring the value.
* Parse the formatted string representation of an object and return the object.
* @param formattedString the formatted string representation
* @return the parsed value
* @throws InvalidFormatException the string was in an invalid form
* @return the parsed object
* @throws InvalidFormatException the formatted string was in an invalid form
*/
public Object parseValue(String formattedString) throws InvalidFormatException;
public Object parse(String formattedString) throws InvalidFormatException;
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2004-2007 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 factory for creating stateful formatter instances.
*
* @author Keith Donald
*/
public interface FormatterFactory {
/**
* The type of object the returned formatters are capable of formatting. This is the most general type supported,
* some formatters may apply specific formatting rules to subclasses of this general type.
* @return the formatted class
*/
public Class getFormattedClass();
/**
* Create a new formatter.
* @param context context for formatter creation
* @return the new formatter
*/
public Formatter createFormatter(FormatterFactoryContext context);
}

View File

@@ -1,21 +0,0 @@
package org.springframework.binding.format;
import java.util.Locale;
/**
* A context for creating a formatter instance.
*
* @author Keith Donald
*/
public interface FormatterFactoryContext {
/**
* The current locale.
*/
public Locale getLocale();
/**
* The type of object of which formatting is desired.
*/
public Class getFormattedClass();
}

View File

@@ -38,9 +38,8 @@ public interface FormatterRegistry {
* 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 id the id of the custom formatter instance; typically descriptive like "localDate"
* @param clazz the class being formatted e.g. Date.class.
* @return the formatter
*/
public Formatter getFormatter(String id, Class clazz);
public Formatter getFormatter(String id);
}

View File

@@ -40,10 +40,10 @@ public class FormatterPropertyEditor extends PropertyEditorSupport {
}
public String getAsText() {
return formatter.formatValue(getValue());
return formatter.format(getValue());
}
public void setAsText(String text) throws IllegalArgumentException {
setValue(formatter.parseValue(text));
setValue(formatter.parse(text));
}
}

View File

@@ -17,20 +17,19 @@ package org.springframework.binding.format.adapters;
import java.beans.PropertyEditor;
import org.springframework.binding.format.factories.AbstractFormatter;
import org.springframework.binding.format.Formatter;
import org.springframework.util.Assert;
/**
* Adapts a property editor to the formatter interface.
*
* @author Keith Donald
*/
public class PropertyEditorFormatter extends AbstractFormatter {
public class PropertyEditorFormatter implements Formatter {
private PropertyEditor propertyEditor;
/**
* Wrap given property editor in a formatter.
* Wrap the given property editor in a formatter.
*/
public PropertyEditorFormatter(PropertyEditor propertyEditor) {
Assert.notNull(propertyEditor, "Property editor is required");
@@ -44,18 +43,13 @@ public class PropertyEditorFormatter extends AbstractFormatter {
return propertyEditor;
}
protected String doFormatValue(Object value) {
public String format(Object value) {
propertyEditor.setValue(value);
return propertyEditor.getAsText();
}
protected Object doParseValue(String formattedValue) {
public Object parse(String formattedValue) {
propertyEditor.setAsText(formattedValue);
return propertyEditor.getValue();
}
protected String getExpectedFormat() {
return null;
}
}

View File

@@ -1,104 +0,0 @@
/*
* Copyright 2004-2007 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.factories;
import java.text.ParseException;
import org.springframework.binding.format.Formatter;
import org.springframework.binding.format.InvalidFormatException;
import org.springframework.util.StringUtils;
/**
* Abstract base class for all formatters.
*
* @author Keith Donald
*/
public abstract class AbstractFormatter implements Formatter {
/**
* Constructs a formatter.
*/
protected AbstractFormatter() {
}
public final String formatValue(Object value) {
if (isEmpty(value)) {
return getEmptyFormattedValue();
} else {
return doFormatValue(value);
}
}
public final Object parseValue(String formattedString) throws InvalidFormatException {
try {
if (isEmpty(formattedString)) {
return getEmptyValue();
}
return doParseValue(formattedString);
} catch (ParseException ex) {
throw new InvalidFormatException(formattedString, getExpectedFormat(), ex);
}
}
/**
* Template method subclasses should override to encapsulate formatting logic.
* @param value the value to format
* @return the formatted string representation
*/
protected abstract String doFormatValue(Object value);
/**
* Returns the formatted form of an empty value. Default implementation just returns the empty string.
*/
protected String getEmptyFormattedValue() {
return "";
}
/**
* Template method subclasses should override to encapsulate parsing logic.
* @param formattedString the formatted string to parse
* @return the parsed value
* @throws InvalidFormatException an exception occurred parsing
* @throws ParseException when parse exceptions occur
*/
protected abstract Object doParseValue(String formattedString) throws InvalidFormatException, ParseException;
/**
* Returns the empty value (resulting from parsing an empty input string). This default implementation just returns
* null.
*/
protected Object getEmptyValue() {
return null;
}
/**
* Returns the expected string format for the given target class.
*/
protected abstract String getExpectedFormat();
/**
* Is given object <i>empty</i> (null or empty string)?
*/
protected boolean isEmpty(Object o) {
if (o == null) {
return true;
} else if (o instanceof String) {
return !StringUtils.hasText((String) o);
} else {
return false;
}
}
}

View File

@@ -1,49 +0,0 @@
package org.springframework.binding.format.factories;
import org.springframework.binding.format.Formatter;
import org.springframework.binding.format.FormatterFactory;
import org.springframework.binding.format.FormatterFactoryContext;
import org.springframework.binding.format.InvalidFormatException;
import org.springframework.util.StringUtils;
public class BooleanFormatterFactory implements FormatterFactory {
public Class getFormattedClass() {
return Boolean.class;
}
public Formatter createFormatter(FormatterFactoryContext context) {
return new BooleanFormatter();
}
public static class BooleanFormatter implements Formatter {
public String formatValue(Object value) throws IllegalArgumentException {
if (value == null) {
return "";
}
if (Boolean.TRUE.equals(value)) {
return "true";
} else if (Boolean.FALSE.equals(value)) {
return "false";
} else {
throw new IllegalArgumentException("Must be a Boolean " + value);
}
}
public Object parseValue(String formattedString) throws InvalidFormatException {
formattedString = (formattedString != null ? formattedString.trim() : null);
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");
}
}
}
}

View File

@@ -1,68 +0,0 @@
package org.springframework.binding.format.factories;
import java.text.DateFormat;
import java.text.ParseException;
import java.util.Date;
import org.springframework.binding.format.Formatter;
import org.springframework.binding.format.FormatterFactory;
import org.springframework.binding.format.FormatterFactoryContext;
import org.springframework.core.style.ToStringCreator;
/**
* Factory for date formatters.
*
* @author Keith Donald
*/
public class DateFormatterFactory implements FormatterFactory {
public Class getFormattedClass() {
return Date.class;
}
public Formatter createFormatter(FormatterFactoryContext context) {
return new DateFormatter(getDateFormat(context));
}
/**
* Returns the date format to use with the formatter being created. Subclasses may override.
* @param context the factory context
* @return the date format
*/
protected DateFormat getDateFormat(FormatterFactoryContext context) {
return DateFormat.getDateInstance(DateFormat.SHORT, context.getLocale());
}
public static class DateFormatter extends AbstractFormatter {
private DateFormat dateFormat;
/**
* Constructs a date formatter that will delegate to the specified date format.
* @param dateFormat the date format to use
*/
public DateFormatter(DateFormat dateFormat) {
this.dateFormat = dateFormat;
}
// convert from date to string
protected String doFormatValue(Object date) {
return dateFormat.format((Date) date);
}
// convert back from string to date
protected Object doParseValue(String formattedString) throws ParseException {
return dateFormat.parse(formattedString);
}
protected String getExpectedFormat() {
return dateFormat.toString();
}
public String toString() {
return new ToStringCreator(this).append("format", dateFormat).toString();
}
}
}

View File

@@ -1,84 +0,0 @@
package org.springframework.binding.format.factories;
import java.text.NumberFormat;
import org.springframework.binding.format.Formatter;
import org.springframework.binding.format.FormatterFactory;
import org.springframework.binding.format.FormatterFactoryContext;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.NumberUtils;
/**
* Factory for number formatters.
*
* @author Keith Donald
*/
public class NumberFormatterFactory implements FormatterFactory {
public Class getFormattedClass() {
return Number.class;
}
public Formatter createFormatter(FormatterFactoryContext context) {
return new NumberFormatter(getNumberFormat(context), context.getFormattedClass());
}
/**
* Returns the number format to use with the formatter being created. Subclasses may override.
* @param context the factory context
* @return the number format
*/
protected NumberFormat getNumberFormat(FormatterFactoryContext context) {
if (context.getFormattedClass().equals(Integer.class)) {
return NumberFormat.getIntegerInstance(context.getLocale());
} else {
return NumberFormat.getNumberInstance(context.getLocale());
}
}
public static class NumberFormatter extends AbstractFormatter {
private NumberFormat numberFormat;
private Class targetClass;
/**
* Create a new number formatter.
* @param numberFormat the number format to use
* @param targetClass the number class to parse into
*/
public NumberFormatter(NumberFormat numberFormat, Class targetClass) {
this.numberFormat = numberFormat;
this.targetClass = targetClass;
}
protected String doFormatValue(Object number) {
if (numberFormat != null) {
// use NumberFormat for rendering value
return numberFormat.format(number);
} else {
// use toString method for rendering value
return number.toString();
}
}
protected Object doParseValue(String text) throws IllegalArgumentException {
if (numberFormat != null) {
// use given NumberFormat for parsing text
return NumberUtils.parseNumber(text, targetClass, numberFormat);
} else {
// use default valueOf methods for parsing text
return NumberUtils.parseNumber(text, targetClass);
}
}
protected String getExpectedFormat() {
return numberFormat.toString();
}
public String toString() {
return new ToStringCreator(this).append("format", numberFormat).append("targetClass", targetClass)
.toString();
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2004-2007 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.StringUtils;
public class BooleanFormatter implements Formatter {
public String format(Object value) throws IllegalArgumentException {
if (value == null) {
return "";
}
if (Boolean.TRUE.equals(value)) {
return "true";
} else if (Boolean.FALSE.equals(value)) {
return "false";
} else {
throw new IllegalArgumentException("Must be a Boolean " + value);
}
}
public Object parse(String formattedString) throws InvalidFormatException {
formattedString = (formattedString != null ? formattedString.trim() : null);
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");
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2004-2007 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.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import org.springframework.binding.format.Formatter;
import org.springframework.binding.format.InvalidFormatException;
public class DateFormatter implements Formatter {
private String pattern;
private Locale locale;
public String getPattern() {
return pattern;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public Locale getLocale() {
return locale;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
public String format(Object date) {
return getDateFormat().format((Date) date);
}
public Object parse(String formattedString) throws InvalidFormatException {
DateFormat dateFormat = getDateFormat();
try {
return dateFormat.parse(formattedString);
} catch (ParseException e) {
throw new InvalidFormatException(formattedString, dateFormat.toString());
}
}
protected DateFormat getDateFormat() {
if (pattern != null) {
return new SimpleDateFormat(pattern, locale);
} else {
return DateFormat.getDateInstance();
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2004-2007 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 org.springframework.binding.format.Formatter;
import org.springframework.binding.format.InvalidFormatException;
import org.springframework.util.Assert;
import org.springframework.util.NumberUtils;
public class NumberFormatter implements Formatter {
private String pattern;
private Class numberClass;
public NumberFormatter(Class numberClass) {
Assert.notNull(numberClass, "The number class is required");
this.numberClass = numberClass;
}
public void setPattern(String pattern) {
this.pattern = pattern;
}
public String format(Object number) {
if (pattern != null) {
return getNumberFormat().format(number);
} else {
return number.toString();
}
}
public Object parse(String text) throws InvalidFormatException {
if (pattern != null) {
return NumberUtils.parseNumber(text, numberClass, getNumberFormat());
} else {
return NumberUtils.parseNumber(text, numberClass);
}
}
private NumberFormat getNumberFormat() {
return new DecimalFormat(pattern);
}
}

View File

@@ -1,29 +0,0 @@
package org.springframework.binding.format.impl;
import java.util.Locale;
import org.springframework.binding.format.FormatterFactoryContext;
public class FormatterFactoryContextImpl implements FormatterFactoryContext {
private Class formattedClass;
private Locale locale;
public Class getFormattedClass() {
return formattedClass;
}
public Locale getLocale() {
return locale;
}
public void setFormattedClass(Class formattedClass) {
this.formattedClass = formattedClass;
}
public void setLocale(Locale locale) {
this.locale = locale;
}
}

View File

@@ -0,0 +1,46 @@
package org.springframework.binding.format.registry;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Date;
import org.springframework.binding.convert.service.DefaultConversionService;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.binding.format.formatters.BooleanFormatter;
import org.springframework.binding.format.formatters.DateFormatter;
import org.springframework.binding.format.formatters.NumberFormatter;
public class DefaultFormatterRegistry extends FormatterRegistryImpl {
/**
* A singleton shared instance. Should never be modified.
*/
private static DefaultFormatterRegistry SHARED_INSTANCE;
public DefaultFormatterRegistry() {
registerDefaultFormatters();
}
protected void registerDefaultFormatters() {
registerFormatter(Integer.class, new NumberFormatter(Integer.class));
registerFormatter(Long.class, new NumberFormatter(Long.class));
registerFormatter(Short.class, new NumberFormatter(Short.class));
registerFormatter(Float.class, new NumberFormatter(Float.class));
registerFormatter(Double.class, new NumberFormatter(Double.class));
registerFormatter(Byte.class, new NumberFormatter(Byte.class));
registerFormatter(BigInteger.class, new NumberFormatter(BigInteger.class));
registerFormatter(BigDecimal.class, new NumberFormatter(BigDecimal.class));
registerFormatter(Boolean.class, new BooleanFormatter());
registerFormatter(Date.class, new DateFormatter());
}
/**
* Returns the shared {@link DefaultConversionService} instance.
*/
public synchronized static FormatterRegistry getSharedInstance() {
if (SHARED_INSTANCE == null) {
SHARED_INSTANCE = new DefaultFormatterRegistry();
}
return SHARED_INSTANCE;
}
}

View File

@@ -13,19 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.binding.format.impl;
package org.springframework.binding.format.registry;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Map;
import org.springframework.binding.format.Formatter;
import org.springframework.binding.format.FormatterFactory;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.context.i18n.LocaleContext;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.context.i18n.SimpleLocaleContext;
import org.springframework.util.Assert;
/**
@@ -36,81 +31,42 @@ import org.springframework.util.Assert;
*/
public class FormatterRegistryImpl implements FormatterRegistry {
private LocaleContext localeContext = new SimpleLocaleContext(Locale.getDefault());
private Map formattersById = new HashMap();
private Map formattersByClass = new HashMap();
/**
* Sets the locale context to use if no locale has been bound to the current thread. Optional. Defaults to a
* {@link SimpleLocaleContext} holding the system default locale.
* @param localeContext the locale context
*/
public void setLocaleContext(LocaleContext localeContext) {
this.localeContext = localeContext;
}
/**
* Returns the locale in use.
*/
protected Locale getLocale() {
Locale currentLocale = LocaleContextHolder.getLocale();
if (currentLocale != null) {
return currentLocale;
} else {
return localeContext.getLocale();
}
}
public Formatter getFormatter(Class clazz) {
Assert.notNull(clazz, "The formatted class argument is required");
clazz = convertToWrapperClassIfNecessary(clazz);
FormatterFactory factory = findFormatterFactory(clazz);
if (factory != null) {
FormatterFactoryContextImpl context = new FormatterFactoryContextImpl();
context.setLocale(getLocale());
context.setFormattedClass(clazz);
return factory.createFormatter(context);
} else {
return null;
}
return findFormatter(clazz);
}
public Formatter getFormatter(String id, Class clazz) {
public Formatter getFormatter(String id) {
Assert.hasText(id, "The id of the custom formatter is required");
FormatterFactory factory = (FormatterFactory) formattersById.get(id);
if (factory != null) {
FormatterFactoryContextImpl context = new FormatterFactoryContextImpl();
context.setLocale(getLocale());
context.setFormattedClass(clazz);
return factory.createFormatter(context);
} else {
return null;
}
return (Formatter) formattersById.get(id);
}
public void registerFormatter(String id, FormatterFactory factory) {
// impl
public void registerFormatter(Class clazz, Formatter formatter) {
Assert.notNull(formatter, "The formatter to register is required");
formattersByClass.put(clazz, formatter);
}
public void registerFormatter(String id, Formatter formatter) {
Assert.hasText(id, "The id of the custom formatter is required");
Assert.notNull(factory, "The formatter factory is required");
formattersById.put(id, factory);
Class formattedClass = factory.getFormattedClass();
if (!formattersByClass.containsKey(formattedClass)) {
formattersByClass.put(formattedClass, factory);
}
Assert.notNull(formatter, "The formatter to register is required");
formattersById.put(id, formatter);
}
public void registerFormatter(FormatterFactory factory) {
Assert.notNull(factory, "The formatter factory is required");
formattersByClass.put(factory.getFormattedClass(), factory);
}
// helpers
private FormatterFactory findFormatterFactory(Class clazz) {
private Formatter findFormatter(Class clazz) {
LinkedList classQueue = new LinkedList();
classQueue.addFirst(clazz);
while (!classQueue.isEmpty()) {
clazz = (Class) classQueue.removeLast();
FormatterFactory factory = (FormatterFactory) formattersByClass.get(clazz);
Formatter factory = (Formatter) formattersByClass.get(clazz);
if (factory != null) {
return factory;
}

View File

@@ -1,78 +1,64 @@
package org.springframework.binding.format.impl;
import java.math.BigDecimal;
import java.text.NumberFormat;
import junit.framework.TestCase;
import org.springframework.binding.format.Formatter;
import org.springframework.binding.format.FormatterFactory;
import org.springframework.binding.format.FormatterFactoryContext;
import org.springframework.binding.format.InvalidFormatException;
import org.springframework.binding.format.factories.NumberFormatterFactory;
import org.springframework.binding.format.formatters.NumberFormatter;
import org.springframework.binding.format.registry.FormatterRegistryImpl;
public class FormatterRegistryImplTests extends TestCase {
FormatterRegistryImpl registry = new FormatterRegistryImpl();
public void testRegisterAndGetFormatter() {
registry.registerFormatter(new NumberFormatterFactory());
registry.registerFormatter(Integer.class, new NumberFormatter(Integer.class));
Formatter formatter = registry.getFormatter(Integer.class);
Integer value = (Integer) formatter.parseValue("3");
Integer value = (Integer) formatter.parse("3");
assertEquals(new Integer(3), value);
}
public void testRegisterAndGetFormatterAbstractClass() {
registry.registerFormatter(new NumberFormatterFactory());
Formatter formatter = registry.getFormatter(Number.class);
Long value = (Long) formatter.parseValue("3");
assertEquals(new Long(3), value);
registry.registerFormatter(Number.class, new NumberFormatter(Long.class));
Formatter formatter = registry.getFormatter(Long.class);
Number value = (Number) formatter.parse("3");
assertEquals(3L, value.longValue());
}
public void testRegisterAndGetFormatterPrimitive() {
registry.registerFormatter(new NumberFormatterFactory());
registry.registerFormatter(Integer.class, new NumberFormatter(Integer.class));
Formatter formatter = registry.getFormatter(int.class);
Integer value = (Integer) formatter.parseValue("3");
Integer value = (Integer) formatter.parse("3");
assertEquals(new Integer(3), value);
}
public void testRegisterAndGetFormatterInterface() {
registry.registerFormatter(new CustomTypeFormatterFactory());
registry.registerFormatter(CustomType.class, new CustomTypeFormatter());
Formatter formatter = registry.getFormatter(DefaultCustomType.class);
assertEquals("12345", formatter.formatValue(new DefaultCustomType("12345")));
assertEquals(new DefaultCustomType("12345"), formatter.parseValue("12345"));
assertEquals("12345", formatter.format(new DefaultCustomType("12345")));
assertEquals(new DefaultCustomType("12345"), formatter.parse("12345"));
}
public void testRegisterCustomFormatter() {
registry.registerFormatter(new NumberFormatterFactory());
registry.registerFormatter("percentNumberFormat", new PercentNumberFormatterFactory());
Formatter formatter = registry.getFormatter("percentNumberFormat", BigDecimal.class);
assertEquals("35%", formatter.formatValue(new BigDecimal(".35")));
BigDecimal value = (BigDecimal) formatter.parseValue("35%");
registry.registerFormatter(Integer.class, new NumberFormatter(Integer.class));
NumberFormatter percentFormatter = new NumberFormatter(BigDecimal.class);
percentFormatter.setPattern("00%");
registry.registerFormatter("percent", percentFormatter);
Formatter formatter = registry.getFormatter("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 NumberFormatterFactory());
registry.registerFormatter("percentNumberFormat", new PercentNumberFormatterFactory());
Formatter formatter = registry.getFormatter("bogusFormat", BigDecimal.class);
registry.registerFormatter(Integer.class, new NumberFormatter(Integer.class));
registry.registerFormatter("double", new NumberFormatter(Double.class));
Formatter formatter = registry.getFormatter("bogusFormat");
assertNull(formatter);
formatter = registry.getFormatter(BigDecimal.class);
formatter = registry.getFormatter("double");
assertNotNull(formatter);
assertEquals("0.35", formatter.formatValue(new BigDecimal(".35")));
BigDecimal value = (BigDecimal) formatter.parseValue("0.35");
assertEquals(new BigDecimal(".35"), value);
formatter = registry.getFormatter("percentNumberFormat", BigDecimal.class);
assertEquals("35%", formatter.formatValue(new BigDecimal(".35")));
value = (BigDecimal) formatter.parseValue("35%");
assertEquals(new BigDecimal(".35"), value);
}
public class PercentNumberFormatterFactory extends NumberFormatterFactory implements FormatterFactory {
protected NumberFormat getNumberFormat(FormatterFactoryContext context) {
return NumberFormat.getPercentInstance();
}
}
public interface CustomType {
@@ -97,27 +83,15 @@ public class FormatterRegistryImplTests extends TestCase {
}
}
private class CustomTypeFormatterFactory implements FormatterFactory {
private class CustomTypeFormatter implements Formatter {
public Formatter createFormatter(FormatterFactoryContext context) {
return new CustomTypeFormatter();
public String format(Object value) throws IllegalArgumentException {
CustomType type = (CustomType) value;
return type.getText();
}
public Class getFormattedClass() {
return CustomType.class;
}
private class CustomTypeFormatter implements Formatter {
public String formatValue(Object value) throws IllegalArgumentException {
CustomType type = (CustomType) value;
return type.getText();
}
public Object parseValue(String formattedString) throws InvalidFormatException {
return new DefaultCustomType(formattedString);
}
public Object parse(String formattedString) throws InvalidFormatException {
return new DefaultCustomType(formattedString);
}
}

View File

@@ -19,11 +19,7 @@ import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.binding.expression.el.DefaultExpressionFactoryUtils;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.binding.format.factories.BooleanFormatterFactory;
import org.springframework.binding.format.factories.DateFormatterFactory;
import org.springframework.binding.format.factories.NumberFormatterFactory;
import org.springframework.binding.format.impl.FormatterRegistryImpl;
import org.springframework.binding.format.registry.DefaultFormatterRegistry;
import org.springframework.faces.model.converter.FacesConversionService;
import org.springframework.faces.webflow.JsfManagedBeanAwareELExpressionParser;
import org.springframework.faces.webflow.JsfViewFactoryCreator;
@@ -87,7 +83,8 @@ public class FacesFlowBuilderServicesBeanDefinitionParser extends AbstractSingle
if (StringUtils.hasText(formatterRegistry)) {
definitionBuilder.addPropertyReference(FORMATTER_REGISTRY_PROPERTY, formatterRegistry);
} else {
definitionBuilder.addPropertyValue(FORMATTER_REGISTRY_PROPERTY, createDefaultFormatterRegistry());
definitionBuilder.addPropertyValue(FORMATTER_REGISTRY_PROPERTY, DefaultFormatterRegistry
.getSharedInstance());
}
}
@@ -119,12 +116,4 @@ public class FacesFlowBuilderServicesBeanDefinitionParser extends AbstractSingle
}
}
private static FormatterRegistry createDefaultFormatterRegistry() {
FormatterRegistryImpl registry = new FormatterRegistryImpl();
registry.registerFormatter(new NumberFormatterFactory());
registry.registerFormatter(new DateFormatterFactory());
registry.registerFormatter(new BooleanFormatterFactory());
return registry;
}
}

View File

@@ -7,11 +7,7 @@ import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.binding.convert.service.DefaultConversionService;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.binding.format.factories.BooleanFormatterFactory;
import org.springframework.binding.format.factories.DateFormatterFactory;
import org.springframework.binding.format.factories.NumberFormatterFactory;
import org.springframework.binding.format.impl.FormatterRegistryImpl;
import org.springframework.binding.format.registry.DefaultFormatterRegistry;
import org.springframework.util.StringUtils;
import org.springframework.webflow.engine.builder.support.FlowBuilderServices;
import org.springframework.webflow.expression.DefaultExpressionParserFactory;
@@ -57,7 +53,8 @@ class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefiniti
if (StringUtils.hasText(formatterRegistry)) {
definitionBuilder.addPropertyReference(FORMATTER_REGISTRY_PROPERTY, formatterRegistry);
} else {
definitionBuilder.addPropertyValue(FORMATTER_REGISTRY_PROPERTY, createDefaultFormatterRegistry());
definitionBuilder.addPropertyValue(FORMATTER_REGISTRY_PROPERTY, DefaultFormatterRegistry
.getSharedInstance());
}
}
@@ -102,8 +99,8 @@ class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefiniti
public static BeanDefinitionHolder registerDefaultFlowBuilderServicesBeanDefinition(ParserContext context) {
FlowBuilderServicesBeanDefinitionParser parser = new FlowBuilderServicesBeanDefinitionParser();
BeanDefinitionBuilder defaultBuilder = BeanDefinitionBuilder.genericBeanDefinition(FlowBuilderServices.class);
defaultBuilder.addPropertyValue(FORMATTER_REGISTRY_PROPERTY, createDefaultFormatterRegistry());
defaultBuilder.addPropertyValue(CONVERSION_SERVICE_PROPERTY, new DefaultConversionService());
defaultBuilder.addPropertyValue(FORMATTER_REGISTRY_PROPERTY, DefaultFormatterRegistry.getSharedInstance());
defaultBuilder.addPropertyValue(CONVERSION_SERVICE_PROPERTY, DefaultConversionService.getSharedInstance());
defaultBuilder.addPropertyValue(EXPRESSION_PARSER_PROPERTY, DefaultExpressionParserFactory
.getExpressionParser());
defaultBuilder.addPropertyReference(VIEW_FACTORY_CREATOR_PROPERTY, parser.createBeanDefinitionForClass(
@@ -114,12 +111,4 @@ class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefiniti
return holder;
}
private static FormatterRegistry createDefaultFormatterRegistry() {
FormatterRegistryImpl registry = new FormatterRegistryImpl();
registry.registerFormatter(new NumberFormatterFactory());
registry.registerFormatter(new DateFormatterFactory());
registry.registerFormatter(new BooleanFormatterFactory());
return registry;
}
}

View File

@@ -305,7 +305,7 @@ public class LocalParameterMap implements ParameterMap, Serializable {
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
in.defaultReadObject();
parameterAccessor = new MapAccessor(parameters);
conversionService = new DefaultConversionService();
conversionService = DefaultConversionService.getSharedInstance();
}
public String toString() {

View File

@@ -72,7 +72,7 @@ public class BindingModel extends ViewRenderingErrors {
Expression fieldExpression = parseFieldExpression(field);
Formatter formatter = getFormatter(fieldExpression);
if (formatter != null) {
return formatter.formatValue(fieldExpression.getValue(boundObject));
return formatter.format(fieldExpression.getValue(boundObject));
} else {
return fieldExpression.getValue(boundObject);
}

View File

@@ -176,9 +176,10 @@ class MvcView implements View {
private void addDefaultMappings(DefaultMapper mapper, ParameterMap requestParameters, Object model) {
for (Iterator it = requestParameters.asMap().keySet().iterator(); it.hasNext();) {
String name = (String) it.next();
Expression source = expressionParser
.parseExpression(name, new FluentParserContext().evaluate(MapAdaptable.class));
Expression target = expressionParser.parseExpression(name, new FluentParserContext().evaluate(model.getClass()));
Expression source = expressionParser.parseExpression(name, new FluentParserContext()
.evaluate(MapAdaptable.class));
Expression target = expressionParser.parseExpression(name, new FluentParserContext().evaluate(model
.getClass()));
DefaultMapping mapping = new DefaultMapping(source, target);
mapping.setTypeConverter(new FormatterBackedMappingConversionExecutor(formatterRegistry));
mapper.addMapping(mapping);
@@ -273,7 +274,7 @@ class MvcView implements View {
Formatter formatter = getFormatter(target, targetClass);
if (formatter != null) {
try {
return formatter.parseValue(formattedValue);
return formatter.parse(formattedValue);
} catch (InvalidFormatException e) {
throw new ConversionException(String.class, targetClass, e);
}
@@ -284,7 +285,7 @@ class MvcView implements View {
private Formatter getFormatter(Expression target, Class targetClass) {
if (formatterRegistry != null) {
Formatter formatter = formatterRegistry.getFormatter(target.getExpressionString(), targetClass);
Formatter formatter = formatterRegistry.getFormatter(target.getExpressionString());
if (formatter != null) {
return formatter;
} else {

View File

@@ -11,9 +11,8 @@ import javax.servlet.http.HttpServletResponse;
import junit.framework.TestCase;
import org.springframework.binding.expression.support.StaticExpression;
import org.springframework.binding.format.factories.DateFormatterFactory;
import org.springframework.binding.format.factories.NumberFormatterFactory;
import org.springframework.binding.format.impl.FormatterRegistryImpl;
import org.springframework.binding.format.FormatterRegistry;
import org.springframework.binding.format.registry.DefaultFormatterRegistry;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext;
@@ -28,12 +27,7 @@ public class MvcViewTests extends TestCase {
private Map model;
private FormatterRegistryImpl formatterRegistry = new FormatterRegistryImpl();
public void setUp() {
formatterRegistry.registerFormatter(new NumberFormatterFactory());
formatterRegistry.registerFormatter(new DateFormatterFactory());
}
private FormatterRegistry formatterRegistry = DefaultFormatterRegistry.getSharedInstance();
public void testRender() throws Exception {
MockRequestContext context = new MockRequestContext();
@@ -83,7 +77,7 @@ public class MvcViewTests extends TestCase {
assertNotNull(bm);
assertEquals(null, bm.getFieldValue("stringProperty"));
assertEquals("3", bm.getFieldValue("integerProperty"));
assertEquals("1/1/08", bm.getFieldValue("dateProperty"));
assertEquals("Jan 1, 2008", bm.getFieldValue("dateProperty"));
}
public void testResumeNoEvent() throws Exception {
@@ -119,7 +113,7 @@ public class MvcViewTests extends TestCase {
context.putRequestParameter("_eventId", "submit");
context.putRequestParameter("stringProperty", "foo");
context.putRequestParameter("integerProperty", "5");
context.putRequestParameter("dateProperty", "1/1/07");
context.putRequestParameter("dateProperty", "Jan 1, 2007");
BindBean bindBean = new BindBean();
StaticExpression modelObject = new StaticExpression(bindBean);
modelObject.setExpressionString("bindBean");