From 71ca7192cac093708950ba77d42574a102c38430 Mon Sep 17 00:00:00 2001 From: Keith Donald Date: Thu, 19 Nov 2009 14:48:41 +0000 Subject: [PATCH] removed converter matcher since its not used directly git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@2450 50f2f4bb-b051-0410-bef5-90022cba6387 --- .../ConditionalGenericConverter.java | 19 ++- .../convert/converter/ConverterMatcher.java | 37 ------ .../convert/converter/GenericConverter.java | 24 ++-- .../support/GenericConversionService.java | 125 ++++++------------ 4 files changed, 69 insertions(+), 136 deletions(-) delete mode 100644 org.springframework.core/src/main/java/org/springframework/core/convert/converter/ConverterMatcher.java diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/converter/ConditionalGenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/converter/ConditionalGenericConverter.java index d47b846af8b..2e1e8e345f8 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/converter/ConditionalGenericConverter.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/converter/ConditionalGenericConverter.java @@ -15,14 +15,25 @@ */ package org.springframework.core.convert.converter; +import org.springframework.core.convert.TypeDescriptor; /** - * 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 @DateTimeFormat. + * A generic converter that conditionally executes. + * Applies a rule that determines if a converter between a set of {@link #getConvertibleTypes() convertible types} matches given a client request to convert between a source field of convertible type S and a target field of convertible type T. + * Often used to selectively match custom conversion logic based on the presence of a field or class-level characteristic, such as an annotation or method. + * For example, when converting from a String field to a Date field, an implementation might return true if the target field has also been annotated with @DateTimeFormat. + * As another example, when converting from a String field to an Account field, an implementation might return true if the target Account class defines a public static findAccount(String) method. * @author Keith Donald * @since 3.0 */ -public interface ConditionalGenericConverter extends GenericConverter, ConverterMatcher { +public interface ConditionalGenericConverter extends GenericConverter { + + /** + * Should the Converter from sourceType to targetType 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); } \ No newline at end of file diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/converter/ConverterMatcher.java b/org.springframework.core/src/main/java/org/springframework/core/convert/converter/ConverterMatcher.java deleted file mode 100644 index 1da8093cf28..00000000000 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/converter/ConverterMatcher.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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.converter; - -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 @DateTimeFormat. - * @author Keith Donald - * @since 3.0 - * TODO - consider collapsing into ConditionalGenericConverter - */ -public interface ConverterMatcher { - - /** - * Should the Converter from sourceType to targetType 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); -} diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/converter/GenericConverter.java b/org.springframework.core/src/main/java/org/springframework/core/convert/converter/GenericConverter.java index e7365e237c1..d91f3ef37ff 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/converter/GenericConverter.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/converter/GenericConverter.java @@ -17,35 +17,37 @@ package org.springframework.core.convert.converter; import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.convert.support.GenericConversionService; /** - * Uniform converter interface as returned from {@link GenericConversionService#getConverter}. - * - *

This interface is primarily an internal detail of the {@link GenericConversionService} - * implementation. It should generally not be implemented by application code directly. - * See {@link Converter} and {@link ConverterFactory} interfaces for simpler public converter SPIs. + * Generic converter interface for converting between two or more types. + *

+ * This is the most flexible of the Converter SPI interfaces, but also the most complex. + * It is flexible in that a GenericConverter may support converting between multiple source/target type pairs (see {@link #getConvertibleTypes()}. + * In addition, GenericConverter implementations have access to source/target {@link TypeDescriptor field context} during the type conversion process. + * This allows for resolving source and target field metadata such as annotations and generics information, which can be used influence the conversion logic. + *

+ * This interface should generally not be used when the simpler {@link Converter} or {@link ConverterFactory} interfaces are sufficient. * * @author Keith Donald * @since 3.0 + * @see TypeDescriptor * @see Converter * @see ConverterFactory - * @see GenericConversionService */ public interface GenericConverter { /** * The source and target types this converter can convert between. - * Each entry in the returned array is a two-element array where the first element is a sourceType that can be converted from, - * and the second element is a targetType that can be converted to. + * Each entry in the returned array is a convertible source-to-target type pair, also expressed as an array. + * For each pair, the first array element is a sourceType that can be converted from, and the second array element is a targetType that can be converted to. */ public Class[][] getConvertibleTypes(); /** * Convert the source to the targetType described by the TypeDescriptor. * @param source the source object to convert (may be null) - * @param sourceType context about the source type to convert from - * @param targetType context about the target type to convert to + * @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 the converted object */ Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType); diff --git a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java index 9f051df6999..23d71bc300f 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java +++ b/org.springframework.core/src/main/java/org/springframework/core/convert/support/GenericConversionService.java @@ -31,9 +31,9 @@ import org.springframework.core.convert.ConversionFailedException; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConverterNotFoundException; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.ConditionalGenericConverter; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.ConverterFactory; -import org.springframework.core.convert.converter.ConverterMatcher; import org.springframework.core.convert.converter.ConverterRegistry; import org.springframework.core.convert.converter.GenericConverter; import org.springframework.util.Assert; @@ -48,18 +48,19 @@ import org.springframework.util.ClassUtils; public class GenericConversionService implements ConversionService, ConverterRegistry { private static final Log logger = LogFactory.getLog(GenericConversionService.class); - + private static final GenericConverter NO_OP_CONVERTER = new GenericConverter() { public Class[][] getConvertibleTypes() { return null; } - + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { return source; } }; - private final Map, Map, MatchableConverters>> converters = new HashMap, Map, MatchableConverters>>(36); + private final Map, Map, MatchableConverters>> converters = new HashMap, Map, MatchableConverters>>( + 36); // implementing ConverterRegistry @@ -84,10 +85,10 @@ public class GenericConversionService implements ConversionService, ConverterReg public void addGenericConverter(GenericConverter converter) { Class[][] convertibleTypes = converter.getConvertibleTypes(); for (Class[] convertibleType : convertibleTypes) { - getMatchableConvertersList(convertibleType[0], convertibleType[1]).add(converter); + getMatchableConvertersList(convertibleType[0], convertibleType[1]).add(converter); } } - + public void removeConvertible(Class sourceType, Class targetType) { getSourceConverterMap(sourceType).remove(targetType); } @@ -284,7 +285,8 @@ public class GenericConversionService implements ConversionService, ConverterReg return converters; } - private GenericConverter getMatchingConverterForTarget(TypeDescriptor sourceType, TypeDescriptor targetType, Map, MatchableConverters> converters) { + private GenericConverter getMatchingConverterForTarget(TypeDescriptor sourceType, TypeDescriptor targetType, + Map, MatchableConverters> converters) { Class targetObjectType = targetType.getObjectType(); if (targetObjectType.isInterface()) { LinkedList> classQueue = new LinkedList>(); @@ -340,14 +342,14 @@ public class GenericConversionService implements ConversionService, ConverterReg private final class ConverterAdapter implements GenericConverter { private final Class[] typeInfo; - + private final Converter converter; public ConverterAdapter(Class[] typeInfo, Converter converter) { this.converter = converter; this.typeInfo = typeInfo; } - + public Class[][] getConvertibleTypes() { return new Class[][] { this.typeInfo }; } @@ -360,7 +362,7 @@ public class GenericConversionService implements ConversionService, ConverterReg } public String toString() { - return this.converter.toString(); + return this.typeInfo[0].getName() + " -> " + this.typeInfo[1].getName() + " : " + this.converter.toString(); } } @@ -373,13 +375,13 @@ public class GenericConversionService implements ConversionService, ConverterReg public ConverterFactoryAdapter(Class[] typeInfo, ConverterFactory converterFactory) { this.converterFactory = converterFactory; - this.typeInfo = typeInfo; + this.typeInfo = typeInfo; } public Class[][] getConvertibleTypes() { return new Class[][] { this.typeInfo }; } - + public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { if (source == null) { return convertNullSource(sourceType, targetType); @@ -388,102 +390,57 @@ public class GenericConversionService implements ConversionService, ConverterReg } public String toString() { - return this.converterFactory.toString(); + return this.typeInfo[0].getName() + " -> " + this.typeInfo[1].getName() + " : " + this.converterFactory.toString(); } } private static class MatchableConverters { - private static final ConverterMatcher ALWAYS_MATCHES = new ConverterMatcher() { - public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { - return true; - } - }; + private LinkedList conditionalConverters; - private LinkedList matchableConverters = new LinkedList(); + private GenericConverter defaultConverter; public void add(GenericConverter 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)); + if (converter instanceof ConditionalGenericConverter) { + if (this.conditionalConverters == null) { + this.conditionalConverters = new LinkedList(); + } + this.conditionalConverters.add((ConditionalGenericConverter) converter); } else { - this.matchableConverters.set(index, matchable); + this.defaultConverter = converter; } } public GenericConverter matchConverter(TypeDescriptor sourceType, TypeDescriptor targetType) { - for (MatchableConverter matchable : this.matchableConverters) { - if (matchable.matches(sourceType, targetType)) { - if (logger.isDebugEnabled()) { - logger.debug("Converter Lookup [MATCHED] " + matchable); + if (this.conditionalConverters != null) { + for (ConditionalGenericConverter conditional : this.conditionalConverters) { + if (conditional.matches(sourceType, targetType)) { + if (logger.isDebugEnabled()) { + logger.debug("Converter Lookup [MATCHED] " + conditional); + } + return conditional; + } else { + if (logger.isDebugEnabled()) { + logger.debug("Converter Lookup [DID NOT MATCH] " + conditional); + } } - return matchable.getConverter(); - } else { - if (logger.isDebugEnabled()) { - logger.debug("Converter Lookup [DID NOT MATCH] " + matchable); - } } } - return null; + if (logger.isDebugEnabled()) { + logger.debug("Converter Lookup [MATCHED] " + this.defaultConverter); + } + return this.defaultConverter; } public String toString() { - if (this.matchableConverters.size() == 1) { - return this.matchableConverters.get(0).toString(); + if (this.conditionalConverters != null) { + return "[" + this.conditionalConverters + "; default = " + this.defaultConverter + "]"; } else { - return "[MatchableConverters = " + this.matchableConverters + "]"; + return this.defaultConverter.toString(); } } - 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