collection property type formatters
This commit is contained in:
parent
2be6ce0407
commit
0c1c5fffba
|
|
@ -18,6 +18,7 @@ package org.springframework.ui.binding;
|
|||
/**
|
||||
* A factory for model property bindings.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public interface BindingFactory {
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.util.Map;
|
|||
* Exception thrown by a Binder when a required source value is missing unexpectedly from the sourceValues map.
|
||||
* Indicates a client configuration error.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see Binder#bind(Map)
|
||||
*/
|
||||
public class MissingSourceValuesException extends RuntimeException {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.springframework.ui.binding;
|
|||
/**
|
||||
* Thrown by a BindingFactory when no binding to a property exists.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
* @see BindingFactory#getBinding(String)
|
||||
*/
|
||||
public class NoSuchBindingException extends RuntimeException {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import org.springframework.ui.format.Formatter;
|
|||
/**
|
||||
* A centralized registry of Formatters indexed by property types.
|
||||
* TODO - consider moving to ui.format
|
||||
* TODO - consider a general add(Formatter) method for simplicity
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
|
|
@ -47,6 +48,13 @@ public interface FormatterRegistry {
|
|||
*/
|
||||
void add(Class<?> propertyType, Formatter<?> formatter);
|
||||
|
||||
/**
|
||||
* Adds a Formatter that will format the values of properties of the provided type.
|
||||
* @param propertyType the type
|
||||
* @param formatter the formatter
|
||||
*/
|
||||
void add(GenericCollectionPropertyType propertyType, Formatter<?> formatter);
|
||||
|
||||
/**
|
||||
* Adds a AnnotationFormatterFactory that will format values of properties annotated with a specific annotation.
|
||||
* @param factory the annotation formatter factory
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.context.expression.MapAccessor;
|
||||
|
|
@ -145,6 +144,17 @@ public class GenericBinder implements Binder {
|
|||
public void registerFormatter(Class<?> propertyType, Formatter<?> formatter) {
|
||||
formatterRegistry.add(propertyType, formatter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a Formatter to format the model properties of a specific property type.
|
||||
* Convenience method that calls {@link FormatterRegistry#add(GenericCollectionPropertyType, Formatter)} internally.
|
||||
* @param propertyType the model property type
|
||||
* @param formatter the formatter
|
||||
*/
|
||||
public void registerFormatter(GenericCollectionPropertyType propertyType, Formatter<?> formatter) {
|
||||
formatterRegistry.add(propertyType, formatter);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a FormatterFactory that creates Formatter instances as required to format model properties annotated with a specific annotation.
|
||||
|
|
@ -337,6 +347,7 @@ public class GenericBinder implements Binder {
|
|||
} catch (ExpressionException e) {
|
||||
throw new IllegalStateException("Failed to get property expression value - this should not happen", e);
|
||||
}
|
||||
// TODO if collection we want to format as a single string, need collection formatter
|
||||
return format(value);
|
||||
}
|
||||
|
||||
|
|
@ -406,6 +417,9 @@ public class GenericBinder implements Binder {
|
|||
private BindingResult setStringValue(String formatted) {
|
||||
Object parsed;
|
||||
try {
|
||||
// if binding to a collection we may want collection formatter to convert String to Collection
|
||||
// alternatively, we could map value to a single element e.g. String -> Address via AddressFormatter, which would bind to addresses[0]
|
||||
// probably want to give preference to collection formatter if one is registered
|
||||
Formatter formatter = getFormatter();
|
||||
parsed = formatter.parse(formatted, LocaleContextHolder.getLocale());
|
||||
} catch (ParseException e) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright 2004-2009 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.ui.binding.support;
|
||||
|
||||
/**
|
||||
* Specifies the element type of a generic collection type.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
public class GenericCollectionPropertyType {
|
||||
|
||||
private Class<?> collectionType;
|
||||
|
||||
private Class<?> elementType;
|
||||
|
||||
/**
|
||||
* Creates a new generic collection property type
|
||||
* @param collectionType the collection type
|
||||
* @param elementType the element type
|
||||
*/
|
||||
public GenericCollectionPropertyType(Class<?> collectionType, Class<?> elementType) {
|
||||
this.collectionType = collectionType;
|
||||
this.elementType = elementType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The collection type.
|
||||
*/
|
||||
public Class<?> getCollectionType() {
|
||||
return collectionType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The element type.
|
||||
*/
|
||||
public Class<?> getElementType() {
|
||||
return elementType;
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof GenericCollectionPropertyType)) {
|
||||
return false;
|
||||
}
|
||||
GenericCollectionPropertyType type = (GenericCollectionPropertyType) o;
|
||||
return collectionType.equals(type.collectionType) && elementType.equals(type.elementType);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return collectionType.hashCode() + elementType.hashCode();
|
||||
}
|
||||
}
|
||||
|
|
@ -42,6 +42,8 @@ public class GenericFormatterRegistry implements FormatterRegistry {
|
|||
|
||||
private Map<Class, Formatter> typeFormatters = new ConcurrentHashMap<Class, Formatter>();
|
||||
|
||||
private Map<GenericCollectionPropertyType, Formatter> collectionTypeFormatters = new ConcurrentHashMap<GenericCollectionPropertyType, Formatter>();
|
||||
|
||||
private Map<Class, AnnotationFormatterFactory> annotationFormatters = new HashMap<Class, AnnotationFormatterFactory>();
|
||||
|
||||
public Formatter<?> getFormatter(TypeDescriptor<?> propertyType) {
|
||||
|
|
@ -52,8 +54,19 @@ public class GenericFormatterRegistry implements FormatterRegistry {
|
|||
return factory.getFormatter(a);
|
||||
}
|
||||
}
|
||||
Class<?> type = getType(propertyType);
|
||||
Formatter<?> formatter = typeFormatters.get(type);
|
||||
Formatter<?> formatter = null;
|
||||
Class<?> type;
|
||||
if (propertyType.isCollection()) {
|
||||
formatter = collectionTypeFormatters.get(new GenericCollectionPropertyType(propertyType.getType(), propertyType.getElementType()));
|
||||
if (formatter != null) {
|
||||
return formatter;
|
||||
} else {
|
||||
type = propertyType.getElementType();
|
||||
}
|
||||
} else {
|
||||
type = propertyType.getType();
|
||||
}
|
||||
formatter = typeFormatters.get(type);
|
||||
if (formatter != null) {
|
||||
return formatter;
|
||||
} else {
|
||||
|
|
@ -84,6 +97,10 @@ public class GenericFormatterRegistry implements FormatterRegistry {
|
|||
}
|
||||
}
|
||||
|
||||
public void add(GenericCollectionPropertyType propertyType, Formatter<?> formatter) {
|
||||
collectionTypeFormatters.put(propertyType, formatter);
|
||||
}
|
||||
|
||||
public void add(AnnotationFormatterFactory<?, ?> factory) {
|
||||
annotationFormatters.put(getAnnotationType(factory), factory);
|
||||
}
|
||||
|
|
@ -109,14 +126,6 @@ public class GenericFormatterRegistry implements FormatterRegistry {
|
|||
+ factory.getClass().getName() + "]; does the factory parameterize the <A> generic type?");
|
||||
}
|
||||
|
||||
private Class getType(TypeDescriptor descriptor) {
|
||||
if (descriptor.isArray() || descriptor.isCollection()) {
|
||||
return descriptor.getElementType();
|
||||
} else {
|
||||
return descriptor.getType();
|
||||
}
|
||||
}
|
||||
|
||||
private Class getParameterClass(Type parameterType, Class converterClass) {
|
||||
if (parameterType instanceof TypeVariable) {
|
||||
parameterType = GenericTypeResolver.resolveTypeVariable((TypeVariable) parameterType, converterClass);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import java.lang.annotation.Target;
|
|||
|
||||
/**
|
||||
* A type that can be formatted as a String for display in a UI.
|
||||
* @author Keith Donald
|
||||
* @since 3.0
|
||||
*/
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
|
|
|
|||
|
|
@ -261,7 +261,6 @@ public class GenericBinderTests {
|
|||
Map<String, String> values = new LinkedHashMap<String, String>();
|
||||
values.put("addresses", "4655 Macy Lane:Melbourne:FL:35452,1234 Rostock Circle:Palm Bay:FL:32901,1977 Bel Aire Estates:Coker:AL:12345");
|
||||
BindingResults results = binder.bind(values);
|
||||
System.out.println(results);
|
||||
Assert.assertEquals(3, bean.addresses.size());
|
||||
assertEquals("4655 Macy Lane", bean.addresses.get(0).street);
|
||||
assertEquals("Melbourne", bean.addresses.get(0).city);
|
||||
|
|
@ -278,8 +277,9 @@ public class GenericBinderTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void getCollectionAsSingleValue() {
|
||||
binder.addBinding("addresses").formatWith(new AddressListFormatter());
|
||||
public void getListAsSingleValue() {
|
||||
binder.addBinding("addresses");
|
||||
binder.registerFormatter(new GenericCollectionPropertyType(List.class, Address.class), new AddressListFormatter());
|
||||
Address address1 = new Address();
|
||||
address1.setStreet("s1");
|
||||
address1.setCity("c1");
|
||||
|
|
|
|||
Loading…
Reference in New Issue