revised matchable converter lookup algorithm; added conversion service bean container tests
This commit is contained in:
parent
c812cd370b
commit
f0de1c3069
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.ui.format.jodatime;
|
package org.springframework.ui.format.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.springframework.ui.format.jodatime;
|
package org.springframework.ui.format.annotation;
|
||||||
|
|
||||||
import java.lang.annotation.ElementType;
|
import java.lang.annotation.ElementType;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
/**
|
||||||
|
* Annotations for declaratively configuring field formatting rules.
|
||||||
|
*/
|
||||||
|
package org.springframework.ui.format.annotation;
|
||||||
|
|
@ -16,7 +16,8 @@
|
||||||
package org.springframework.ui.format.jodatime;
|
package org.springframework.ui.format.jodatime;
|
||||||
|
|
||||||
import org.joda.time.format.DateTimeFormatter;
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
import org.springframework.ui.format.jodatime.DateTimeFormat.Style;
|
import org.springframework.ui.format.annotation.DateTimeFormat;
|
||||||
|
import org.springframework.ui.format.annotation.DateTimeFormat.Style;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats properties annotated with the {@link DateTimeFormat} annotation.
|
* Formats properties annotated with the {@link DateTimeFormat} annotation.
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@
|
||||||
package org.springframework.ui.format.jodatime;
|
package org.springframework.ui.format.jodatime;
|
||||||
|
|
||||||
import org.joda.time.format.DateTimeFormatter;
|
import org.joda.time.format.DateTimeFormatter;
|
||||||
import org.springframework.ui.format.jodatime.ISODateTimeFormat.Style;
|
import org.springframework.ui.format.annotation.ISODateTimeFormat;
|
||||||
|
import org.springframework.ui.format.annotation.ISODateTimeFormat.Style;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats properties annotated with the {@link ISODateTimeFormat} annotation.
|
* Formats properties annotated with the {@link ISODateTimeFormat} annotation.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
package org.springframework.context.conversionservice;
|
||||||
|
|
||||||
|
public class Bar {
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
public Bar(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.springframework.context.conversionservice;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||||
|
|
||||||
|
public class ConversionServiceContextConfigTests {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConfigOk() {
|
||||||
|
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("org/springframework/context/conversionservice/conversionservice.xml");
|
||||||
|
TestClient client = context.getBean("testClient", TestClient.class);
|
||||||
|
assertEquals(2, client.getBars().size());
|
||||||
|
assertEquals("value1", client.getBars().get(0).getValue());
|
||||||
|
assertEquals("value2", client.getBars().get(1).getValue());
|
||||||
|
assertTrue(client.isBool());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.springframework.context.conversionservice;
|
||||||
|
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
|
||||||
|
public class StringToBarConverter implements Converter<String, Bar> {
|
||||||
|
|
||||||
|
public Bar convert(String source) {
|
||||||
|
return new Bar(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package org.springframework.context.conversionservice;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
public class TestClient {
|
||||||
|
|
||||||
|
private List<Bar> bars;
|
||||||
|
|
||||||
|
private boolean bool;
|
||||||
|
|
||||||
|
public List<Bar> getBars() {
|
||||||
|
return bars;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
public void setBars(List<Bar> bars) {
|
||||||
|
this.bars = bars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isBool() {
|
||||||
|
return bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBool(boolean bool) {
|
||||||
|
this.bool = bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -12,11 +12,11 @@ import org.joda.time.LocalDateTime;
|
||||||
import org.joda.time.LocalTime;
|
import org.joda.time.LocalTime;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.springframework.beans.MutablePropertyValues;
|
import org.springframework.beans.MutablePropertyValues;
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
import org.springframework.ui.format.jodatime.DateTimeFormat.Style;
|
import org.springframework.ui.format.annotation.DateTimeFormat;
|
||||||
|
import org.springframework.ui.format.annotation.DateTimeFormat.Style;
|
||||||
import org.springframework.ui.format.support.FormattingConversionService;
|
import org.springframework.ui.format.support.FormattingConversionService;
|
||||||
import org.springframework.validation.DataBinder;
|
import org.springframework.validation.DataBinder;
|
||||||
|
|
||||||
|
|
@ -101,17 +101,15 @@ public class JodaTimeFormattingTests {
|
||||||
propertyValues.addPropertyValue("localDateTimeAnnotated", "Saturday, October 31, 2009 12:00:00 PM ");
|
propertyValues.addPropertyValue("localDateTimeAnnotated", "Saturday, October 31, 2009 12:00:00 PM ");
|
||||||
binder.bind(propertyValues);
|
binder.bind(propertyValues);
|
||||||
assertEquals(0, binder.getBindingResult().getErrorCount());
|
assertEquals(0, binder.getBindingResult().getErrorCount());
|
||||||
assertEquals("Saturday, October 31, 2009 12:00:00 PM ", binder.getBindingResult().getFieldValue("localDateTimeAnnotated"));
|
assertEquals("Saturday, October 31, 2009 12:00:00 PM ", binder.getBindingResult().getFieldValue(
|
||||||
|
"localDateTimeAnnotated"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
|
||||||
public void testBindDateTime() {
|
public void testBindDateTime() {
|
||||||
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
MutablePropertyValues propertyValues = new MutablePropertyValues();
|
||||||
propertyValues.addPropertyValue("dateTime", "10/31/09 12:00 PM");
|
propertyValues.addPropertyValue("dateTime", "10/31/09 12:00 PM");
|
||||||
// this doesn't work because the String->ReadableInstant converter doesn't match due to String->@DateTimeFormat DateTime Matchable taking precedence
|
|
||||||
binder.bind(propertyValues);
|
binder.bind(propertyValues);
|
||||||
System.out.println(binder.getBindingResult());
|
|
||||||
assertEquals(0, binder.getBindingResult().getErrorCount());
|
assertEquals(0, binder.getBindingResult().getErrorCount());
|
||||||
assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("dateTime"));
|
assertEquals("10/31/09 12:00 PM", binder.getBindingResult().getFieldValue("dateTime"));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,10 +31,10 @@ import org.junit.Test;
|
||||||
import org.springframework.context.i18n.LocaleContextHolder;
|
import org.springframework.context.i18n.LocaleContextHolder;
|
||||||
import org.springframework.core.convert.TypeDescriptor;
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.core.convert.converter.Converter;
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.ui.format.annotation.DateTimeFormat.Style;
|
||||||
import org.springframework.ui.format.jodatime.DateTimeFormatAnnotationFormatterFactory;
|
import org.springframework.ui.format.jodatime.DateTimeFormatAnnotationFormatterFactory;
|
||||||
import org.springframework.ui.format.jodatime.DateTimeParser;
|
import org.springframework.ui.format.jodatime.DateTimeParser;
|
||||||
import org.springframework.ui.format.jodatime.ReadablePartialPrinter;
|
import org.springframework.ui.format.jodatime.ReadablePartialPrinter;
|
||||||
import org.springframework.ui.format.jodatime.DateTimeFormat.Style;
|
|
||||||
import org.springframework.ui.format.number.IntegerFormatter;
|
import org.springframework.ui.format.number.IntegerFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -105,7 +105,7 @@ public class FormattingConversionServiceTests {
|
||||||
private static class Model {
|
private static class Model {
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
@org.springframework.ui.format.jodatime.DateTimeFormat(dateStyle = Style.SHORT)
|
@org.springframework.ui.format.annotation.DateTimeFormat(dateStyle = Style.SHORT)
|
||||||
public Date date;
|
public Date date;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xmlns:context="http://www.springframework.org/schema/context"
|
||||||
|
xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||||
|
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
|
||||||
|
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd">
|
||||||
|
|
||||||
|
<bean id="conversionService" class="org.springframework.core.convert.support.DefaultConversionService">
|
||||||
|
<property name="converters">
|
||||||
|
<bean class="org.springframework.context.conversionservice.StringToBarConverter" />
|
||||||
|
</property>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean id="testClient" class="org.springframework.context.conversionservice.TestClient">
|
||||||
|
<property name="bool" value="true"/>
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean class="org.springframework.context.conversionservice.Bar">
|
||||||
|
<constructor-arg value ="value1" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<bean class="org.springframework.context.conversionservice.Bar">
|
||||||
|
<constructor-arg value ="value2" />
|
||||||
|
</bean>
|
||||||
|
|
||||||
|
<context:annotation-config />
|
||||||
|
|
||||||
|
</beans>
|
||||||
|
|
@ -51,8 +51,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final Map<Class<?>, Map<Class<?>, MatchableConverters>> converters = new HashMap<Class<?>, Map<Class<?>, MatchableConverters>>(
|
private final Map<Class<?>, Map<Class<?>, MatchableConverters>> converters = new HashMap<Class<?>, Map<Class<?>, MatchableConverters>>(36);
|
||||||
36);
|
|
||||||
|
|
||||||
private ConversionService parent;
|
private ConversionService parent;
|
||||||
|
|
||||||
|
|
@ -269,9 +268,7 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
* @return the generic converter that will perform the conversion, or <code>null</code> if no suitable converter was found
|
* @return the generic converter that will perform the conversion, or <code>null</code> if no suitable converter was found
|
||||||
*/
|
*/
|
||||||
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||||
MatchableConverters matchable = findMatchableConvertersForClassPair(sourceType.getObjectType(), targetType
|
GenericConverter converter = findConverterForClassPair(sourceType, targetType);
|
||||||
.getObjectType());
|
|
||||||
GenericConverter converter = matchConverter(matchable, sourceType, targetType);
|
|
||||||
if (converter != null) {
|
if (converter != null) {
|
||||||
return converter;
|
return converter;
|
||||||
} else if (this.parent != null && this.parent.canConvert(sourceType, targetType)) {
|
} else if (this.parent != null && this.parent.canConvert(sourceType, targetType)) {
|
||||||
|
|
@ -328,16 +325,17 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
Assert.notNull(targetType, "The targetType to convert to is required");
|
Assert.notNull(targetType, "The targetType to convert to is required");
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatchableConverters findMatchableConvertersForClassPair(Class<?> sourceType, Class<?> targetType) {
|
private GenericConverter findConverterForClassPair(TypeDescriptor sourceType, TypeDescriptor targetType) {
|
||||||
if (sourceType.isInterface()) {
|
Class<?> sourceObjectType = sourceType.getObjectType();
|
||||||
|
if (sourceObjectType.isInterface()) {
|
||||||
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
||||||
classQueue.addFirst(sourceType);
|
classQueue.addFirst(sourceObjectType);
|
||||||
while (!classQueue.isEmpty()) {
|
while (!classQueue.isEmpty()) {
|
||||||
Class<?> currentClass = classQueue.removeLast();
|
Class<?> currentClass = classQueue.removeLast();
|
||||||
Map<Class<?>, MatchableConverters> converters = getTargetConvertersForSource(currentClass);
|
Map<Class<?>, MatchableConverters> converters = getTargetConvertersForSource(currentClass);
|
||||||
MatchableConverters matchable = getMatchableConvertersForTarget(converters, targetType);
|
GenericConverter converter = getMatchingConverterForTarget(sourceType, targetType, converters);
|
||||||
if (matchable != null) {
|
if (converter != null) {
|
||||||
return matchable;
|
return converter;
|
||||||
}
|
}
|
||||||
Class<?>[] interfaces = currentClass.getInterfaces();
|
Class<?>[] interfaces = currentClass.getInterfaces();
|
||||||
for (Class<?> ifc : interfaces) {
|
for (Class<?> ifc : interfaces) {
|
||||||
|
|
@ -345,16 +343,16 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Map<Class<?>, MatchableConverters> objectConverters = getTargetConvertersForSource(Object.class);
|
Map<Class<?>, MatchableConverters> objectConverters = getTargetConvertersForSource(Object.class);
|
||||||
return getMatchableConvertersForTarget(objectConverters, targetType);
|
return getMatchingConverterForTarget(sourceType, targetType, objectConverters);
|
||||||
} else {
|
} else {
|
||||||
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
||||||
classQueue.addFirst(sourceType);
|
classQueue.addFirst(sourceObjectType);
|
||||||
while (!classQueue.isEmpty()) {
|
while (!classQueue.isEmpty()) {
|
||||||
Class<?> currentClass = classQueue.removeLast();
|
Class<?> currentClass = classQueue.removeLast();
|
||||||
Map<Class<?>, MatchableConverters> converters = getTargetConvertersForSource(currentClass);
|
Map<Class<?>, MatchableConverters> converters = getTargetConvertersForSource(currentClass);
|
||||||
MatchableConverters matchable = getMatchableConvertersForTarget(converters, targetType);
|
GenericConverter converter = getMatchingConverterForTarget(sourceType, targetType, converters);
|
||||||
if (matchable != null) {
|
if (converter != null) {
|
||||||
return matchable;
|
return converter;
|
||||||
}
|
}
|
||||||
if (currentClass.isArray()) {
|
if (currentClass.isArray()) {
|
||||||
Class<?> componentType = ClassUtils.resolvePrimitiveIfNecessary(currentClass.getComponentType());
|
Class<?> componentType = ClassUtils.resolvePrimitiveIfNecessary(currentClass.getComponentType());
|
||||||
|
|
@ -383,14 +381,15 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
return converters;
|
return converters;
|
||||||
}
|
}
|
||||||
|
|
||||||
private MatchableConverters getMatchableConvertersForTarget(Map<Class<?>, MatchableConverters> converters,
|
private GenericConverter getMatchingConverterForTarget(TypeDescriptor sourceType, TypeDescriptor targetType, Map<Class<?>, MatchableConverters> converters) {
|
||||||
Class<?> targetType) {
|
Class<?> targetObjectType = targetType.getObjectType();
|
||||||
if (targetType.isInterface()) {
|
if (targetObjectType.isInterface()) {
|
||||||
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
||||||
classQueue.addFirst(targetType);
|
classQueue.addFirst(targetObjectType);
|
||||||
while (!classQueue.isEmpty()) {
|
while (!classQueue.isEmpty()) {
|
||||||
Class<?> currentClass = classQueue.removeLast();
|
Class<?> currentClass = classQueue.removeLast();
|
||||||
MatchableConverters converter = converters.get(currentClass);
|
MatchableConverters matchable = converters.get(currentClass);
|
||||||
|
GenericConverter converter = matchConverter(matchable, sourceType, targetType);
|
||||||
if (converter != null) {
|
if (converter != null) {
|
||||||
return converter;
|
return converter;
|
||||||
}
|
}
|
||||||
|
|
@ -399,13 +398,14 @@ public class GenericConversionService implements ConversionService, ConverterReg
|
||||||
classQueue.addFirst(ifc);
|
classQueue.addFirst(ifc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return converters.get(Object.class);
|
return matchConverter(converters.get(Object.class), sourceType, targetType);
|
||||||
} else {
|
} else {
|
||||||
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
|
||||||
classQueue.addFirst(targetType);
|
classQueue.addFirst(targetObjectType);
|
||||||
while (!classQueue.isEmpty()) {
|
while (!classQueue.isEmpty()) {
|
||||||
Class<?> currentClass = classQueue.removeLast();
|
Class<?> currentClass = classQueue.removeLast();
|
||||||
MatchableConverters converter = converters.get(currentClass);
|
MatchableConverters matchable = converters.get(currentClass);
|
||||||
|
GenericConverter converter = matchConverter(matchable, sourceType, targetType);
|
||||||
if (converter != null) {
|
if (converter != null) {
|
||||||
return converter;
|
return converter;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue