提交 ffbfa299 编写于 作者: K Keith Donald

polish

上级 0a78287a
......@@ -82,7 +82,7 @@ public class FormattingConversionService implements FormatterRegistry, Conversio
+ "]; does the factory parameterize the <A extends Annotation> generic type?");
}
Set<Class<?>> fieldTypes = annotationFormatterFactory.getFieldTypes();
for (Class<?> fieldType : fieldTypes) {
for (final Class<?> fieldType : fieldTypes) {
this.conversionService.addGenericConverter(fieldType, String.class, new ConditionalGenericConverter() {
public boolean matches(TypeDescriptor sourceFieldType, TypeDescriptor targetFieldType) {
return sourceFieldType.getAnnotation(annotationType) != null;
......@@ -90,7 +90,10 @@ public class FormattingConversionService implements FormatterRegistry, Conversio
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Printer<?> printer = annotationFormatterFactory.getPrinter(sourceType.getAnnotation(annotationType), targetType.getType());
return new PrinterConverter(printer, conversionService).convert(source, sourceType, targetType);
}
}
public String toString() {
return "@" + annotationType.getName() + " " + fieldType.getName() + " -> " + String.class.getName();
}
});
this.conversionService.addGenericConverter(String.class, fieldType, new ConditionalGenericConverter() {
public boolean matches(TypeDescriptor sourceFieldType, TypeDescriptor targetFieldType) {
......@@ -99,6 +102,9 @@ public class FormattingConversionService implements FormatterRegistry, Conversio
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Parser<?> parser = annotationFormatterFactory.getParser(targetType.getAnnotation(annotationType), targetType.getType());
return new ParserConverter(parser, conversionService).convert(source, sourceType, targetType);
}
public String toString() {
return String.class.getName() + " -> @" + annotationType.getName() + " " + fieldType.getName();
}
});
}
......@@ -127,6 +133,10 @@ public class FormattingConversionService implements FormatterRegistry, Conversio
return this.conversionService.convert(source, sourceType, targetType);
}
public String toString() {
return this.conversionService.toString();
}
// internal helpers
@SuppressWarnings("unchecked")
......
......@@ -41,7 +41,7 @@ import org.springframework.ui.format.number.IntegerFormatter;
* @author Keith Donald
* @author Juergen Hoeller
*/
public class GenericFormattingServiceTests {
public class FormattingConversionServiceTests {
private FormattingConversionService formattingService;
......@@ -93,6 +93,7 @@ public class GenericFormattingServiceTests {
}
});
formattingService.addFormatterForFieldAnnotation(new DateTimeFormatAnnotationFormatterFactory());
System.out.println(this.formattingService);
String formatted = (String) formattingService.convert(new LocalDate(2009, 10, 31).toDateTimeAtCurrentTime()
.toDate(), new TypeDescriptor(Model.class.getField("date")), TypeDescriptor.valueOf(String.class));
assertEquals("10/31/09", formatted);
......
......@@ -15,23 +15,13 @@
*/
package org.springframework.core.convert.support;
import org.springframework.core.convert.TypeDescriptor;
/**
* A generic converter that conditionally executes.
* A generic converter that, as a ConverterMatcher, conditionally executes.
* Often used when selectively matching custom conversion logic based on the presence of a field or class-level annotation.
* For example, when converting from a String to a Date field, an implementation might return true if the target field has also been annotated with <code>@DateTimeFormat</code>.
* @author Keith Donald
* @since 3.0
*/
public interface ConditionalGenericConverter extends GenericConverter {
/**
* Should the conversion between <code>sourceFieldType</code> and <code>targetFieldType</code> be performed?
* @param sourceFieldType the type descriptor of the field we are converting from
* @param targetFieldType the type descriptor of the field we are converting to
* @return true if conversion should be performed, false otherwise
*/
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
public interface ConditionalGenericConverter extends GenericConverter, ConverterMatcher {
}
\ No newline at end of file
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.convert.support;
import org.springframework.core.convert.TypeDescriptor;
/**
* A rule that determines if a particular Converter of S to T matches given a request to convert between a field of type S and a field of type T.
* Often used to selectively apply custom type conversion logic based on the presence of a field annotation.
* For example, when converting from a String to a Date field, an implementation might return true only if the target Date field has also been annotated with <code>@DateTimeFormat</code>.
* @author Keith Donald
* @since 3.0
*/
public interface ConverterMatcher {
/**
* Should the Converter from <code>sourceType</code> to <code>targetType</code> currently under consideration be selected?
* @param sourceType the type descriptor of the field we are converting from
* @param targetType the type descriptor of the field we are converting to
* @return true if conversion should be performed, false otherwise
*/
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
......@@ -16,7 +16,9 @@
package org.springframework.core.convert.support;
import java.util.Collection;
import java.util.Locale;
import java.util.Map;
/**
* Default implementation of a conversion service. Will automatically register <i>from string</i>
......@@ -32,6 +34,21 @@ public class DefaultConversionService extends GenericConversionService {
* Create a new default conversion service, installing the default converters.
*/
public DefaultConversionService() {
addGenericConverter(Object[].class, Object[].class, new ArrayToArrayConverter(this));
addGenericConverter(Object[].class, Collection.class, new ArrayToCollectionConverter(this));
addGenericConverter(Object[].class, Map.class, new ArrayToMapConverter(this));
addGenericConverter(Object[].class, Object.class, new ArrayToObjectConverter(this));
addGenericConverter(Collection.class, Collection.class, new CollectionToCollectionConverter(this));
addGenericConverter(Collection.class, Object[].class, new CollectionToArrayConverter(this));
addGenericConverter(Collection.class, Map.class, new CollectionToMapConverter(this));
addGenericConverter(Collection.class, Object.class, new CollectionToObjectConverter(this));
addGenericConverter(Map.class, Map.class, new MapToMapConverter(this));
addGenericConverter(Map.class, Object[].class, new MapToArrayConverter(this));
addGenericConverter(Map.class, Collection.class, new MapToCollectionConverter(this));
addGenericConverter(Map.class, Object.class, new MapToObjectConverter(this));
addGenericConverter(Object.class, Object[].class, new ObjectToArrayConverter(this));
addGenericConverter(Object.class, Collection.class, new ObjectToCollectionConverter(this));
addGenericConverter(Object.class, Map.class, new ObjectToMapConverter(this));
addConverter(String.class, Boolean.class, new StringToBooleanConverter());
addConverter(String.class, Character.class, new StringToCharacterConverter());
addConverter(String.class, Locale.class, new StringToLocaleConverter());
......
......@@ -61,28 +61,6 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
};
/**
* Create a new GenericConversionService.
* Generic converters for Collection types are registered.
*/
public GenericConversionService() {
addGenericConverter(Object[].class, Object[].class, new ArrayToArrayConverter(this));
addGenericConverter(Object[].class, Collection.class, new ArrayToCollectionConverter(this));
addGenericConverter(Object[].class, Map.class, new ArrayToMapConverter(this));
addGenericConverter(Object[].class, Object.class, new ArrayToObjectConverter(this));
addGenericConverter(Collection.class, Collection.class, new CollectionToCollectionConverter(this));
addGenericConverter(Collection.class, Object[].class, new CollectionToArrayConverter(this));
addGenericConverter(Collection.class, Map.class, new CollectionToMapConverter(this));
addGenericConverter(Collection.class, Object.class, new CollectionToObjectConverter(this));
addGenericConverter(Map.class, Map.class, new MapToMapConverter(this));
addGenericConverter(Map.class, Object[].class, new MapToArrayConverter(this));
addGenericConverter(Map.class, Collection.class, new MapToCollectionConverter(this));
addGenericConverter(Map.class, Object.class, new MapToObjectConverter(this));
addGenericConverter(Object.class, Object[].class, new ObjectToArrayConverter(this));
addGenericConverter(Object.class, Collection.class, new ObjectToCollectionConverter(this));
addGenericConverter(Object.class, Map.class, new ObjectToMapConverter(this));
}
/**
* Registers the converters in the set provided.
* JavaBean-friendly alternative to calling {@link #addConverter(Converter)}.
......@@ -191,7 +169,18 @@ public class GenericConversionService implements ConversionService, ConverterReg
public void addGenericConverter(Class<?> sourceType, Class<?> targetType, GenericConverter converter) {
getMatchableConvertersList(sourceType, targetType).add(converter);
}
/**
* Registers a GenericConverter for the source/target type pair that will only be matched if the provided matcher returns true.
* @param sourceType the source type to convert from
* @param targetType the target type to convert to
* @param matcher a matcher can restrict a match of the converter based on source and target runtime field types
* @param converter the generic converter.
*/
public void addGenericConverter(Class<?> sourceType, Class<?> targetType, GenericConverter converter, ConverterMatcher matcher) {
getMatchableConvertersList(sourceType, targetType).add(matcher, converter);
}
/**
* Registers a Converter with the sourceType and targetType to index on specified explicitly.
* This method performs better than {@link #addConverter(Converter)} because there parameterized types S and T don't have to be discovered.
......@@ -214,6 +203,22 @@ public class GenericConversionService implements ConversionService, ConverterReg
addGenericConverter(sourceType, targetType, new ConverterFactoryAdapter(converterFactory));
}
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("ConversionService converters = ").append("\n");
for (Map<Class<?>, MatchableConverters> targetConverters : this.converters.values()) {
for (MatchableConverters matchable : targetConverters.values()) {
builder.append("\t");
builder.append(matchable);
builder.append("\n");
}
}
if (this.parent != null) {
builder.append("parent = ").append(this.parent);
}
return builder.toString();
}
// subclassing hooks
/**
......@@ -402,7 +407,8 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
}
private GenericConverter matchConverter(MatchableConverters matchable, TypeDescriptor sourceFieldType, TypeDescriptor targetFieldType) {
private GenericConverter matchConverter(MatchableConverters matchable, TypeDescriptor sourceFieldType,
TypeDescriptor targetFieldType) {
return matchable != null ? matchable.matchConverter(sourceFieldType, targetFieldType) : null;
}
......@@ -421,6 +427,10 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
return this.converter.convert(source);
}
public String toString() {
return this.converter.toString();
}
}
@SuppressWarnings("unchecked")
......@@ -438,29 +448,97 @@ public class GenericConversionService implements ConversionService, ConverterReg
}
return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
}
public String toString() {
return this.converterFactory.toString();
}
}
private static class MatchableConverters {
private LinkedList<GenericConverter> matchableConverters = new LinkedList<GenericConverter>();
private static final ConverterMatcher ALWAYS_MATCHES = new ConverterMatcher() {
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return true;
}
};
private LinkedList<MatchableConverter> matchableConverters = new LinkedList<MatchableConverter>();
public void add(GenericConverter converter) {
this.matchableConverters.addFirst(converter);
if (converter instanceof ConverterMatcher) {
add((ConverterMatcher) converter, converter);
} else {
add(ALWAYS_MATCHES, converter);
}
}
public void add(ConverterMatcher matcher, GenericConverter converter) {
MatchableConverter matchable = new MatchableConverter(matcher, converter);
int index = this.matchableConverters.indexOf(matchable);
if (index == -1) {
this.matchableConverters.addFirst(new MatchableConverter(matcher, converter));
} else {
this.matchableConverters.set(index, matchable);
}
}
public GenericConverter matchConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
for (GenericConverter matchable : this.matchableConverters) {
if (!(matchable instanceof ConditionalGenericConverter)) {
return matchable;
}
ConditionalGenericConverter conditional = (ConditionalGenericConverter) matchable;
if (conditional.matches(sourceType, targetType)) {
return matchable;
for (MatchableConverter matchable : this.matchableConverters) {
if (matchable.matches(sourceType, targetType)) {
return matchable.getConverter();
}
}
return null;
}
public String toString() {
if (this.matchableConverters.size() == 1) {
return this.matchableConverters.get(0).toString();
} else {
return "[MatchableConverters = " + this.matchableConverters + "]";
}
}
private static class MatchableConverter {
private ConverterMatcher matcher;
private GenericConverter converter;
public MatchableConverter(ConverterMatcher matcher, GenericConverter converter) {
this.matcher = matcher;
this.converter = converter;
}
public GenericConverter getConverter() {
return this.converter;
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.matcher.matches(sourceType, targetType);
}
public int hashCode() {
return this.matcher.hashCode();
}
public boolean equals(Object o) {
if (!(o instanceof MatchableConverter)) {
return false;
}
MatchableConverter matchable = (MatchableConverter) o;
return this.matcher.equals(matchable.matcher);
}
public String toString() {
if (matcher == ALWAYS_MATCHES || matcher == converter) {
return this.converter.toString();
} else {
return "if (" + this.matcher + ") " + this.converter;
}
}
}
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册